]> git.openstreetmap.org Git - rails.git/blob - vendor/assets/iD/iD.js
Update to iD v2.18.0
[rails.git] / vendor / assets / iD / iD.js
1 (function () {
2         var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
3
4         function createCommonjsModule(fn, basedir, module) {
5                 return module = {
6                   path: basedir,
7                   exports: {},
8                   require: function (path, base) {
9               return commonjsRequire(path, (base === undefined || base === null) ? module.path : base);
10             }
11                 }, fn(module, module.exports), module.exports;
12         }
13
14         function getCjsExportFromNamespace (n) {
15                 return n && n['default'] || n;
16         }
17
18         function commonjsRequire () {
19                 throw new Error('Dynamic requires are not currently supported by @rollup/plugin-commonjs');
20         }
21
22         var isImplemented = function () {
23                 var set, iterator, result;
24                 if (typeof Set !== 'function') return false;
25                 set = new Set(['raz', 'dwa', 'trzy']);
26                 if (String(set) !== '[object Set]') return false;
27                 if (set.size !== 3) return false;
28                 if (typeof set.add !== 'function') return false;
29                 if (typeof set.clear !== 'function') return false;
30                 if (typeof set.delete !== 'function') return false;
31                 if (typeof set.entries !== 'function') return false;
32                 if (typeof set.forEach !== 'function') return false;
33                 if (typeof set.has !== 'function') return false;
34                 if (typeof set.keys !== 'function') return false;
35                 if (typeof set.values !== 'function') return false;
36
37                 iterator = set.values();
38                 result = iterator.next();
39                 if (result.done !== false) return false;
40                 if (result.value !== 'raz') return false;
41
42                 return true;
43         };
44
45         // eslint-disable-next-line no-empty-function
46         var noop = function () {};
47
48         var _undefined = noop(); // Support ES3 engines
49
50         var isValue = function (val) { return val !== _undefined && val !== null; };
51
52         var validValue = function (value) {
53                 if (!isValue(value)) throw new TypeError("Cannot use null or undefined");
54                 return value;
55         };
56
57         var clear = function () {
58                 validValue(this).length = 0;
59                 return this;
60         };
61
62         var isImplemented$1 = function () {
63                 var numberIsNaN = Number.isNaN;
64                 if (typeof numberIsNaN !== "function") return false;
65                 return !numberIsNaN({}) && numberIsNaN(NaN) && !numberIsNaN(34);
66         };
67
68         var shim = function (value) {
69                 // eslint-disable-next-line no-self-compare
70                 return value !== value;
71         };
72
73         var isNan = isImplemented$1() ? Number.isNaN : shim;
74
75         var isImplemented$2 = function () {
76                 var sign = Math.sign;
77                 if (typeof sign !== "function") return false;
78                 return sign(10) === 1 && sign(-20) === -1;
79         };
80
81         var shim$1 = function (value) {
82                 value = Number(value);
83                 if (isNaN(value) || value === 0) return value;
84                 return value > 0 ? 1 : -1;
85         };
86
87         var sign = isImplemented$2() ? Math.sign : shim$1;
88
89         var abs   = Math.abs
90           , floor = Math.floor;
91
92         var toInteger = function (value) {
93                 if (isNaN(value)) return 0;
94                 value = Number(value);
95                 if (value === 0 || !isFinite(value)) return value;
96                 return sign(value) * floor(abs(value));
97         };
98
99         var max       = Math.max;
100
101         var toPosInteger = function (value) { return max(0, toInteger(value)); };
102
103         var indexOf           = Array.prototype.indexOf
104           , objHasOwnProperty = Object.prototype.hasOwnProperty
105           , abs$1               = Math.abs
106           , floor$1             = Math.floor;
107
108         var eIndexOf = function (searchElement/*, fromIndex*/) {
109                 var i, length, fromIndex, val;
110                 if (!isNan(searchElement)) return indexOf.apply(this, arguments);
111
112                 length = toPosInteger(validValue(this).length);
113                 fromIndex = arguments[1];
114                 if (isNaN(fromIndex)) fromIndex = 0;
115                 else if (fromIndex >= 0) fromIndex = floor$1(fromIndex);
116                 else fromIndex = toPosInteger(this.length) - floor$1(abs$1(fromIndex));
117
118                 for (i = fromIndex; i < length; ++i) {
119                         if (objHasOwnProperty.call(this, i)) {
120                                 val = this[i];
121                                 if (isNan(val)) return i; // Jslint: ignore
122                         }
123                 }
124                 return -1;
125         };
126
127         var create = Object.create, getPrototypeOf = Object.getPrototypeOf, plainObject = {};
128
129         var isImplemented$3 = function (/* CustomCreate*/) {
130                 var setPrototypeOf = Object.setPrototypeOf, customCreate = arguments[0] || create;
131                 if (typeof setPrototypeOf !== "function") return false;
132                 return getPrototypeOf(setPrototypeOf(customCreate(null), plainObject)) === plainObject;
133         };
134
135         var map = { function: true, object: true };
136
137         var isObject = function (value) { return (isValue(value) && map[typeof value]) || false; };
138
139         var create$1 = Object.create, shim$2;
140
141         if (!isImplemented$3()) {
142                 shim$2 = shim$3;
143         }
144
145         var create_1 = (function () {
146                 var nullObject, polyProps, desc;
147                 if (!shim$2) return create$1;
148                 if (shim$2.level !== 1) return create$1;
149
150                 nullObject = {};
151                 polyProps = {};
152                 desc = { configurable: false, enumerable: false, writable: true, value: undefined };
153                 Object.getOwnPropertyNames(Object.prototype).forEach(function (name) {
154                         if (name === "__proto__") {
155                                 polyProps[name] = {
156                                         configurable: true,
157                                         enumerable: false,
158                                         writable: true,
159                                         value: undefined
160                                 };
161                                 return;
162                         }
163                         polyProps[name] = desc;
164                 });
165                 Object.defineProperties(nullObject, polyProps);
166
167                 Object.defineProperty(shim$2, "nullPolyfill", {
168                         configurable: false,
169                         enumerable: false,
170                         writable: false,
171                         value: nullObject
172                 });
173
174                 return function (prototype, props) {
175                         return create$1(prototype === null ? nullObject : prototype, props);
176                 };
177         })();
178
179         var objIsPrototypeOf = Object.prototype.isPrototypeOf
180           , defineProperty   = Object.defineProperty
181           , nullDesc         = { configurable: true, enumerable: false, writable: true, value: undefined }
182           , validate;
183
184         validate = function (obj, prototype) {
185                 validValue(obj);
186                 if (prototype === null || isObject(prototype)) return obj;
187                 throw new TypeError("Prototype must be null or an object");
188         };
189
190         var shim$3 = (function (status) {
191                 var fn, set;
192                 if (!status) return null;
193                 if (status.level === 2) {
194                         if (status.set) {
195                                 set = status.set;
196                                 fn = function (obj, prototype) {
197                                         set.call(validate(obj, prototype), prototype);
198                                         return obj;
199                                 };
200                         } else {
201                                 fn = function (obj, prototype) {
202                                         validate(obj, prototype).__proto__ = prototype;
203                                         return obj;
204                                 };
205                         }
206                 } else {
207                         fn = function self(obj, prototype) {
208                                 var isNullBase;
209                                 validate(obj, prototype);
210                                 isNullBase = objIsPrototypeOf.call(self.nullPolyfill, obj);
211                                 if (isNullBase) delete self.nullPolyfill.__proto__;
212                                 if (prototype === null) prototype = self.nullPolyfill;
213                                 obj.__proto__ = prototype;
214                                 if (isNullBase) defineProperty(self.nullPolyfill, "__proto__", nullDesc);
215                                 return obj;
216                         };
217                 }
218                 return Object.defineProperty(fn, "level", {
219                         configurable: false,
220                         enumerable: false,
221                         writable: false,
222                         value: status.level
223                 });
224         })(
225                 (function () {
226                         var tmpObj1 = Object.create(null)
227                           , tmpObj2 = {}
228                           , set
229                           , desc = Object.getOwnPropertyDescriptor(Object.prototype, "__proto__");
230
231                         if (desc) {
232                                 try {
233                                         set = desc.set; // Opera crashes at this point
234                                         set.call(tmpObj1, tmpObj2);
235                                 } catch (ignore) {}
236                                 if (Object.getPrototypeOf(tmpObj1) === tmpObj2) return { set: set, level: 2 };
237                         }
238
239                         tmpObj1.__proto__ = tmpObj2;
240                         if (Object.getPrototypeOf(tmpObj1) === tmpObj2) return { level: 2 };
241
242                         tmpObj1 = {};
243                         tmpObj1.__proto__ = tmpObj2;
244                         if (Object.getPrototypeOf(tmpObj1) === tmpObj2) return { level: 1 };
245
246                         return false;
247                 })()
248         );
249
250         var setPrototypeOf = isImplemented$3() ? Object.setPrototypeOf : shim$3;
251
252         var validCallable = function (fn) {
253                 if (typeof fn !== "function") throw new TypeError(fn + " is not a function");
254                 return fn;
255         };
256
257         // ES3 safe
258         var _undefined$1 = void 0;
259
260         var is = function (value) { return value !== _undefined$1 && value !== null; };
261
262         // prettier-ignore
263         var possibleTypes = { "object": true, "function": true, "undefined": true /* document.all */ };
264
265         var is$1 = function (value) {
266                 if (!is(value)) return false;
267                 return hasOwnProperty.call(possibleTypes, typeof value);
268         };
269
270         var is$2 = function (value) {
271                 if (!is$1(value)) return false;
272                 try {
273                         if (!value.constructor) return false;
274                         return value.constructor.prototype === value;
275                 } catch (error) {
276                         return false;
277                 }
278         };
279
280         var is$3 = function (value) {
281                 if (typeof value !== "function") return false;
282
283                 if (!hasOwnProperty.call(value, "length")) return false;
284
285                 try {
286                         if (typeof value.length !== "number") return false;
287                         if (typeof value.call !== "function") return false;
288                         if (typeof value.apply !== "function") return false;
289                 } catch (error) {
290                         return false;
291                 }
292
293                 return !is$2(value);
294         };
295
296         var classRe = /^\s*class[\s{/}]/, functionToString = Function.prototype.toString;
297
298         var is$4 = function (value) {
299                 if (!is$3(value)) return false;
300                 if (classRe.test(functionToString.call(value))) return false;
301                 return true;
302         };
303
304         var isImplemented$4 = function () {
305                 var assign = Object.assign, obj;
306                 if (typeof assign !== "function") return false;
307                 obj = { foo: "raz" };
308                 assign(obj, { bar: "dwa" }, { trzy: "trzy" });
309                 return obj.foo + obj.bar + obj.trzy === "razdwatrzy";
310         };
311
312         var isImplemented$5 = function () {
313                 try {
314                         Object.keys("primitive");
315                         return true;
316                 } catch (e) {
317                         return false;
318                 }
319         };
320
321         var keys = Object.keys;
322
323         var shim$4 = function (object) { return keys(isValue(object) ? Object(object) : object); };
324
325         var keys$1 = isImplemented$5() ? Object.keys : shim$4;
326
327         var max$1   = Math.max;
328
329         var shim$5 = function (dest, src/*, …srcn*/) {
330                 var error, i, length = max$1(arguments.length, 2), assign;
331                 dest = Object(validValue(dest));
332                 assign = function (key) {
333                         try {
334                                 dest[key] = src[key];
335                         } catch (e) {
336                                 if (!error) error = e;
337                         }
338                 };
339                 for (i = 1; i < length; ++i) {
340                         src = arguments[i];
341                         keys$1(src).forEach(assign);
342                 }
343                 if (error !== undefined) throw error;
344                 return dest;
345         };
346
347         var assign = isImplemented$4() ? Object.assign : shim$5;
348
349         var forEach = Array.prototype.forEach, create$2 = Object.create;
350
351         var process$1 = function (src, obj) {
352                 var key;
353                 for (key in src) obj[key] = src[key];
354         };
355
356         // eslint-disable-next-line no-unused-vars
357         var normalizeOptions = function (opts1/*, …options*/) {
358                 var result = create$2(null);
359                 forEach.call(arguments, function (options) {
360                         if (!isValue(options)) return;
361                         process$1(Object(options), result);
362                 });
363                 return result;
364         };
365
366         var str = "razdwatrzy";
367
368         var isImplemented$6 = function () {
369                 if (typeof str.contains !== "function") return false;
370                 return str.contains("dwa") === true && str.contains("foo") === false;
371         };
372
373         var indexOf$1 = String.prototype.indexOf;
374
375         var shim$6 = function (searchString/*, position*/) {
376                 return indexOf$1.call(this, searchString, arguments[1]) > -1;
377         };
378
379         var contains = isImplemented$6() ? String.prototype.contains : shim$6;
380
381         var d_1 = createCommonjsModule(function (module) {
382
383
384
385         var d = (module.exports = function (dscr, value/*, options*/) {
386                 var c, e, w, options, desc;
387                 if (arguments.length < 2 || typeof dscr !== "string") {
388                         options = value;
389                         value = dscr;
390                         dscr = null;
391                 } else {
392                         options = arguments[2];
393                 }
394                 if (is(dscr)) {
395                         c = contains.call(dscr, "c");
396                         e = contains.call(dscr, "e");
397                         w = contains.call(dscr, "w");
398                 } else {
399                         c = w = true;
400                         e = false;
401                 }
402
403                 desc = { value: value, configurable: c, enumerable: e, writable: w };
404                 return !options ? desc : assign(normalizeOptions(options), desc);
405         });
406
407         d.gs = function (dscr, get, set/*, options*/) {
408                 var c, e, options, desc;
409                 if (typeof dscr !== "string") {
410                         options = set;
411                         set = get;
412                         get = dscr;
413                         dscr = null;
414                 } else {
415                         options = arguments[3];
416                 }
417                 if (!is(get)) {
418                         get = undefined;
419                 } else if (!is$4(get)) {
420                         options = get;
421                         get = set = undefined;
422                 } else if (!is(set)) {
423                         set = undefined;
424                 } else if (!is$4(set)) {
425                         options = set;
426                         set = undefined;
427                 }
428                 if (is(dscr)) {
429                         c = contains.call(dscr, "c");
430                         e = contains.call(dscr, "e");
431                 } else {
432                         c = true;
433                         e = false;
434                 }
435
436                 desc = { get: get, set: set, configurable: c, enumerable: e };
437                 return !options ? desc : assign(normalizeOptions(options), desc);
438         };
439         });
440
441         var eventEmitter = createCommonjsModule(function (module, exports) {
442
443         var apply = Function.prototype.apply, call = Function.prototype.call
444           , create = Object.create, defineProperty = Object.defineProperty
445           , defineProperties = Object.defineProperties
446           , hasOwnProperty = Object.prototype.hasOwnProperty
447           , descriptor = { configurable: true, enumerable: false, writable: true }
448
449           , on, once, off, emit, methods, descriptors, base;
450
451         on = function (type, listener) {
452                 var data;
453
454                 validCallable(listener);
455
456                 if (!hasOwnProperty.call(this, '__ee__')) {
457                         data = descriptor.value = create(null);
458                         defineProperty(this, '__ee__', descriptor);
459                         descriptor.value = null;
460                 } else {
461                         data = this.__ee__;
462                 }
463                 if (!data[type]) data[type] = listener;
464                 else if (typeof data[type] === 'object') data[type].push(listener);
465                 else data[type] = [data[type], listener];
466
467                 return this;
468         };
469
470         once = function (type, listener) {
471                 var once, self;
472
473                 validCallable(listener);
474                 self = this;
475                 on.call(this, type, once = function () {
476                         off.call(self, type, once);
477                         apply.call(listener, this, arguments);
478                 });
479
480                 once.__eeOnceListener__ = listener;
481                 return this;
482         };
483
484         off = function (type, listener) {
485                 var data, listeners, candidate, i;
486
487                 validCallable(listener);
488
489                 if (!hasOwnProperty.call(this, '__ee__')) return this;
490                 data = this.__ee__;
491                 if (!data[type]) return this;
492                 listeners = data[type];
493
494                 if (typeof listeners === 'object') {
495                         for (i = 0; (candidate = listeners[i]); ++i) {
496                                 if ((candidate === listener) ||
497                                                 (candidate.__eeOnceListener__ === listener)) {
498                                         if (listeners.length === 2) data[type] = listeners[i ? 0 : 1];
499                                         else listeners.splice(i, 1);
500                                 }
501                         }
502                 } else {
503                         if ((listeners === listener) ||
504                                         (listeners.__eeOnceListener__ === listener)) {
505                                 delete data[type];
506                         }
507                 }
508
509                 return this;
510         };
511
512         emit = function (type) {
513                 var i, l, listener, listeners, args;
514
515                 if (!hasOwnProperty.call(this, '__ee__')) return;
516                 listeners = this.__ee__[type];
517                 if (!listeners) return;
518
519                 if (typeof listeners === 'object') {
520                         l = arguments.length;
521                         args = new Array(l - 1);
522                         for (i = 1; i < l; ++i) args[i - 1] = arguments[i];
523
524                         listeners = listeners.slice();
525                         for (i = 0; (listener = listeners[i]); ++i) {
526                                 apply.call(listener, this, args);
527                         }
528                 } else {
529                         switch (arguments.length) {
530                         case 1:
531                                 call.call(listeners, this);
532                                 break;
533                         case 2:
534                                 call.call(listeners, this, arguments[1]);
535                                 break;
536                         case 3:
537                                 call.call(listeners, this, arguments[1], arguments[2]);
538                                 break;
539                         default:
540                                 l = arguments.length;
541                                 args = new Array(l - 1);
542                                 for (i = 1; i < l; ++i) {
543                                         args[i - 1] = arguments[i];
544                                 }
545                                 apply.call(listeners, this, args);
546                         }
547                 }
548         };
549
550         methods = {
551                 on: on,
552                 once: once,
553                 off: off,
554                 emit: emit
555         };
556
557         descriptors = {
558                 on: d_1(on),
559                 once: d_1(once),
560                 off: d_1(off),
561                 emit: d_1(emit)
562         };
563
564         base = defineProperties({}, descriptors);
565
566         module.exports = exports = function (o) {
567                 return (o == null) ? create(base) : defineProperties(Object(o), descriptors);
568         };
569         exports.methods = methods;
570         });
571
572         var validTypes = { object: true, symbol: true };
573
574         var isImplemented$7 = function () {
575                 var symbol;
576                 if (typeof Symbol !== 'function') return false;
577                 symbol = Symbol('test symbol');
578                 try { String(symbol); } catch (e) { return false; }
579
580                 // Return 'true' also for polyfills
581                 if (!validTypes[typeof Symbol.iterator]) return false;
582                 if (!validTypes[typeof Symbol.toPrimitive]) return false;
583                 if (!validTypes[typeof Symbol.toStringTag]) return false;
584
585                 return true;
586         };
587
588         var isSymbol = function (x) {
589                 if (!x) return false;
590                 if (typeof x === 'symbol') return true;
591                 if (!x.constructor) return false;
592                 if (x.constructor.name !== 'Symbol') return false;
593                 return (x[x.constructor.toStringTag] === 'Symbol');
594         };
595
596         var validateSymbol = function (value) {
597                 if (!isSymbol(value)) throw new TypeError(value + " is not a symbol");
598                 return value;
599         };
600
601         var create$3 = Object.create, defineProperties = Object.defineProperties
602           , defineProperty$1 = Object.defineProperty, objPrototype = Object.prototype
603           , NativeSymbol, SymbolPolyfill, HiddenSymbol, globalSymbols = create$3(null)
604           , isNativeSafe;
605
606         if (typeof Symbol === 'function') {
607                 NativeSymbol = Symbol;
608                 try {
609                         String(NativeSymbol());
610                         isNativeSafe = true;
611                 } catch (ignore) {}
612         }
613
614         var generateName = (function () {
615                 var created = create$3(null);
616                 return function (desc) {
617                         var postfix = 0, name, ie11BugWorkaround;
618                         while (created[desc + (postfix || '')]) ++postfix;
619                         desc += (postfix || '');
620                         created[desc] = true;
621                         name = '@@' + desc;
622                         defineProperty$1(objPrototype, name, d_1.gs(null, function (value) {
623                                 // For IE11 issue see:
624                                 // https://connect.microsoft.com/IE/feedbackdetail/view/1928508/
625                                 //    ie11-broken-getters-on-dom-objects
626                                 // https://github.com/medikoo/es6-symbol/issues/12
627                                 if (ie11BugWorkaround) return;
628                                 ie11BugWorkaround = true;
629                                 defineProperty$1(this, name, d_1(value));
630                                 ie11BugWorkaround = false;
631                         }));
632                         return name;
633                 };
634         }());
635
636         // Internal constructor (not one exposed) for creating Symbol instances.
637         // This one is used to ensure that `someSymbol instanceof Symbol` always return false
638         HiddenSymbol = function Symbol(description) {
639                 if (this instanceof HiddenSymbol) throw new TypeError('Symbol is not a constructor');
640                 return SymbolPolyfill(description);
641         };
642
643         // Exposed `Symbol` constructor
644         // (returns instances of HiddenSymbol)
645         var polyfill = SymbolPolyfill = function Symbol(description) {
646                 var symbol;
647                 if (this instanceof Symbol) throw new TypeError('Symbol is not a constructor');
648                 if (isNativeSafe) return NativeSymbol(description);
649                 symbol = create$3(HiddenSymbol.prototype);
650                 description = (description === undefined ? '' : String(description));
651                 return defineProperties(symbol, {
652                         __description__: d_1('', description),
653                         __name__: d_1('', generateName(description))
654                 });
655         };
656         defineProperties(SymbolPolyfill, {
657                 for: d_1(function (key) {
658                         if (globalSymbols[key]) return globalSymbols[key];
659                         return (globalSymbols[key] = SymbolPolyfill(String(key)));
660                 }),
661                 keyFor: d_1(function (s) {
662                         var key;
663                         validateSymbol(s);
664                         for (key in globalSymbols) if (globalSymbols[key] === s) return key;
665                 }),
666
667                 // To ensure proper interoperability with other native functions (e.g. Array.from)
668                 // fallback to eventual native implementation of given symbol
669                 hasInstance: d_1('', (NativeSymbol && NativeSymbol.hasInstance) || SymbolPolyfill('hasInstance')),
670                 isConcatSpreadable: d_1('', (NativeSymbol && NativeSymbol.isConcatSpreadable) ||
671                         SymbolPolyfill('isConcatSpreadable')),
672                 iterator: d_1('', (NativeSymbol && NativeSymbol.iterator) || SymbolPolyfill('iterator')),
673                 match: d_1('', (NativeSymbol && NativeSymbol.match) || SymbolPolyfill('match')),
674                 replace: d_1('', (NativeSymbol && NativeSymbol.replace) || SymbolPolyfill('replace')),
675                 search: d_1('', (NativeSymbol && NativeSymbol.search) || SymbolPolyfill('search')),
676                 species: d_1('', (NativeSymbol && NativeSymbol.species) || SymbolPolyfill('species')),
677                 split: d_1('', (NativeSymbol && NativeSymbol.split) || SymbolPolyfill('split')),
678                 toPrimitive: d_1('', (NativeSymbol && NativeSymbol.toPrimitive) || SymbolPolyfill('toPrimitive')),
679                 toStringTag: d_1('', (NativeSymbol && NativeSymbol.toStringTag) || SymbolPolyfill('toStringTag')),
680                 unscopables: d_1('', (NativeSymbol && NativeSymbol.unscopables) || SymbolPolyfill('unscopables'))
681         });
682
683         // Internal tweaks for real symbol producer
684         defineProperties(HiddenSymbol.prototype, {
685                 constructor: d_1(SymbolPolyfill),
686                 toString: d_1('', function () { return this.__name__; })
687         });
688
689         // Proper implementation of methods exposed on Symbol.prototype
690         // They won't be accessible on produced symbol instances as they derive from HiddenSymbol.prototype
691         defineProperties(SymbolPolyfill.prototype, {
692                 toString: d_1(function () { return 'Symbol (' + validateSymbol(this).__description__ + ')'; }),
693                 valueOf: d_1(function () { return validateSymbol(this); })
694         });
695         defineProperty$1(SymbolPolyfill.prototype, SymbolPolyfill.toPrimitive, d_1('', function () {
696                 var symbol = validateSymbol(this);
697                 if (typeof symbol === 'symbol') return symbol;
698                 return symbol.toString();
699         }));
700         defineProperty$1(SymbolPolyfill.prototype, SymbolPolyfill.toStringTag, d_1('c', 'Symbol'));
701
702         // Proper implementaton of toPrimitive and toStringTag for returned symbol instances
703         defineProperty$1(HiddenSymbol.prototype, SymbolPolyfill.toStringTag,
704                 d_1('c', SymbolPolyfill.prototype[SymbolPolyfill.toStringTag]));
705
706         // Note: It's important to define `toPrimitive` as last one, as some implementations
707         // implement `toPrimitive` natively without implementing `toStringTag` (or other specified symbols)
708         // And that may invoke error in definition flow:
709         // See: https://github.com/medikoo/es6-symbol/issues/13#issuecomment-164146149
710         defineProperty$1(HiddenSymbol.prototype, SymbolPolyfill.toPrimitive,
711                 d_1('c', SymbolPolyfill.prototype[SymbolPolyfill.toPrimitive]));
712
713         var es6Symbol = isImplemented$7() ? Symbol : polyfill;
714
715         var objToString = Object.prototype.toString
716           , id = objToString.call((function () { return arguments; })());
717
718         var isArguments = function (value) { return objToString.call(value) === id; };
719
720         var objToString$1 = Object.prototype.toString, id$1 = objToString$1.call("");
721
722         var isString = function (value) {
723                 return (
724                         typeof value === "string" ||
725                         (value &&
726                                 typeof value === "object" &&
727                                 (value instanceof String || objToString$1.call(value) === id$1)) ||
728                         false
729                 );
730         };
731
732         var isImplemented$8 = function () {
733                 if (typeof globalThis !== "object") return false;
734                 if (!globalThis) return false;
735                 return globalThis.Array === Array;
736         };
737
738         var naiveFallback = function () {
739                 if (typeof self === "object" && self) return self;
740                 if (typeof window === "object" && window) return window;
741                 throw new Error("Unable to resolve global `this`");
742         };
743
744         var implementation = (function () {
745                 if (this) return this;
746
747                 // Unexpected strict mode (may happen if e.g. bundled into ESM module)
748
749                 // Thanks @mathiasbynens -> https://mathiasbynens.be/notes/globalthis
750                 // In all ES5+ engines global object inherits from Object.prototype
751                 // (if you approached one that doesn't please report)
752                 try {
753                         Object.defineProperty(Object.prototype, "__global__", {
754                                 get: function () { return this; },
755                                 configurable: true
756                         });
757                 } catch (error) {
758                         // Unfortunate case of Object.prototype being sealed (via preventExtensions, seal or freeze)
759                         return naiveFallback();
760                 }
761                 try {
762                         // Safari case (window.__global__ is resolved with global context, but __global__ does not)
763                         if (!__global__) return naiveFallback();
764                         return __global__;
765                 } finally {
766                         delete Object.prototype.__global__;
767                 }
768         })();
769
770         var globalThis_1 = isImplemented$8() ? globalThis : implementation;
771
772         var validTypes$1 = { object: true, symbol: true };
773
774         var isImplemented$9 = function () {
775                 var Symbol = globalThis_1.Symbol;
776                 var symbol;
777                 if (typeof Symbol !== "function") return false;
778                 symbol = Symbol("test symbol");
779                 try { String(symbol); }
780                 catch (e) { return false; }
781
782                 // Return 'true' also for polyfills
783                 if (!validTypes$1[typeof Symbol.iterator]) return false;
784                 if (!validTypes$1[typeof Symbol.toPrimitive]) return false;
785                 if (!validTypes$1[typeof Symbol.toStringTag]) return false;
786
787                 return true;
788         };
789
790         var isSymbol$1 = function (value) {
791                 if (!value) return false;
792                 if (typeof value === "symbol") return true;
793                 if (!value.constructor) return false;
794                 if (value.constructor.name !== "Symbol") return false;
795                 return value[value.constructor.toStringTag] === "Symbol";
796         };
797
798         var validateSymbol$1 = function (value) {
799                 if (!isSymbol$1(value)) throw new TypeError(value + " is not a symbol");
800                 return value;
801         };
802
803         var create$4 = Object.create, defineProperty$2 = Object.defineProperty, objPrototype$1 = Object.prototype;
804
805         var created = create$4(null);
806         var generateName$1 = function (desc) {
807                 var postfix = 0, name, ie11BugWorkaround;
808                 while (created[desc + (postfix || "")]) ++postfix;
809                 desc += postfix || "";
810                 created[desc] = true;
811                 name = "@@" + desc;
812                 defineProperty$2(
813                         objPrototype$1,
814                         name,
815                         d_1.gs(null, function (value) {
816                                 // For IE11 issue see:
817                                 // https://connect.microsoft.com/IE/feedbackdetail/view/1928508/
818                                 //    ie11-broken-getters-on-dom-objects
819                                 // https://github.com/medikoo/es6-symbol/issues/12
820                                 if (ie11BugWorkaround) return;
821                                 ie11BugWorkaround = true;
822                                 defineProperty$2(this, name, d_1(value));
823                                 ie11BugWorkaround = false;
824                         })
825                 );
826                 return name;
827         };
828
829         var NativeSymbol$1 = globalThis_1.Symbol;
830
831         var standardSymbols = function (SymbolPolyfill) {
832                 return Object.defineProperties(SymbolPolyfill, {
833                         // To ensure proper interoperability with other native functions (e.g. Array.from)
834                         // fallback to eventual native implementation of given symbol
835                         hasInstance: d_1(
836                                 "", (NativeSymbol$1 && NativeSymbol$1.hasInstance) || SymbolPolyfill("hasInstance")
837                         ),
838                         isConcatSpreadable: d_1(
839                                 "",
840                                 (NativeSymbol$1 && NativeSymbol$1.isConcatSpreadable) ||
841                                         SymbolPolyfill("isConcatSpreadable")
842                         ),
843                         iterator: d_1("", (NativeSymbol$1 && NativeSymbol$1.iterator) || SymbolPolyfill("iterator")),
844                         match: d_1("", (NativeSymbol$1 && NativeSymbol$1.match) || SymbolPolyfill("match")),
845                         replace: d_1("", (NativeSymbol$1 && NativeSymbol$1.replace) || SymbolPolyfill("replace")),
846                         search: d_1("", (NativeSymbol$1 && NativeSymbol$1.search) || SymbolPolyfill("search")),
847                         species: d_1("", (NativeSymbol$1 && NativeSymbol$1.species) || SymbolPolyfill("species")),
848                         split: d_1("", (NativeSymbol$1 && NativeSymbol$1.split) || SymbolPolyfill("split")),
849                         toPrimitive: d_1(
850                                 "", (NativeSymbol$1 && NativeSymbol$1.toPrimitive) || SymbolPolyfill("toPrimitive")
851                         ),
852                         toStringTag: d_1(
853                                 "", (NativeSymbol$1 && NativeSymbol$1.toStringTag) || SymbolPolyfill("toStringTag")
854                         ),
855                         unscopables: d_1(
856                                 "", (NativeSymbol$1 && NativeSymbol$1.unscopables) || SymbolPolyfill("unscopables")
857                         )
858                 });
859         };
860
861         var registry = Object.create(null);
862
863         var symbolRegistry = function (SymbolPolyfill) {
864                 return Object.defineProperties(SymbolPolyfill, {
865                         for: d_1(function (key) {
866                                 if (registry[key]) return registry[key];
867                                 return (registry[key] = SymbolPolyfill(String(key)));
868                         }),
869                         keyFor: d_1(function (symbol) {
870                                 var key;
871                                 validateSymbol$1(symbol);
872                                 for (key in registry) {
873                                         if (registry[key] === symbol) return key;
874                                 }
875                                 return undefined;
876                         })
877                 });
878         };
879
880         var NativeSymbol$2         = globalThis_1.Symbol;
881
882         var create$5 = Object.create
883           , defineProperties$1 = Object.defineProperties
884           , defineProperty$3 = Object.defineProperty;
885
886         var SymbolPolyfill$1, HiddenSymbol$1, isNativeSafe$1;
887
888         if (typeof NativeSymbol$2 === "function") {
889                 try {
890                         String(NativeSymbol$2());
891                         isNativeSafe$1 = true;
892                 } catch (ignore) {}
893         } else {
894                 NativeSymbol$2 = null;
895         }
896
897         // Internal constructor (not one exposed) for creating Symbol instances.
898         // This one is used to ensure that `someSymbol instanceof Symbol` always return false
899         HiddenSymbol$1 = function Symbol(description) {
900                 if (this instanceof HiddenSymbol$1) throw new TypeError("Symbol is not a constructor");
901                 return SymbolPolyfill$1(description);
902         };
903
904         // Exposed `Symbol` constructor
905         // (returns instances of HiddenSymbol)
906         var polyfill$1 = SymbolPolyfill$1 = function Symbol(description) {
907                 var symbol;
908                 if (this instanceof Symbol) throw new TypeError("Symbol is not a constructor");
909                 if (isNativeSafe$1) return NativeSymbol$2(description);
910                 symbol = create$5(HiddenSymbol$1.prototype);
911                 description = description === undefined ? "" : String(description);
912                 return defineProperties$1(symbol, {
913                         __description__: d_1("", description),
914                         __name__: d_1("", generateName$1(description))
915                 });
916         };
917
918         standardSymbols(SymbolPolyfill$1);
919         symbolRegistry(SymbolPolyfill$1);
920
921         // Internal tweaks for real symbol producer
922         defineProperties$1(HiddenSymbol$1.prototype, {
923                 constructor: d_1(SymbolPolyfill$1),
924                 toString: d_1("", function () { return this.__name__; })
925         });
926
927         // Proper implementation of methods exposed on Symbol.prototype
928         // They won't be accessible on produced symbol instances as they derive from HiddenSymbol.prototype
929         defineProperties$1(SymbolPolyfill$1.prototype, {
930                 toString: d_1(function () { return "Symbol (" + validateSymbol$1(this).__description__ + ")"; }),
931                 valueOf: d_1(function () { return validateSymbol$1(this); })
932         });
933         defineProperty$3(
934                 SymbolPolyfill$1.prototype,
935                 SymbolPolyfill$1.toPrimitive,
936                 d_1("", function () {
937                         var symbol = validateSymbol$1(this);
938                         if (typeof symbol === "symbol") return symbol;
939                         return symbol.toString();
940                 })
941         );
942         defineProperty$3(SymbolPolyfill$1.prototype, SymbolPolyfill$1.toStringTag, d_1("c", "Symbol"));
943
944         // Proper implementaton of toPrimitive and toStringTag for returned symbol instances
945         defineProperty$3(
946                 HiddenSymbol$1.prototype, SymbolPolyfill$1.toStringTag,
947                 d_1("c", SymbolPolyfill$1.prototype[SymbolPolyfill$1.toStringTag])
948         );
949
950         // Note: It's important to define `toPrimitive` as last one, as some implementations
951         // implement `toPrimitive` natively without implementing `toStringTag` (or other specified symbols)
952         // And that may invoke error in definition flow:
953         // See: https://github.com/medikoo/es6-symbol/issues/13#issuecomment-164146149
954         defineProperty$3(
955                 HiddenSymbol$1.prototype, SymbolPolyfill$1.toPrimitive,
956                 d_1("c", SymbolPolyfill$1.prototype[SymbolPolyfill$1.toPrimitive])
957         );
958
959         var es6Symbol$1 = isImplemented$9()
960                 ? globalThis_1.Symbol
961                 : polyfill$1;
962
963         var iteratorSymbol = es6Symbol$1.iterator
964           , isArray        = Array.isArray;
965
966         var isIterable = function (value) {
967                 if (!isValue(value)) return false;
968                 if (isArray(value)) return true;
969                 if (isString(value)) return true;
970                 if (isArguments(value)) return true;
971                 return typeof value[iteratorSymbol] === "function";
972         };
973
974         var validIterable = function (value) {
975                 if (!isIterable(value)) throw new TypeError(value + " is not iterable");
976                 return value;
977         };
978
979         var objectToString = Object.prototype.toString;
980
981         var coerce = function (value) {
982                 if (!is(value)) return null;
983                 if (is$1(value)) {
984                         // Reject Object.prototype.toString coercion
985                         var valueToString = value.toString;
986                         if (typeof valueToString !== "function") return null;
987                         if (valueToString === objectToString) return null;
988                         // Note: It can be object coming from other realm, still as there's no ES3 and CSP compliant
989                         // way to resolve its realm's Object.prototype.toString it's left as not addressed edge case
990                 }
991                 try {
992                         return "" + value; // Ensure implicit coercion
993                 } catch (error) {
994                         return null;
995                 }
996         };
997
998         var safeToString = function (value) {
999                 try {
1000                         return value.toString();
1001                 } catch (error) {
1002                         try { return String(value); }
1003                         catch (error2) { return null; }
1004                 }
1005         };
1006
1007         var reNewLine = /[\n\r\u2028\u2029]/g;
1008
1009         var toShortString = function (value) {
1010                 var string = safeToString(value);
1011                 if (string === null) return "<Non-coercible to string value>";
1012                 // Trim if too long
1013                 if (string.length > 100) string = string.slice(0, 99) + "…";
1014                 // Replace eventual new lines
1015                 string = string.replace(reNewLine, function (char) {
1016                         switch (char) {
1017                                 case "\n":
1018                                         return "\\n";
1019                                 case "\r":
1020                                         return "\\r";
1021                                 case "\u2028":
1022                                         return "\\u2028";
1023                                 case "\u2029":
1024                                         return "\\u2029";
1025                                 /* istanbul ignore next */
1026                                 default:
1027                                         throw new Error("Unexpected character");
1028                         }
1029                 });
1030                 return string;
1031         };
1032
1033         var resolveMessage = function (message, value) {
1034                 return message.replace("%v", toShortString(value));
1035         };
1036
1037         var resolveException = function (value, defaultMessage, inputOptions) {
1038                 if (!is$1(inputOptions)) throw new TypeError(resolveMessage(defaultMessage, value));
1039                 if (!is(value)) {
1040                         if ("default" in inputOptions) return inputOptions["default"];
1041                         if (inputOptions.isOptional) return null;
1042                 }
1043                 var errorMessage = coerce(inputOptions.errorMessage);
1044                 if (!is(errorMessage)) errorMessage = defaultMessage;
1045                 throw new TypeError(resolveMessage(errorMessage, value));
1046         };
1047
1048         var ensure = function (value/*, options*/) {
1049                 if (is(value)) return value;
1050                 return resolveException(value, "Cannot use %v", arguments[1]);
1051         };
1052
1053         var ensure$1 = function (value/*, options*/) {
1054                 if (is$4(value)) return value;
1055                 return resolveException(value, "%v is not a plain function", arguments[1]);
1056         };
1057
1058         var isImplemented$a = function () {
1059                 var from = Array.from, arr, result;
1060                 if (typeof from !== "function") return false;
1061                 arr = ["raz", "dwa"];
1062                 result = from(arr);
1063                 return Boolean(result && result !== arr && result[1] === "dwa");
1064         };
1065
1066         var objToString$2 = Object.prototype.toString
1067           , isFunctionStringTag = RegExp.prototype.test.bind(/^[object [A-Za-z0-9]*Function]$/);
1068
1069         var isFunction = function (value) {
1070                 return typeof value === "function" && isFunctionStringTag(objToString$2.call(value));
1071         };
1072
1073         var iteratorSymbol$1 = es6Symbol$1.iterator
1074           , isArray$1        = Array.isArray
1075           , call           = Function.prototype.call
1076           , desc           = { configurable: true, enumerable: true, writable: true, value: null }
1077           , defineProperty$4 = Object.defineProperty;
1078
1079         // eslint-disable-next-line complexity, max-lines-per-function
1080         var shim$7 = function (arrayLike/*, mapFn, thisArg*/) {
1081                 var mapFn = arguments[1]
1082                   , thisArg = arguments[2]
1083                   , Context
1084                   , i
1085                   , j
1086                   , arr
1087                   , length
1088                   , code
1089                   , iterator
1090                   , result
1091                   , getIterator
1092                   , value;
1093
1094                 arrayLike = Object(validValue(arrayLike));
1095
1096                 if (isValue(mapFn)) validCallable(mapFn);
1097                 if (!this || this === Array || !isFunction(this)) {
1098                         // Result: Plain array
1099                         if (!mapFn) {
1100                                 if (isArguments(arrayLike)) {
1101                                         // Source: Arguments
1102                                         length = arrayLike.length;
1103                                         if (length !== 1) return Array.apply(null, arrayLike);
1104                                         arr = new Array(1);
1105                                         arr[0] = arrayLike[0];
1106                                         return arr;
1107                                 }
1108                                 if (isArray$1(arrayLike)) {
1109                                         // Source: Array
1110                                         arr = new Array((length = arrayLike.length));
1111                                         for (i = 0; i < length; ++i) arr[i] = arrayLike[i];
1112                                         return arr;
1113                                 }
1114                         }
1115                         arr = [];
1116                 } else {
1117                         // Result: Non plain array
1118                         Context = this;
1119                 }
1120
1121                 if (!isArray$1(arrayLike)) {
1122                         if ((getIterator = arrayLike[iteratorSymbol$1]) !== undefined) {
1123                                 // Source: Iterator
1124                                 iterator = validCallable(getIterator).call(arrayLike);
1125                                 if (Context) arr = new Context();
1126                                 result = iterator.next();
1127                                 i = 0;
1128                                 while (!result.done) {
1129                                         value = mapFn ? call.call(mapFn, thisArg, result.value, i) : result.value;
1130                                         if (Context) {
1131                                                 desc.value = value;
1132                                                 defineProperty$4(arr, i, desc);
1133                                         } else {
1134                                                 arr[i] = value;
1135                                         }
1136                                         result = iterator.next();
1137                                         ++i;
1138                                 }
1139                                 length = i;
1140                         } else if (isString(arrayLike)) {
1141                                 // Source: String
1142                                 length = arrayLike.length;
1143                                 if (Context) arr = new Context();
1144                                 for (i = 0, j = 0; i < length; ++i) {
1145                                         value = arrayLike[i];
1146                                         if (i + 1 < length) {
1147                                                 code = value.charCodeAt(0);
1148                                                 // eslint-disable-next-line max-depth
1149                                                 if (code >= 0xd800 && code <= 0xdbff) value += arrayLike[++i];
1150                                         }
1151                                         value = mapFn ? call.call(mapFn, thisArg, value, j) : value;
1152                                         if (Context) {
1153                                                 desc.value = value;
1154                                                 defineProperty$4(arr, j, desc);
1155                                         } else {
1156                                                 arr[j] = value;
1157                                         }
1158                                         ++j;
1159                                 }
1160                                 length = j;
1161                         }
1162                 }
1163                 if (length === undefined) {
1164                         // Source: array or array-like
1165                         length = toPosInteger(arrayLike.length);
1166                         if (Context) arr = new Context(length);
1167                         for (i = 0; i < length; ++i) {
1168                                 value = mapFn ? call.call(mapFn, thisArg, arrayLike[i], i) : arrayLike[i];
1169                                 if (Context) {
1170                                         desc.value = value;
1171                                         defineProperty$4(arr, i, desc);
1172                                 } else {
1173                                         arr[i] = value;
1174                                 }
1175                         }
1176                 }
1177                 if (Context) {
1178                         desc.value = null;
1179                         arr.length = length;
1180                 }
1181                 return arr;
1182         };
1183
1184         var from_1 = isImplemented$a() ? Array.from : shim$7;
1185
1186         var copy = function (obj/*, propertyNames, options*/) {
1187                 var copy = Object(validValue(obj)), propertyNames = arguments[1], options = Object(arguments[2]);
1188                 if (copy !== obj && !propertyNames) return copy;
1189                 var result = {};
1190                 if (propertyNames) {
1191                         from_1(propertyNames, function (propertyName) {
1192                                 if (options.ensure || propertyName in obj) result[propertyName] = obj[propertyName];
1193                         });
1194                 } else {
1195                         assign(result, obj);
1196                 }
1197                 return result;
1198         };
1199
1200         var bind                    = Function.prototype.bind
1201           , call$1                    = Function.prototype.call
1202           , keys$2                    = Object.keys
1203           , objPropertyIsEnumerable = Object.prototype.propertyIsEnumerable;
1204
1205         var _iterate = function (method, defVal) {
1206                 return function (obj, cb/*, thisArg, compareFn*/) {
1207                         var list, thisArg = arguments[2], compareFn = arguments[3];
1208                         obj = Object(validValue(obj));
1209                         validCallable(cb);
1210
1211                         list = keys$2(obj);
1212                         if (compareFn) {
1213                                 list.sort(typeof compareFn === "function" ? bind.call(compareFn, obj) : undefined);
1214                         }
1215                         if (typeof method !== "function") method = list[method];
1216                         return call$1.call(method, list, function (key, index) {
1217                                 if (!objPropertyIsEnumerable.call(obj, key)) return defVal;
1218                                 return call$1.call(cb, thisArg, obj[key], key, obj, index);
1219                         });
1220                 };
1221         };
1222
1223         var forEach$1 = _iterate("forEach");
1224
1225         var call$2     = Function.prototype.call;
1226
1227         var map$1 = function (obj, cb/*, thisArg*/) {
1228                 var result = {}, thisArg = arguments[2];
1229                 validCallable(cb);
1230                 forEach$1(obj, function (value, key, targetObj, index) {
1231                         result[key] = call$2.call(cb, thisArg, value, key, targetObj, index);
1232                 });
1233                 return result;
1234         };
1235
1236         var bind$1 = Function.prototype.bind
1237           , defineProperty$5 = Object.defineProperty
1238           , hasOwnProperty$1 = Object.prototype.hasOwnProperty
1239           , define;
1240
1241         define = function (name, desc, options) {
1242                 var value = ensure(desc) && ensure$1(desc.value), dgs;
1243                 dgs = copy(desc);
1244                 delete dgs.writable;
1245                 delete dgs.value;
1246                 dgs.get = function () {
1247                         if (!options.overwriteDefinition && hasOwnProperty$1.call(this, name)) return value;
1248                         desc.value = bind$1.call(value, options.resolveContext ? options.resolveContext(this) : this);
1249                         defineProperty$5(this, name, desc);
1250                         return this[name];
1251                 };
1252                 return dgs;
1253         };
1254
1255         var autoBind = function (props/*, options*/) {
1256                 var options = normalizeOptions(arguments[1]);
1257                 if (is(options.resolveContext)) ensure$1(options.resolveContext);
1258                 return map$1(props, function (desc, name) { return define(name, desc, options); });
1259         };
1260
1261         var defineProperty$6 = Object.defineProperty, defineProperties$2 = Object.defineProperties, Iterator;
1262
1263         var es6Iterator = Iterator = function (list, context) {
1264                 if (!(this instanceof Iterator)) throw new TypeError("Constructor requires 'new'");
1265                 defineProperties$2(this, {
1266                         __list__: d_1("w", validValue(list)),
1267                         __context__: d_1("w", context),
1268                         __nextIndex__: d_1("w", 0)
1269                 });
1270                 if (!context) return;
1271                 validCallable(context.on);
1272                 context.on("_add", this._onAdd);
1273                 context.on("_delete", this._onDelete);
1274                 context.on("_clear", this._onClear);
1275         };
1276
1277         // Internal %IteratorPrototype% doesn't expose its constructor
1278         delete Iterator.prototype.constructor;
1279
1280         defineProperties$2(
1281                 Iterator.prototype,
1282                 assign(
1283                         {
1284                                 _next: d_1(function () {
1285                                         var i;
1286                                         if (!this.__list__) return undefined;
1287                                         if (this.__redo__) {
1288                                                 i = this.__redo__.shift();
1289                                                 if (i !== undefined) return i;
1290                                         }
1291                                         if (this.__nextIndex__ < this.__list__.length) return this.__nextIndex__++;
1292                                         this._unBind();
1293                                         return undefined;
1294                                 }),
1295                                 next: d_1(function () {
1296                                         return this._createResult(this._next());
1297                                 }),
1298                                 _createResult: d_1(function (i) {
1299                                         if (i === undefined) return { done: true, value: undefined };
1300                                         return { done: false, value: this._resolve(i) };
1301                                 }),
1302                                 _resolve: d_1(function (i) {
1303                                         return this.__list__[i];
1304                                 }),
1305                                 _unBind: d_1(function () {
1306                                         this.__list__ = null;
1307                                         delete this.__redo__;
1308                                         if (!this.__context__) return;
1309                                         this.__context__.off("_add", this._onAdd);
1310                                         this.__context__.off("_delete", this._onDelete);
1311                                         this.__context__.off("_clear", this._onClear);
1312                                         this.__context__ = null;
1313                                 }),
1314                                 toString: d_1(function () {
1315                                         return "[object " + (this[es6Symbol$1.toStringTag] || "Object") + "]";
1316                                 })
1317                         },
1318                         autoBind({
1319                                 _onAdd: d_1(function (index) {
1320                                         if (index >= this.__nextIndex__) return;
1321                                         ++this.__nextIndex__;
1322                                         if (!this.__redo__) {
1323                                                 defineProperty$6(this, "__redo__", d_1("c", [index]));
1324                                                 return;
1325                                         }
1326                                         this.__redo__.forEach(function (redo, i) {
1327                                                 if (redo >= index) this.__redo__[i] = ++redo;
1328                                         }, this);
1329                                         this.__redo__.push(index);
1330                                 }),
1331                                 _onDelete: d_1(function (index) {
1332                                         var i;
1333                                         if (index >= this.__nextIndex__) return;
1334                                         --this.__nextIndex__;
1335                                         if (!this.__redo__) return;
1336                                         i = this.__redo__.indexOf(index);
1337                                         if (i !== -1) this.__redo__.splice(i, 1);
1338                                         this.__redo__.forEach(function (redo, j) {
1339                                                 if (redo > index) this.__redo__[j] = --redo;
1340                                         }, this);
1341                                 }),
1342                                 _onClear: d_1(function () {
1343                                         if (this.__redo__) clear.call(this.__redo__);
1344                                         this.__nextIndex__ = 0;
1345                                 })
1346                         })
1347                 )
1348         );
1349
1350         defineProperty$6(
1351                 Iterator.prototype,
1352                 es6Symbol$1.iterator,
1353                 d_1(function () {
1354                         return this;
1355                 })
1356         );
1357
1358         var array = createCommonjsModule(function (module) {
1359
1360
1361
1362         var defineProperty = Object.defineProperty, ArrayIterator;
1363
1364         ArrayIterator = module.exports = function (arr, kind) {
1365                 if (!(this instanceof ArrayIterator)) throw new TypeError("Constructor requires 'new'");
1366                 es6Iterator.call(this, arr);
1367                 if (!kind) kind = "value";
1368                 else if (contains.call(kind, "key+value")) kind = "key+value";
1369                 else if (contains.call(kind, "key")) kind = "key";
1370                 else kind = "value";
1371                 defineProperty(this, "__kind__", d_1("", kind));
1372         };
1373         if (setPrototypeOf) setPrototypeOf(ArrayIterator, es6Iterator);
1374
1375         // Internal %ArrayIteratorPrototype% doesn't expose its constructor
1376         delete ArrayIterator.prototype.constructor;
1377
1378         ArrayIterator.prototype = Object.create(es6Iterator.prototype, {
1379                 _resolve: d_1(function (i) {
1380                         if (this.__kind__ === "value") return this.__list__[i];
1381                         if (this.__kind__ === "key+value") return [i, this.__list__[i]];
1382                         return i;
1383                 })
1384         });
1385         defineProperty(ArrayIterator.prototype, es6Symbol$1.toStringTag, d_1("c", "Array Iterator"));
1386         });
1387
1388         var string = createCommonjsModule(function (module) {
1389
1390
1391
1392         var defineProperty = Object.defineProperty, StringIterator;
1393
1394         StringIterator = module.exports = function (str) {
1395                 if (!(this instanceof StringIterator)) throw new TypeError("Constructor requires 'new'");
1396                 str = String(str);
1397                 es6Iterator.call(this, str);
1398                 defineProperty(this, "__length__", d_1("", str.length));
1399         };
1400         if (setPrototypeOf) setPrototypeOf(StringIterator, es6Iterator);
1401
1402         // Internal %ArrayIteratorPrototype% doesn't expose its constructor
1403         delete StringIterator.prototype.constructor;
1404
1405         StringIterator.prototype = Object.create(es6Iterator.prototype, {
1406                 _next: d_1(function () {
1407                         if (!this.__list__) return undefined;
1408                         if (this.__nextIndex__ < this.__length__) return this.__nextIndex__++;
1409                         this._unBind();
1410                         return undefined;
1411                 }),
1412                 _resolve: d_1(function (i) {
1413                         var char = this.__list__[i], code;
1414                         if (this.__nextIndex__ === this.__length__) return char;
1415                         code = char.charCodeAt(0);
1416                         if (code >= 0xd800 && code <= 0xdbff) return char + this.__list__[this.__nextIndex__++];
1417                         return char;
1418                 })
1419         });
1420         defineProperty(StringIterator.prototype, es6Symbol$1.toStringTag, d_1("c", "String Iterator"));
1421         });
1422
1423         var iteratorSymbol$2 = es6Symbol$1.iterator;
1424
1425         var get = function (obj) {
1426                 if (typeof validIterable(obj)[iteratorSymbol$2] === "function") return obj[iteratorSymbol$2]();
1427                 if (isArguments(obj)) return new array(obj);
1428                 if (isString(obj)) return new string(obj);
1429                 return new array(obj);
1430         };
1431
1432         var isArray$2 = Array.isArray, call$3 = Function.prototype.call, some = Array.prototype.some;
1433
1434         var forOf = function (iterable, cb /*, thisArg*/) {
1435                 var mode, thisArg = arguments[2], result, doBreak, broken, i, length, char, code;
1436                 if (isArray$2(iterable) || isArguments(iterable)) mode = "array";
1437                 else if (isString(iterable)) mode = "string";
1438                 else iterable = get(iterable);
1439
1440                 validCallable(cb);
1441                 doBreak = function () {
1442                         broken = true;
1443                 };
1444                 if (mode === "array") {
1445                         some.call(iterable, function (value) {
1446                                 call$3.call(cb, thisArg, value, doBreak);
1447                                 return broken;
1448                         });
1449                         return;
1450                 }
1451                 if (mode === "string") {
1452                         length = iterable.length;
1453                         for (i = 0; i < length; ++i) {
1454                                 char = iterable[i];
1455                                 if (i + 1 < length) {
1456                                         code = char.charCodeAt(0);
1457                                         if (code >= 0xd800 && code <= 0xdbff) char += iterable[++i];
1458                                 }
1459                                 call$3.call(cb, thisArg, char, doBreak);
1460                                 if (broken) break;
1461                         }
1462                         return;
1463                 }
1464                 result = iterable.next();
1465
1466                 while (!result.done) {
1467                         call$3.call(cb, thisArg, result.value, doBreak);
1468                         if (broken) return;
1469                         result = iterable.next();
1470                 }
1471         };
1472
1473         var iterator = createCommonjsModule(function (module) {
1474
1475         var toStringTagSymbol = es6Symbol.toStringTag
1476
1477           , defineProperty = Object.defineProperty
1478           , SetIterator;
1479
1480         SetIterator = module.exports = function (set, kind) {
1481                 if (!(this instanceof SetIterator)) return new SetIterator(set, kind);
1482                 es6Iterator.call(this, set.__setData__, set);
1483                 if (!kind) kind = 'value';
1484                 else if (contains.call(kind, 'key+value')) kind = 'key+value';
1485                 else kind = 'value';
1486                 defineProperty(this, '__kind__', d_1('', kind));
1487         };
1488         if (setPrototypeOf) setPrototypeOf(SetIterator, es6Iterator);
1489
1490         SetIterator.prototype = Object.create(es6Iterator.prototype, {
1491                 constructor: d_1(SetIterator),
1492                 _resolve: d_1(function (i) {
1493                         if (this.__kind__ === 'value') return this.__list__[i];
1494                         return [this.__list__[i], this.__list__[i]];
1495                 }),
1496                 toString: d_1(function () { return '[object Set Iterator]'; })
1497         });
1498         defineProperty(SetIterator.prototype, toStringTagSymbol, d_1('c', 'Set Iterator'));
1499         });
1500
1501         // Exports true if environment provides native `Set` implementation,
1502
1503         var isNativeImplemented = (function () {
1504                 if (typeof Set === 'undefined') return false;
1505                 return (Object.prototype.toString.call(Set.prototype) === '[object Set]');
1506         }());
1507
1508         var iterator$1       = validIterable
1509
1510           , call$4 = Function.prototype.call
1511           , defineProperty$7 = Object.defineProperty, getPrototypeOf$1 = Object.getPrototypeOf
1512           , SetPoly, getValues, NativeSet;
1513
1514         if (isNativeImplemented) NativeSet = Set;
1515
1516         var polyfill$2 = SetPoly = function Set(/*iterable*/) {
1517                 var iterable = arguments[0], self;
1518                 if (!(this instanceof SetPoly)) throw new TypeError('Constructor requires \'new\'');
1519                 if (isNativeImplemented && setPrototypeOf) self = setPrototypeOf(new NativeSet(), getPrototypeOf$1(this));
1520                 else self = this;
1521                 if (iterable != null) iterator$1(iterable);
1522                 defineProperty$7(self, '__setData__', d_1('c', []));
1523                 if (!iterable) return self;
1524                 forOf(iterable, function (value) {
1525                         if (eIndexOf.call(this, value) !== -1) return;
1526                         this.push(value);
1527                 }, self.__setData__);
1528                 return self;
1529         };
1530
1531         if (isNativeImplemented) {
1532                 if (setPrototypeOf) setPrototypeOf(SetPoly, NativeSet);
1533                 SetPoly.prototype = Object.create(NativeSet.prototype, { constructor: d_1(SetPoly) });
1534         }
1535
1536         eventEmitter(Object.defineProperties(SetPoly.prototype, {
1537                 add: d_1(function (value) {
1538                         if (this.has(value)) return this;
1539                         this.emit('_add', this.__setData__.push(value) - 1, value);
1540                         return this;
1541                 }),
1542                 clear: d_1(function () {
1543                         if (!this.__setData__.length) return;
1544                         clear.call(this.__setData__);
1545                         this.emit('_clear');
1546                 }),
1547                 delete: d_1(function (value) {
1548                         var index = eIndexOf.call(this.__setData__, value);
1549                         if (index === -1) return false;
1550                         this.__setData__.splice(index, 1);
1551                         this.emit('_delete', index, value);
1552                         return true;
1553                 }),
1554                 entries: d_1(function () { return new iterator(this, 'key+value'); }),
1555                 forEach: d_1(function (cb/*, thisArg*/) {
1556                         var thisArg = arguments[1], iterator, result, value;
1557                         validCallable(cb);
1558                         iterator = this.values();
1559                         result = iterator._next();
1560                         while (result !== undefined) {
1561                                 value = iterator._resolve(result);
1562                                 call$4.call(cb, thisArg, value, value, this);
1563                                 result = iterator._next();
1564                         }
1565                 }),
1566                 has: d_1(function (value) {
1567                         return (eIndexOf.call(this.__setData__, value) !== -1);
1568                 }),
1569                 keys: d_1(getValues = function () { return this.values(); }),
1570                 size: d_1.gs(function () { return this.__setData__.length; }),
1571                 values: d_1(function () { return new iterator(this); }),
1572                 toString: d_1(function () { return '[object Set]'; })
1573         }));
1574         defineProperty$7(SetPoly.prototype, es6Symbol.iterator, d_1(getValues));
1575         defineProperty$7(SetPoly.prototype, es6Symbol.toStringTag, d_1('c', 'Set'));
1576
1577         var es6Set = isImplemented() ? Set : polyfill$2;
1578
1579         var isImplemented$b = function () {
1580                 var map, iterator, result;
1581                 if (typeof Map !== 'function') return false;
1582                 try {
1583                         // WebKit doesn't support arguments and crashes
1584                         map = new Map([['raz', 'one'], ['dwa', 'two'], ['trzy', 'three']]);
1585                 } catch (e) {
1586                         return false;
1587                 }
1588                 if (String(map) !== '[object Map]') return false;
1589                 if (map.size !== 3) return false;
1590                 if (typeof map.clear !== 'function') return false;
1591                 if (typeof map.delete !== 'function') return false;
1592                 if (typeof map.entries !== 'function') return false;
1593                 if (typeof map.forEach !== 'function') return false;
1594                 if (typeof map.get !== 'function') return false;
1595                 if (typeof map.has !== 'function') return false;
1596                 if (typeof map.keys !== 'function') return false;
1597                 if (typeof map.set !== 'function') return false;
1598                 if (typeof map.values !== 'function') return false;
1599
1600                 iterator = map.entries();
1601                 result = iterator.next();
1602                 if (result.done !== false) return false;
1603                 if (!result.value) return false;
1604                 if (result.value[0] !== 'raz') return false;
1605                 if (result.value[1] !== 'one') return false;
1606
1607                 return true;
1608         };
1609
1610         var forEach$2 = Array.prototype.forEach, create$6 = Object.create;
1611
1612         // eslint-disable-next-line no-unused-vars
1613         var primitiveSet = function (arg/*, …args*/) {
1614                 var set = create$6(null);
1615                 forEach$2.call(arguments, function (name) { set[name] = true; });
1616                 return set;
1617         };
1618
1619         var iteratorKinds = primitiveSet('key',
1620                 'value', 'key+value');
1621
1622         var iterator$2 = createCommonjsModule(function (module) {
1623
1624         var toStringTagSymbol = es6Symbol$1.toStringTag
1625
1626           , defineProperties = Object.defineProperties
1627           , unBind = es6Iterator.prototype._unBind
1628           , MapIterator;
1629
1630         MapIterator = module.exports = function (map, kind) {
1631                 if (!(this instanceof MapIterator)) return new MapIterator(map, kind);
1632                 es6Iterator.call(this, map.__mapKeysData__, map);
1633                 if (!kind || !iteratorKinds[kind]) kind = 'key+value';
1634                 defineProperties(this, {
1635                         __kind__: d_1('', kind),
1636                         __values__: d_1('w', map.__mapValuesData__)
1637                 });
1638         };
1639         if (setPrototypeOf) setPrototypeOf(MapIterator, es6Iterator);
1640
1641         MapIterator.prototype = Object.create(es6Iterator.prototype, {
1642                 constructor: d_1(MapIterator),
1643                 _resolve: d_1(function (i) {
1644                         if (this.__kind__ === 'value') return this.__values__[i];
1645                         if (this.__kind__ === 'key') return this.__list__[i];
1646                         return [this.__list__[i], this.__values__[i]];
1647                 }),
1648                 _unBind: d_1(function () {
1649                         this.__values__ = null;
1650                         unBind.call(this);
1651                 }),
1652                 toString: d_1(function () { return '[object Map Iterator]'; })
1653         });
1654         Object.defineProperty(MapIterator.prototype, toStringTagSymbol,
1655                 d_1('c', 'Map Iterator'));
1656         });
1657
1658         // Exports true if environment provides native `Map` implementation,
1659
1660         var isNativeImplemented$1 = (function () {
1661                 if (typeof Map === 'undefined') return false;
1662                 return (Object.prototype.toString.call(new Map()) === '[object Map]');
1663         }());
1664
1665         var iterator$3       = validIterable
1666
1667           , call$5 = Function.prototype.call
1668           , defineProperties$3 = Object.defineProperties, getPrototypeOf$2 = Object.getPrototypeOf
1669           , MapPoly;
1670
1671         var polyfill$3 = MapPoly = function (/*iterable*/) {
1672                 var iterable = arguments[0], keys, values, self;
1673                 if (!(this instanceof MapPoly)) throw new TypeError('Constructor requires \'new\'');
1674                 if (isNativeImplemented$1 && setPrototypeOf && (Map !== MapPoly)) {
1675                         self = setPrototypeOf(new Map(), getPrototypeOf$2(this));
1676                 } else {
1677                         self = this;
1678                 }
1679                 if (iterable != null) iterator$3(iterable);
1680                 defineProperties$3(self, {
1681                         __mapKeysData__: d_1('c', keys = []),
1682                         __mapValuesData__: d_1('c', values = [])
1683                 });
1684                 if (!iterable) return self;
1685                 forOf(iterable, function (value) {
1686                         var key = validValue(value)[0];
1687                         value = value[1];
1688                         if (eIndexOf.call(keys, key) !== -1) return;
1689                         keys.push(key);
1690                         values.push(value);
1691                 }, self);
1692                 return self;
1693         };
1694
1695         if (isNativeImplemented$1) {
1696                 if (setPrototypeOf) setPrototypeOf(MapPoly, Map);
1697                 MapPoly.prototype = Object.create(Map.prototype, {
1698                         constructor: d_1(MapPoly)
1699                 });
1700         }
1701
1702         eventEmitter(defineProperties$3(MapPoly.prototype, {
1703                 clear: d_1(function () {
1704                         if (!this.__mapKeysData__.length) return;
1705                         clear.call(this.__mapKeysData__);
1706                         clear.call(this.__mapValuesData__);
1707                         this.emit('_clear');
1708                 }),
1709                 delete: d_1(function (key) {
1710                         var index = eIndexOf.call(this.__mapKeysData__, key);
1711                         if (index === -1) return false;
1712                         this.__mapKeysData__.splice(index, 1);
1713                         this.__mapValuesData__.splice(index, 1);
1714                         this.emit('_delete', index, key);
1715                         return true;
1716                 }),
1717                 entries: d_1(function () { return new iterator$2(this, 'key+value'); }),
1718                 forEach: d_1(function (cb/*, thisArg*/) {
1719                         var thisArg = arguments[1], iterator, result;
1720                         validCallable(cb);
1721                         iterator = this.entries();
1722                         result = iterator._next();
1723                         while (result !== undefined) {
1724                                 call$5.call(cb, thisArg, this.__mapValuesData__[result],
1725                                         this.__mapKeysData__[result], this);
1726                                 result = iterator._next();
1727                         }
1728                 }),
1729                 get: d_1(function (key) {
1730                         var index = eIndexOf.call(this.__mapKeysData__, key);
1731                         if (index === -1) return;
1732                         return this.__mapValuesData__[index];
1733                 }),
1734                 has: d_1(function (key) {
1735                         return (eIndexOf.call(this.__mapKeysData__, key) !== -1);
1736                 }),
1737                 keys: d_1(function () { return new iterator$2(this, 'key'); }),
1738                 set: d_1(function (key, value) {
1739                         var index = eIndexOf.call(this.__mapKeysData__, key), emit;
1740                         if (index === -1) {
1741                                 index = this.__mapKeysData__.push(key) - 1;
1742                                 emit = true;
1743                         }
1744                         this.__mapValuesData__[index] = value;
1745                         if (emit) this.emit('_add', index, key);
1746                         return this;
1747                 }),
1748                 size: d_1.gs(function () { return this.__mapKeysData__.length; }),
1749                 values: d_1(function () { return new iterator$2(this, 'value'); }),
1750                 toString: d_1(function () { return '[object Map]'; })
1751         }));
1752         Object.defineProperty(MapPoly.prototype, es6Symbol$1.iterator, d_1(function () {
1753                 return this.entries();
1754         }));
1755         Object.defineProperty(MapPoly.prototype, es6Symbol$1.toStringTag, d_1('c', 'Map'));
1756
1757         var es6Map = isImplemented$b() ? Map : polyfill$3;
1758
1759         var toStr = Object.prototype.toString;
1760
1761         var isArguments$1 = function isArguments(value) {
1762                 var str = toStr.call(value);
1763                 var isArgs = str === '[object Arguments]';
1764                 if (!isArgs) {
1765                         isArgs = str !== '[object Array]' &&
1766                                 value !== null &&
1767                                 typeof value === 'object' &&
1768                                 typeof value.length === 'number' &&
1769                                 value.length >= 0 &&
1770                                 toStr.call(value.callee) === '[object Function]';
1771                 }
1772                 return isArgs;
1773         };
1774
1775         var keysShim;
1776         if (!Object.keys) {
1777                 // modified from https://github.com/es-shims/es5-shim
1778                 var has = Object.prototype.hasOwnProperty;
1779                 var toStr$1 = Object.prototype.toString;
1780                 var isArgs = isArguments$1; // eslint-disable-line global-require
1781                 var isEnumerable = Object.prototype.propertyIsEnumerable;
1782                 var hasDontEnumBug = !isEnumerable.call({ toString: null }, 'toString');
1783                 var hasProtoEnumBug = isEnumerable.call(function () {}, 'prototype');
1784                 var dontEnums = [
1785                         'toString',
1786                         'toLocaleString',
1787                         'valueOf',
1788                         'hasOwnProperty',
1789                         'isPrototypeOf',
1790                         'propertyIsEnumerable',
1791                         'constructor'
1792                 ];
1793                 var equalsConstructorPrototype = function (o) {
1794                         var ctor = o.constructor;
1795                         return ctor && ctor.prototype === o;
1796                 };
1797                 var excludedKeys = {
1798                         $applicationCache: true,
1799                         $console: true,
1800                         $external: true,
1801                         $frame: true,
1802                         $frameElement: true,
1803                         $frames: true,
1804                         $innerHeight: true,
1805                         $innerWidth: true,
1806                         $onmozfullscreenchange: true,
1807                         $onmozfullscreenerror: true,
1808                         $outerHeight: true,
1809                         $outerWidth: true,
1810                         $pageXOffset: true,
1811                         $pageYOffset: true,
1812                         $parent: true,
1813                         $scrollLeft: true,
1814                         $scrollTop: true,
1815                         $scrollX: true,
1816                         $scrollY: true,
1817                         $self: true,
1818                         $webkitIndexedDB: true,
1819                         $webkitStorageInfo: true,
1820                         $window: true
1821                 };
1822                 var hasAutomationEqualityBug = (function () {
1823                         /* global window */
1824                         if (typeof window === 'undefined') { return false; }
1825                         for (var k in window) {
1826                                 try {
1827                                         if (!excludedKeys['$' + k] && has.call(window, k) && window[k] !== null && typeof window[k] === 'object') {
1828                                                 try {
1829                                                         equalsConstructorPrototype(window[k]);
1830                                                 } catch (e) {
1831                                                         return true;
1832                                                 }
1833                                         }
1834                                 } catch (e) {
1835                                         return true;
1836                                 }
1837                         }
1838                         return false;
1839                 }());
1840                 var equalsConstructorPrototypeIfNotBuggy = function (o) {
1841                         /* global window */
1842                         if (typeof window === 'undefined' || !hasAutomationEqualityBug) {
1843                                 return equalsConstructorPrototype(o);
1844                         }
1845                         try {
1846                                 return equalsConstructorPrototype(o);
1847                         } catch (e) {
1848                                 return false;
1849                         }
1850                 };
1851
1852                 keysShim = function keys(object) {
1853                         var isObject = object !== null && typeof object === 'object';
1854                         var isFunction = toStr$1.call(object) === '[object Function]';
1855                         var isArguments = isArgs(object);
1856                         var isString = isObject && toStr$1.call(object) === '[object String]';
1857                         var theKeys = [];
1858
1859                         if (!isObject && !isFunction && !isArguments) {
1860                                 throw new TypeError('Object.keys called on a non-object');
1861                         }
1862
1863                         var skipProto = hasProtoEnumBug && isFunction;
1864                         if (isString && object.length > 0 && !has.call(object, 0)) {
1865                                 for (var i = 0; i < object.length; ++i) {
1866                                         theKeys.push(String(i));
1867                                 }
1868                         }
1869
1870                         if (isArguments && object.length > 0) {
1871                                 for (var j = 0; j < object.length; ++j) {
1872                                         theKeys.push(String(j));
1873                                 }
1874                         } else {
1875                                 for (var name in object) {
1876                                         if (!(skipProto && name === 'prototype') && has.call(object, name)) {
1877                                                 theKeys.push(String(name));
1878                                         }
1879                                 }
1880                         }
1881
1882                         if (hasDontEnumBug) {
1883                                 var skipConstructor = equalsConstructorPrototypeIfNotBuggy(object);
1884
1885                                 for (var k = 0; k < dontEnums.length; ++k) {
1886                                         if (!(skipConstructor && dontEnums[k] === 'constructor') && has.call(object, dontEnums[k])) {
1887                                                 theKeys.push(dontEnums[k]);
1888                                         }
1889                                 }
1890                         }
1891                         return theKeys;
1892                 };
1893         }
1894         var implementation$1 = keysShim;
1895
1896         var slice = Array.prototype.slice;
1897
1898
1899         var origKeys = Object.keys;
1900         var keysShim$1 = origKeys ? function keys(o) { return origKeys(o); } : implementation$1;
1901
1902         var originalKeys = Object.keys;
1903
1904         keysShim$1.shim = function shimObjectKeys() {
1905                 if (Object.keys) {
1906                         var keysWorksWithArguments = (function () {
1907                                 // Safari 5.0 bug
1908                                 var args = Object.keys(arguments);
1909                                 return args && args.length === arguments.length;
1910                         }(1, 2));
1911                         if (!keysWorksWithArguments) {
1912                                 Object.keys = function keys(object) { // eslint-disable-line func-name-matching
1913                                         if (isArguments$1(object)) {
1914                                                 return originalKeys(slice.call(object));
1915                                         }
1916                                         return originalKeys(object);
1917                                 };
1918                         }
1919                 } else {
1920                         Object.keys = keysShim$1;
1921                 }
1922                 return Object.keys || keysShim$1;
1923         };
1924
1925         var objectKeys = keysShim$1;
1926
1927         var hasSymbols = typeof Symbol === 'function' && typeof Symbol('foo') === 'symbol';
1928
1929         var toStr$2 = Object.prototype.toString;
1930         var concat = Array.prototype.concat;
1931         var origDefineProperty = Object.defineProperty;
1932
1933         var isFunction$1 = function (fn) {
1934                 return typeof fn === 'function' && toStr$2.call(fn) === '[object Function]';
1935         };
1936
1937         var arePropertyDescriptorsSupported = function () {
1938                 var obj = {};
1939                 try {
1940                         origDefineProperty(obj, 'x', { enumerable: false, value: obj });
1941                         // eslint-disable-next-line no-unused-vars, no-restricted-syntax
1942                         for (var _ in obj) { // jscs:ignore disallowUnusedVariables
1943                                 return false;
1944                         }
1945                         return obj.x === obj;
1946                 } catch (e) { /* this is IE 8. */
1947                         return false;
1948                 }
1949         };
1950         var supportsDescriptors = origDefineProperty && arePropertyDescriptorsSupported();
1951
1952         var defineProperty$8 = function (object, name, value, predicate) {
1953                 if (name in object && (!isFunction$1(predicate) || !predicate())) {
1954                         return;
1955                 }
1956                 if (supportsDescriptors) {
1957                         origDefineProperty(object, name, {
1958                                 configurable: true,
1959                                 enumerable: false,
1960                                 value: value,
1961                                 writable: true
1962                         });
1963                 } else {
1964                         object[name] = value;
1965                 }
1966         };
1967
1968         var defineProperties$4 = function (object, map) {
1969                 var predicates = arguments.length > 2 ? arguments[2] : {};
1970                 var props = objectKeys(map);
1971                 if (hasSymbols) {
1972                         props = concat.call(props, Object.getOwnPropertySymbols(map));
1973                 }
1974                 for (var i = 0; i < props.length; i += 1) {
1975                         defineProperty$8(object, props[i], map[props[i]], predicates[props[i]]);
1976                 }
1977         };
1978
1979         defineProperties$4.supportsDescriptors = !!supportsDescriptors;
1980
1981         var defineProperties_1 = defineProperties$4;
1982
1983         /* eslint complexity: [2, 18], max-statements: [2, 33] */
1984         var shams = function hasSymbols() {
1985                 if (typeof Symbol !== 'function' || typeof Object.getOwnPropertySymbols !== 'function') { return false; }
1986                 if (typeof Symbol.iterator === 'symbol') { return true; }
1987
1988                 var obj = {};
1989                 var sym = Symbol('test');
1990                 var symObj = Object(sym);
1991                 if (typeof sym === 'string') { return false; }
1992
1993                 if (Object.prototype.toString.call(sym) !== '[object Symbol]') { return false; }
1994                 if (Object.prototype.toString.call(symObj) !== '[object Symbol]') { return false; }
1995
1996                 // temp disabled per https://github.com/ljharb/object.assign/issues/17
1997                 // if (sym instanceof Symbol) { return false; }
1998                 // temp disabled per https://github.com/WebReflection/get-own-property-symbols/issues/4
1999                 // if (!(symObj instanceof Symbol)) { return false; }
2000
2001                 // if (typeof Symbol.prototype.toString !== 'function') { return false; }
2002                 // if (String(sym) !== Symbol.prototype.toString.call(sym)) { return false; }
2003
2004                 var symVal = 42;
2005                 obj[sym] = symVal;
2006                 for (sym in obj) { return false; } // eslint-disable-line no-restricted-syntax
2007                 if (typeof Object.keys === 'function' && Object.keys(obj).length !== 0) { return false; }
2008
2009                 if (typeof Object.getOwnPropertyNames === 'function' && Object.getOwnPropertyNames(obj).length !== 0) { return false; }
2010
2011                 var syms = Object.getOwnPropertySymbols(obj);
2012                 if (syms.length !== 1 || syms[0] !== sym) { return false; }
2013
2014                 if (!Object.prototype.propertyIsEnumerable.call(obj, sym)) { return false; }
2015
2016                 if (typeof Object.getOwnPropertyDescriptor === 'function') {
2017                         var descriptor = Object.getOwnPropertyDescriptor(obj, sym);
2018                         if (descriptor.value !== symVal || descriptor.enumerable !== true) { return false; }
2019                 }
2020
2021                 return true;
2022         };
2023
2024         var origSymbol = commonjsGlobal.Symbol;
2025
2026
2027         var hasSymbols$1 = function hasNativeSymbols() {
2028                 if (typeof origSymbol !== 'function') { return false; }
2029                 if (typeof Symbol !== 'function') { return false; }
2030                 if (typeof origSymbol('foo') !== 'symbol') { return false; }
2031                 if (typeof Symbol('bar') !== 'symbol') { return false; }
2032
2033                 return shams();
2034         };
2035
2036         /* eslint no-invalid-this: 1 */
2037
2038         var ERROR_MESSAGE = 'Function.prototype.bind called on incompatible ';
2039         var slice$1 = Array.prototype.slice;
2040         var toStr$3 = Object.prototype.toString;
2041         var funcType = '[object Function]';
2042
2043         var implementation$2 = function bind(that) {
2044             var target = this;
2045             if (typeof target !== 'function' || toStr$3.call(target) !== funcType) {
2046                 throw new TypeError(ERROR_MESSAGE + target);
2047             }
2048             var args = slice$1.call(arguments, 1);
2049
2050             var bound;
2051             var binder = function () {
2052                 if (this instanceof bound) {
2053                     var result = target.apply(
2054                         this,
2055                         args.concat(slice$1.call(arguments))
2056                     );
2057                     if (Object(result) === result) {
2058                         return result;
2059                     }
2060                     return this;
2061                 } else {
2062                     return target.apply(
2063                         that,
2064                         args.concat(slice$1.call(arguments))
2065                     );
2066                 }
2067             };
2068
2069             var boundLength = Math.max(0, target.length - args.length);
2070             var boundArgs = [];
2071             for (var i = 0; i < boundLength; i++) {
2072                 boundArgs.push('$' + i);
2073             }
2074
2075             bound = Function('binder', 'return function (' + boundArgs.join(',') + '){ return binder.apply(this,arguments); }')(binder);
2076
2077             if (target.prototype) {
2078                 var Empty = function Empty() {};
2079                 Empty.prototype = target.prototype;
2080                 bound.prototype = new Empty();
2081                 Empty.prototype = null;
2082             }
2083
2084             return bound;
2085         };
2086
2087         var functionBind = Function.prototype.bind || implementation$2;
2088
2089         /* globals
2090                 Atomics,
2091                 SharedArrayBuffer,
2092         */
2093
2094         var undefined$1;
2095
2096         var $TypeError = TypeError;
2097
2098         var $gOPD = Object.getOwnPropertyDescriptor;
2099         if ($gOPD) {
2100                 try {
2101                         $gOPD({}, '');
2102                 } catch (e) {
2103                         $gOPD = null; // this is IE 8, which has a broken gOPD
2104                 }
2105         }
2106
2107         var throwTypeError = function () { throw new $TypeError(); };
2108         var ThrowTypeError = $gOPD
2109                 ? (function () {
2110                         try {
2111                                 // eslint-disable-next-line no-unused-expressions, no-caller, no-restricted-properties
2112                                 arguments.callee; // IE 8 does not throw here
2113                                 return throwTypeError;
2114                         } catch (calleeThrows) {
2115                                 try {
2116                                         // IE 8 throws on Object.getOwnPropertyDescriptor(arguments, '')
2117                                         return $gOPD(arguments, 'callee').get;
2118                                 } catch (gOPDthrows) {
2119                                         return throwTypeError;
2120                                 }
2121                         }
2122                 }())
2123                 : throwTypeError;
2124
2125         var hasSymbols$2 = hasSymbols$1();
2126
2127         var getProto = Object.getPrototypeOf || function (x) { return x.__proto__; }; // eslint-disable-line no-proto
2128         var generatorFunction =  undefined$1;
2129         var asyncFunction =  undefined$1;
2130         var asyncGenFunction =  undefined$1;
2131
2132         var TypedArray = typeof Uint8Array === 'undefined' ? undefined$1 : getProto(Uint8Array);
2133
2134         var INTRINSICS = {
2135                 '%Array%': Array,
2136                 '%ArrayBuffer%': typeof ArrayBuffer === 'undefined' ? undefined$1 : ArrayBuffer,
2137                 '%ArrayBufferPrototype%': typeof ArrayBuffer === 'undefined' ? undefined$1 : ArrayBuffer.prototype,
2138                 '%ArrayIteratorPrototype%': hasSymbols$2 ? getProto([][Symbol.iterator]()) : undefined$1,
2139                 '%ArrayPrototype%': Array.prototype,
2140                 '%ArrayProto_entries%': Array.prototype.entries,
2141                 '%ArrayProto_forEach%': Array.prototype.forEach,
2142                 '%ArrayProto_keys%': Array.prototype.keys,
2143                 '%ArrayProto_values%': Array.prototype.values,
2144                 '%AsyncFromSyncIteratorPrototype%': undefined$1,
2145                 '%AsyncFunction%': asyncFunction,
2146                 '%AsyncFunctionPrototype%':  undefined$1,
2147                 '%AsyncGenerator%':  undefined$1,
2148                 '%AsyncGeneratorFunction%': asyncGenFunction,
2149                 '%AsyncGeneratorPrototype%':  undefined$1,
2150                 '%AsyncIteratorPrototype%':  undefined$1,
2151                 '%Atomics%': typeof Atomics === 'undefined' ? undefined$1 : Atomics,
2152                 '%Boolean%': Boolean,
2153                 '%BooleanPrototype%': Boolean.prototype,
2154                 '%DataView%': typeof DataView === 'undefined' ? undefined$1 : DataView,
2155                 '%DataViewPrototype%': typeof DataView === 'undefined' ? undefined$1 : DataView.prototype,
2156                 '%Date%': Date,
2157                 '%DatePrototype%': Date.prototype,
2158                 '%decodeURI%': decodeURI,
2159                 '%decodeURIComponent%': decodeURIComponent,
2160                 '%encodeURI%': encodeURI,
2161                 '%encodeURIComponent%': encodeURIComponent,
2162                 '%Error%': Error,
2163                 '%ErrorPrototype%': Error.prototype,
2164                 '%eval%': eval, // eslint-disable-line no-eval
2165                 '%EvalError%': EvalError,
2166                 '%EvalErrorPrototype%': EvalError.prototype,
2167                 '%Float32Array%': typeof Float32Array === 'undefined' ? undefined$1 : Float32Array,
2168                 '%Float32ArrayPrototype%': typeof Float32Array === 'undefined' ? undefined$1 : Float32Array.prototype,
2169                 '%Float64Array%': typeof Float64Array === 'undefined' ? undefined$1 : Float64Array,
2170                 '%Float64ArrayPrototype%': typeof Float64Array === 'undefined' ? undefined$1 : Float64Array.prototype,
2171                 '%Function%': Function,
2172                 '%FunctionPrototype%': Function.prototype,
2173                 '%Generator%':  undefined$1,
2174                 '%GeneratorFunction%': generatorFunction,
2175                 '%GeneratorPrototype%':  undefined$1,
2176                 '%Int8Array%': typeof Int8Array === 'undefined' ? undefined$1 : Int8Array,
2177                 '%Int8ArrayPrototype%': typeof Int8Array === 'undefined' ? undefined$1 : Int8Array.prototype,
2178                 '%Int16Array%': typeof Int16Array === 'undefined' ? undefined$1 : Int16Array,
2179                 '%Int16ArrayPrototype%': typeof Int16Array === 'undefined' ? undefined$1 : Int8Array.prototype,
2180                 '%Int32Array%': typeof Int32Array === 'undefined' ? undefined$1 : Int32Array,
2181                 '%Int32ArrayPrototype%': typeof Int32Array === 'undefined' ? undefined$1 : Int32Array.prototype,
2182                 '%isFinite%': isFinite,
2183                 '%isNaN%': isNaN,
2184                 '%IteratorPrototype%': hasSymbols$2 ? getProto(getProto([][Symbol.iterator]())) : undefined$1,
2185                 '%JSON%': typeof JSON === 'object' ? JSON : undefined$1,
2186                 '%JSONParse%': typeof JSON === 'object' ? JSON.parse : undefined$1,
2187                 '%Map%': typeof Map === 'undefined' ? undefined$1 : Map,
2188                 '%MapIteratorPrototype%': typeof Map === 'undefined' || !hasSymbols$2 ? undefined$1 : getProto(new Map()[Symbol.iterator]()),
2189                 '%MapPrototype%': typeof Map === 'undefined' ? undefined$1 : Map.prototype,
2190                 '%Math%': Math,
2191                 '%Number%': Number,
2192                 '%NumberPrototype%': Number.prototype,
2193                 '%Object%': Object,
2194                 '%ObjectPrototype%': Object.prototype,
2195                 '%ObjProto_toString%': Object.prototype.toString,
2196                 '%ObjProto_valueOf%': Object.prototype.valueOf,
2197                 '%parseFloat%': parseFloat,
2198                 '%parseInt%': parseInt,
2199                 '%Promise%': typeof Promise === 'undefined' ? undefined$1 : Promise,
2200                 '%PromisePrototype%': typeof Promise === 'undefined' ? undefined$1 : Promise.prototype,
2201                 '%PromiseProto_then%': typeof Promise === 'undefined' ? undefined$1 : Promise.prototype.then,
2202                 '%Promise_all%': typeof Promise === 'undefined' ? undefined$1 : Promise.all,
2203                 '%Promise_reject%': typeof Promise === 'undefined' ? undefined$1 : Promise.reject,
2204                 '%Promise_resolve%': typeof Promise === 'undefined' ? undefined$1 : Promise.resolve,
2205                 '%Proxy%': typeof Proxy === 'undefined' ? undefined$1 : Proxy,
2206                 '%RangeError%': RangeError,
2207                 '%RangeErrorPrototype%': RangeError.prototype,
2208                 '%ReferenceError%': ReferenceError,
2209                 '%ReferenceErrorPrototype%': ReferenceError.prototype,
2210                 '%Reflect%': typeof Reflect === 'undefined' ? undefined$1 : Reflect,
2211                 '%RegExp%': RegExp,
2212                 '%RegExpPrototype%': RegExp.prototype,
2213                 '%Set%': typeof Set === 'undefined' ? undefined$1 : Set,
2214                 '%SetIteratorPrototype%': typeof Set === 'undefined' || !hasSymbols$2 ? undefined$1 : getProto(new Set()[Symbol.iterator]()),
2215                 '%SetPrototype%': typeof Set === 'undefined' ? undefined$1 : Set.prototype,
2216                 '%SharedArrayBuffer%': typeof SharedArrayBuffer === 'undefined' ? undefined$1 : SharedArrayBuffer,
2217                 '%SharedArrayBufferPrototype%': typeof SharedArrayBuffer === 'undefined' ? undefined$1 : SharedArrayBuffer.prototype,
2218                 '%String%': String,
2219                 '%StringIteratorPrototype%': hasSymbols$2 ? getProto(''[Symbol.iterator]()) : undefined$1,
2220                 '%StringPrototype%': String.prototype,
2221                 '%Symbol%': hasSymbols$2 ? Symbol : undefined$1,
2222                 '%SymbolPrototype%': hasSymbols$2 ? Symbol.prototype : undefined$1,
2223                 '%SyntaxError%': SyntaxError,
2224                 '%SyntaxErrorPrototype%': SyntaxError.prototype,
2225                 '%ThrowTypeError%': ThrowTypeError,
2226                 '%TypedArray%': TypedArray,
2227                 '%TypedArrayPrototype%': TypedArray ? TypedArray.prototype : undefined$1,
2228                 '%TypeError%': $TypeError,
2229                 '%TypeErrorPrototype%': $TypeError.prototype,
2230                 '%Uint8Array%': typeof Uint8Array === 'undefined' ? undefined$1 : Uint8Array,
2231                 '%Uint8ArrayPrototype%': typeof Uint8Array === 'undefined' ? undefined$1 : Uint8Array.prototype,
2232                 '%Uint8ClampedArray%': typeof Uint8ClampedArray === 'undefined' ? undefined$1 : Uint8ClampedArray,
2233                 '%Uint8ClampedArrayPrototype%': typeof Uint8ClampedArray === 'undefined' ? undefined$1 : Uint8ClampedArray.prototype,
2234                 '%Uint16Array%': typeof Uint16Array === 'undefined' ? undefined$1 : Uint16Array,
2235                 '%Uint16ArrayPrototype%': typeof Uint16Array === 'undefined' ? undefined$1 : Uint16Array.prototype,
2236                 '%Uint32Array%': typeof Uint32Array === 'undefined' ? undefined$1 : Uint32Array,
2237                 '%Uint32ArrayPrototype%': typeof Uint32Array === 'undefined' ? undefined$1 : Uint32Array.prototype,
2238                 '%URIError%': URIError,
2239                 '%URIErrorPrototype%': URIError.prototype,
2240                 '%WeakMap%': typeof WeakMap === 'undefined' ? undefined$1 : WeakMap,
2241                 '%WeakMapPrototype%': typeof WeakMap === 'undefined' ? undefined$1 : WeakMap.prototype,
2242                 '%WeakSet%': typeof WeakSet === 'undefined' ? undefined$1 : WeakSet,
2243                 '%WeakSetPrototype%': typeof WeakSet === 'undefined' ? undefined$1 : WeakSet.prototype
2244         };
2245
2246
2247         var $replace = functionBind.call(Function.call, String.prototype.replace);
2248
2249         /* adapted from https://github.com/lodash/lodash/blob/4.17.15/dist/lodash.js#L6735-L6744 */
2250         var rePropName = /[^%.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|%$))/g;
2251         var reEscapeChar = /\\(\\)?/g; /** Used to match backslashes in property paths. */
2252         var stringToPath = function stringToPath(string) {
2253                 var result = [];
2254                 $replace(string, rePropName, function (match, number, quote, subString) {
2255                         result[result.length] = quote ? $replace(subString, reEscapeChar, '$1') : (number || match);
2256                 });
2257                 return result;
2258         };
2259         /* end adaptation */
2260
2261         var getBaseIntrinsic = function getBaseIntrinsic(name, allowMissing) {
2262                 if (!(name in INTRINSICS)) {
2263                         throw new SyntaxError('intrinsic ' + name + ' does not exist!');
2264                 }
2265
2266                 // istanbul ignore if // hopefully this is impossible to test :-)
2267                 if (typeof INTRINSICS[name] === 'undefined' && !allowMissing) {
2268                         throw new $TypeError('intrinsic ' + name + ' exists, but is not available. Please file an issue!');
2269                 }
2270
2271                 return INTRINSICS[name];
2272         };
2273
2274         var GetIntrinsic = function GetIntrinsic(name, allowMissing) {
2275                 if (typeof name !== 'string' || name.length === 0) {
2276                         throw new TypeError('intrinsic name must be a non-empty string');
2277                 }
2278                 if (arguments.length > 1 && typeof allowMissing !== 'boolean') {
2279                         throw new TypeError('"allowMissing" argument must be a boolean');
2280                 }
2281
2282                 var parts = stringToPath(name);
2283
2284                 var value = getBaseIntrinsic('%' + (parts.length > 0 ? parts[0] : '') + '%', allowMissing);
2285                 for (var i = 1; i < parts.length; i += 1) {
2286                         if (value != null) {
2287                                 if ($gOPD && (i + 1) >= parts.length) {
2288                                         var desc = $gOPD(value, parts[i]);
2289                                         if (!allowMissing && !(parts[i] in value)) {
2290                                                 throw new $TypeError('base intrinsic for ' + name + ' exists, but the property is not available.');
2291                                         }
2292                                         value = desc ? (desc.get || desc.value) : value[parts[i]];
2293                                 } else {
2294                                         value = value[parts[i]];
2295                                 }
2296                         }
2297                 }
2298                 return value;
2299         };
2300
2301         var $TypeError$1 = GetIntrinsic('%TypeError%');
2302
2303         // http://www.ecma-international.org/ecma-262/5.1/#sec-9.10
2304
2305         var CheckObjectCoercible = function CheckObjectCoercible(value, optMessage) {
2306                 if (value == null) {
2307                         throw new $TypeError$1(optMessage || ('Cannot call method on ' + value));
2308                 }
2309                 return value;
2310         };
2311
2312         var RequireObjectCoercible = CheckObjectCoercible;
2313
2314         var $Object = GetIntrinsic('%Object%');
2315
2316
2317
2318         // https://www.ecma-international.org/ecma-262/6.0/#sec-toobject
2319
2320         var ToObject = function ToObject(value) {
2321                 RequireObjectCoercible(value);
2322                 return $Object(value);
2323         };
2324
2325         var $Math = GetIntrinsic('%Math%');
2326         var $Number = GetIntrinsic('%Number%');
2327
2328         var maxSafeInteger = $Number.MAX_SAFE_INTEGER || $Math.pow(2, 53) - 1;
2329
2330         // http://www.ecma-international.org/ecma-262/5.1/#sec-9.3
2331
2332         var ToNumber = function ToNumber(value) {
2333                 return +value; // eslint-disable-line no-implicit-coercion
2334         };
2335
2336         var _isNaN = Number.isNaN || function isNaN(a) {
2337                 return a !== a;
2338         };
2339
2340         var $isNaN = Number.isNaN || function (a) { return a !== a; };
2341
2342         var _isFinite = Number.isFinite || function (x) { return typeof x === 'number' && !$isNaN(x) && x !== Infinity && x !== -Infinity; };
2343
2344         var sign$1 = function sign(number) {
2345                 return number >= 0 ? 1 : -1;
2346         };
2347
2348         var $Math$1 = GetIntrinsic('%Math%');
2349
2350
2351
2352
2353
2354
2355         var $floor = $Math$1.floor;
2356         var $abs = $Math$1.abs;
2357
2358         // http://www.ecma-international.org/ecma-262/5.1/#sec-9.4
2359
2360         var ToInteger = function ToInteger(value) {
2361                 var number = ToNumber(value);
2362                 if (_isNaN(number)) { return 0; }
2363                 if (number === 0 || !_isFinite(number)) { return number; }
2364                 return sign$1(number) * $floor($abs(number));
2365         };
2366
2367         var $apply = GetIntrinsic('%Function.prototype.apply%');
2368         var $call = GetIntrinsic('%Function.prototype.call%');
2369         var $reflectApply = GetIntrinsic('%Reflect.apply%', true) || functionBind.call($call, $apply);
2370
2371         var callBind = function callBind() {
2372                 return $reflectApply(functionBind, $call, arguments);
2373         };
2374
2375         var apply = function applyBind() {
2376                 return $reflectApply(functionBind, $apply, arguments);
2377         };
2378         callBind.apply = apply;
2379
2380         var $indexOf = callBind(GetIntrinsic('String.prototype.indexOf'));
2381
2382         var callBound = function callBoundIntrinsic(name, allowMissing) {
2383                 var intrinsic = GetIntrinsic(name, !!allowMissing);
2384                 if (typeof intrinsic === 'function' && $indexOf(name, '.prototype.')) {
2385                         return callBind(intrinsic);
2386                 }
2387                 return intrinsic;
2388         };
2389
2390         var $test = GetIntrinsic('RegExp.prototype.test');
2391
2392
2393
2394         var regexTester = function regexTester(regex) {
2395                 return callBind($test, regex);
2396         };
2397
2398         var isPrimitive = function isPrimitive(value) {
2399                 return value === null || (typeof value !== 'function' && typeof value !== 'object');
2400         };
2401
2402         var isPrimitive$1 = function isPrimitive(value) {
2403                 return value === null || (typeof value !== 'function' && typeof value !== 'object');
2404         };
2405
2406         var fnToStr = Function.prototype.toString;
2407         var reflectApply = typeof Reflect === 'object' && Reflect !== null && Reflect.apply;
2408         var badArrayLike;
2409         var isCallableMarker;
2410         if (typeof reflectApply === 'function' && typeof Object.defineProperty === 'function') {
2411                 try {
2412                         badArrayLike = Object.defineProperty({}, 'length', {
2413                                 get: function () {
2414                                         throw isCallableMarker;
2415                                 }
2416                         });
2417                         isCallableMarker = {};
2418                 } catch (_) {
2419                         reflectApply = null;
2420                 }
2421         } else {
2422                 reflectApply = null;
2423         }
2424
2425         var constructorRegex = /^\s*class\b/;
2426         var isES6ClassFn = function isES6ClassFunction(value) {
2427                 try {
2428                         var fnStr = fnToStr.call(value);
2429                         return constructorRegex.test(fnStr);
2430                 } catch (e) {
2431                         return false; // not a function
2432                 }
2433         };
2434
2435         var tryFunctionObject = function tryFunctionToStr(value) {
2436                 try {
2437                         if (isES6ClassFn(value)) { return false; }
2438                         fnToStr.call(value);
2439                         return true;
2440                 } catch (e) {
2441                         return false;
2442                 }
2443         };
2444         var toStr$4 = Object.prototype.toString;
2445         var fnClass = '[object Function]';
2446         var genClass = '[object GeneratorFunction]';
2447         var hasToStringTag = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol';
2448
2449         var isCallable = reflectApply
2450                 ? function isCallable(value) {
2451                         if (!value) { return false; }
2452                         if (typeof value !== 'function' && typeof value !== 'object') { return false; }
2453                         if (typeof value === 'function' && !value.prototype) { return true; }
2454                         try {
2455                                 reflectApply(value, null, badArrayLike);
2456                         } catch (e) {
2457                                 if (e !== isCallableMarker) { return false; }
2458                         }
2459                         return !isES6ClassFn(value);
2460                 }
2461                 : function isCallable(value) {
2462                         if (!value) { return false; }
2463                         if (typeof value !== 'function' && typeof value !== 'object') { return false; }
2464                         if (typeof value === 'function' && !value.prototype) { return true; }
2465                         if (hasToStringTag) { return tryFunctionObject(value); }
2466                         if (isES6ClassFn(value)) { return false; }
2467                         var strClass = toStr$4.call(value);
2468                         return strClass === fnClass || strClass === genClass;
2469                 };
2470
2471         var getDay = Date.prototype.getDay;
2472         var tryDateObject = function tryDateGetDayCall(value) {
2473                 try {
2474                         getDay.call(value);
2475                         return true;
2476                 } catch (e) {
2477                         return false;
2478                 }
2479         };
2480
2481         var toStr$5 = Object.prototype.toString;
2482         var dateClass = '[object Date]';
2483         var hasToStringTag$1 = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol';
2484
2485         var isDateObject = function isDateObject(value) {
2486                 if (typeof value !== 'object' || value === null) {
2487                         return false;
2488                 }
2489                 return hasToStringTag$1 ? tryDateObject(value) : toStr$5.call(value) === dateClass;
2490         };
2491
2492         var isSymbol$2 = createCommonjsModule(function (module) {
2493
2494         var toStr = Object.prototype.toString;
2495         var hasSymbols = hasSymbols$1();
2496
2497         if (hasSymbols) {
2498                 var symToStr = Symbol.prototype.toString;
2499                 var symStringRegex = /^Symbol\(.*\)$/;
2500                 var isSymbolObject = function isRealSymbolObject(value) {
2501                         if (typeof value.valueOf() !== 'symbol') {
2502                                 return false;
2503                         }
2504                         return symStringRegex.test(symToStr.call(value));
2505                 };
2506
2507                 module.exports = function isSymbol(value) {
2508                         if (typeof value === 'symbol') {
2509                                 return true;
2510                         }
2511                         if (toStr.call(value) !== '[object Symbol]') {
2512                                 return false;
2513                         }
2514                         try {
2515                                 return isSymbolObject(value);
2516                         } catch (e) {
2517                                 return false;
2518                         }
2519                 };
2520         } else {
2521
2522                 module.exports = function isSymbol(value) {
2523                         // this environment does not support Symbols.
2524                         return false ;
2525                 };
2526         }
2527         });
2528
2529         var hasSymbols$3 = typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol';
2530
2531
2532
2533
2534
2535
2536         var ordinaryToPrimitive = function OrdinaryToPrimitive(O, hint) {
2537                 if (typeof O === 'undefined' || O === null) {
2538                         throw new TypeError('Cannot call method on ' + O);
2539                 }
2540                 if (typeof hint !== 'string' || (hint !== 'number' && hint !== 'string')) {
2541                         throw new TypeError('hint must be "string" or "number"');
2542                 }
2543                 var methodNames = hint === 'string' ? ['toString', 'valueOf'] : ['valueOf', 'toString'];
2544                 var method, result, i;
2545                 for (i = 0; i < methodNames.length; ++i) {
2546                         method = O[methodNames[i]];
2547                         if (isCallable(method)) {
2548                                 result = method.call(O);
2549                                 if (isPrimitive$1(result)) {
2550                                         return result;
2551                                 }
2552                         }
2553                 }
2554                 throw new TypeError('No default value');
2555         };
2556
2557         var GetMethod = function GetMethod(O, P) {
2558                 var func = O[P];
2559                 if (func !== null && typeof func !== 'undefined') {
2560                         if (!isCallable(func)) {
2561                                 throw new TypeError(func + ' returned for property ' + P + ' of object ' + O + ' is not a function');
2562                         }
2563                         return func;
2564                 }
2565                 return void 0;
2566         };
2567
2568         // http://www.ecma-international.org/ecma-262/6.0/#sec-toprimitive
2569         var es2015 = function ToPrimitive(input) {
2570                 if (isPrimitive$1(input)) {
2571                         return input;
2572                 }
2573                 var hint = 'default';
2574                 if (arguments.length > 1) {
2575                         if (arguments[1] === String) {
2576                                 hint = 'string';
2577                         } else if (arguments[1] === Number) {
2578                                 hint = 'number';
2579                         }
2580                 }
2581
2582                 var exoticToPrim;
2583                 if (hasSymbols$3) {
2584                         if (Symbol.toPrimitive) {
2585                                 exoticToPrim = GetMethod(input, Symbol.toPrimitive);
2586                         } else if (isSymbol$2(input)) {
2587                                 exoticToPrim = Symbol.prototype.valueOf;
2588                         }
2589                 }
2590                 if (typeof exoticToPrim !== 'undefined') {
2591                         var result = exoticToPrim.call(input, hint);
2592                         if (isPrimitive$1(result)) {
2593                                 return result;
2594                         }
2595                         throw new TypeError('unable to convert exotic object to primitive');
2596                 }
2597                 if (hint === 'default' && (isDateObject(input) || isSymbol$2(input))) {
2598                         hint = 'string';
2599                 }
2600                 return ordinaryToPrimitive(input, hint === 'default' ? 'number' : hint);
2601         };
2602
2603         // https://www.ecma-international.org/ecma-262/6.0/#sec-toprimitive
2604
2605         var ToPrimitive = function ToPrimitive(input) {
2606                 if (arguments.length > 1) {
2607                         return es2015(input, arguments[1]);
2608                 }
2609                 return es2015(input);
2610         };
2611
2612         var $TypeError$2 = GetIntrinsic('%TypeError%');
2613         var $Number$1 = GetIntrinsic('%Number%');
2614         var $RegExp = GetIntrinsic('%RegExp%');
2615         var $parseInteger = GetIntrinsic('%parseInt%');
2616
2617
2618
2619
2620
2621         var $strSlice = callBound('String.prototype.slice');
2622         var isBinary = regexTester(/^0b[01]+$/i);
2623         var isOctal = regexTester(/^0o[0-7]+$/i);
2624         var isInvalidHexLiteral = regexTester(/^[-+]0x[0-9a-f]+$/i);
2625         var nonWS = ['\u0085', '\u200b', '\ufffe'].join('');
2626         var nonWSregex = new $RegExp('[' + nonWS + ']', 'g');
2627         var hasNonWS = regexTester(nonWSregex);
2628
2629         // whitespace from: https://es5.github.io/#x15.5.4.20
2630         // implementation from https://github.com/es-shims/es5-shim/blob/v3.4.0/es5-shim.js#L1304-L1324
2631         var ws = [
2632                 '\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u180E\u2000\u2001\u2002\u2003',
2633                 '\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028',
2634                 '\u2029\uFEFF'
2635         ].join('');
2636         var trimRegex = new RegExp('(^[' + ws + ']+)|([' + ws + ']+$)', 'g');
2637         var $replace$1 = callBound('String.prototype.replace');
2638         var $trim = function (value) {
2639                 return $replace$1(value, trimRegex, '');
2640         };
2641
2642
2643
2644         // https://www.ecma-international.org/ecma-262/6.0/#sec-tonumber
2645
2646         var ToNumber$1 = function ToNumber(argument) {
2647                 var value = isPrimitive(argument) ? argument : ToPrimitive(argument, $Number$1);
2648                 if (typeof value === 'symbol') {
2649                         throw new $TypeError$2('Cannot convert a Symbol value to a number');
2650                 }
2651                 if (typeof value === 'string') {
2652                         if (isBinary(value)) {
2653                                 return ToNumber($parseInteger($strSlice(value, 2), 2));
2654                         } else if (isOctal(value)) {
2655                                 return ToNumber($parseInteger($strSlice(value, 2), 8));
2656                         } else if (hasNonWS(value) || isInvalidHexLiteral(value)) {
2657                                 return NaN;
2658                         } else {
2659                                 var trimmed = $trim(value);
2660                                 if (trimmed !== value) {
2661                                         return ToNumber(trimmed);
2662                                 }
2663                         }
2664                 }
2665                 return $Number$1(value);
2666         };
2667
2668         // https://www.ecma-international.org/ecma-262/6.0/#sec-tointeger
2669
2670         var ToInteger$1 = function ToInteger$1(value) {
2671                 var number = ToNumber$1(value);
2672                 return ToInteger(number);
2673         };
2674
2675         var ToLength = function ToLength(argument) {
2676                 var len = ToInteger$1(argument);
2677                 if (len <= 0) { return 0; } // includes converting -0 to +0
2678                 if (len > maxSafeInteger) { return maxSafeInteger; }
2679                 return len;
2680         };
2681
2682         // http://www.ecma-international.org/ecma-262/5.1/#sec-9.11
2683
2684         var IsCallable = isCallable;
2685
2686         var implementation$3 = function find(predicate) {
2687                 var list = ToObject(this);
2688                 var length = ToLength(list.length);
2689                 if (!IsCallable(predicate)) {
2690                         throw new TypeError('Array#find: predicate must be a function');
2691                 }
2692                 if (length === 0) {
2693                         return void 0;
2694                 }
2695                 var thisArg;
2696                 if (arguments.length > 0) {
2697                         thisArg = arguments[1];
2698                 }
2699
2700                 for (var i = 0, value; i < length; i++) {
2701                         value = list[i];
2702                         // inlined for performance: if (Call(predicate, thisArg, [value, i, list])) {
2703                         if (predicate.apply(thisArg, [value, i, list])) {
2704                                 return value;
2705                         }
2706                 }
2707                 return void 0;
2708         };
2709
2710         var polyfill$4 = function getPolyfill() {
2711                 // Detect if an implementation exists
2712                 // Detect early implementations which skipped holes in sparse arrays
2713                 // eslint-disable-next-line no-sparse-arrays
2714                 var implemented = Array.prototype.find && [, 1].find(function () {
2715                         return true;
2716                 }) !== 1;
2717
2718                 // eslint-disable-next-line global-require
2719                 return implemented ? Array.prototype.find : implementation$3;
2720         };
2721
2722         var shim$8 = function shimArrayPrototypeFind() {
2723                 var polyfill = polyfill$4();
2724
2725                 defineProperties_1(Array.prototype, { find: polyfill }, {
2726                         find: function () {
2727                                 return Array.prototype.find !== polyfill;
2728                         }
2729                 });
2730
2731                 return polyfill;
2732         };
2733
2734         var slice$2 = Array.prototype.slice;
2735
2736         var polyfill$5 = polyfill$4();
2737
2738         var boundFindShim = function find(array, predicate) { // eslint-disable-line no-unused-vars
2739                 RequireObjectCoercible(array);
2740                 var args = slice$2.call(arguments, 1);
2741                 return polyfill$5.apply(array, args);
2742         };
2743
2744         defineProperties_1(boundFindShim, {
2745                 getPolyfill: polyfill$4,
2746                 implementation: implementation$3,
2747                 shim: shim$8
2748         });
2749
2750         var array_prototype_find = boundFindShim;
2751
2752         var implementation$4 = function findIndex(predicate) {
2753                 var list = ToObject(this);
2754                 var length = ToLength(list.length);
2755                 if (!IsCallable(predicate)) {
2756                         throw new TypeError('Array#findIndex: predicate must be a function');
2757                 }
2758
2759                 if (length === 0) {
2760                         return -1;
2761                 }
2762
2763                 var thisArg;
2764                 if (arguments.length > 0) {
2765                         thisArg = arguments[1];
2766                 }
2767
2768                 for (var i = 0, value; i < length; i++) {
2769                         value = list[i];
2770                         // inlined for performance: if (Call(predicate, thisArg, [value, i, list])) return i;
2771                         if (predicate.apply(thisArg, [value, i, list])) {
2772                                 return i;
2773                         }
2774                 }
2775
2776                 return -1;
2777         };
2778
2779         var polyfill$6 = function getPolyfill() {
2780                 // Detect if an implementation exists
2781                 // Detect early implementations which skipped holes in sparse arrays
2782                 // eslint-disable-next-line no-sparse-arrays
2783                 var implemented = Array.prototype.findIndex && ([, 1].findIndex(function (item, idx) {
2784                         return idx === 0;
2785                 }) === 0);
2786
2787                 return implemented ? Array.prototype.findIndex : implementation$4;
2788         };
2789
2790         var shim$9 = function shimFindIndex() {
2791                 var polyfill = polyfill$6();
2792
2793                 defineProperties_1(Array.prototype, { findIndex: polyfill }, {
2794                         findIndex: function () {
2795                                 return Array.prototype.findIndex !== polyfill;
2796                         }
2797                 });
2798
2799                 return polyfill;
2800         };
2801
2802         var slice$3 = Array.prototype.slice;
2803
2804         var polyfill$7 = polyfill$6();
2805
2806         var boundShim = function findIndex(array, predicate) { // eslint-disable-line no-unused-vars
2807                 RequireObjectCoercible(array);
2808                 var args = slice$3.call(arguments, 1);
2809                 return polyfill$7.apply(array, args);
2810         };
2811
2812         defineProperties_1(boundShim, {
2813                 getPolyfill: polyfill$6,
2814                 implementation: implementation$4,
2815                 shim: shim$9
2816         });
2817
2818         var array_prototype_findindex = boundShim;
2819
2820         var $apply$1 = GetIntrinsic('%Reflect.apply%', true) || callBound('%Function.prototype.apply%');
2821
2822         // https://www.ecma-international.org/ecma-262/6.0/#sec-call
2823
2824         var Call = function Call(F, V) {
2825                 var args = arguments.length > 2 ? arguments[2] : [];
2826                 return $apply$1(F, V, args);
2827         };
2828
2829         var $defineProperty = GetIntrinsic('%Object.defineProperty%', true);
2830
2831         if ($defineProperty) {
2832                 try {
2833                         $defineProperty({}, 'a', { value: 1 });
2834                 } catch (e) {
2835                         // IE 8 has a broken defineProperty
2836                         $defineProperty = null;
2837                 }
2838         }
2839
2840
2841
2842         var $isEnumerable = callBound('Object.prototype.propertyIsEnumerable');
2843
2844         // eslint-disable-next-line max-params
2845         var DefineOwnProperty = function DefineOwnProperty(IsDataDescriptor, SameValue, FromPropertyDescriptor, O, P, desc) {
2846                 if (!$defineProperty) {
2847                         if (!IsDataDescriptor(desc)) {
2848                                 // ES3 does not support getters/setters
2849                                 return false;
2850                         }
2851                         if (!desc['[[Configurable]]'] || !desc['[[Writable]]']) {
2852                                 return false;
2853                         }
2854
2855                         // fallback for ES3
2856                         if (P in O && $isEnumerable(O, P) !== !!desc['[[Enumerable]]']) {
2857                                 // a non-enumerable existing property
2858                                 return false;
2859                         }
2860
2861                         // property does not exist at all, or exists but is enumerable
2862                         var V = desc['[[Value]]'];
2863                         // eslint-disable-next-line no-param-reassign
2864                         O[P] = V; // will use [[Define]]
2865                         return SameValue(O[P], V);
2866                 }
2867                 $defineProperty(O, P, FromPropertyDescriptor(desc));
2868                 return true;
2869         };
2870
2871         var src = functionBind.call(Function.call, Object.prototype.hasOwnProperty);
2872
2873         var $TypeError$3 = GetIntrinsic('%TypeError%');
2874         var $SyntaxError = GetIntrinsic('%SyntaxError%');
2875
2876
2877
2878         var predicates = {
2879                 // https://ecma-international.org/ecma-262/6.0/#sec-property-descriptor-specification-type
2880                 'Property Descriptor': function isPropertyDescriptor(Type, Desc) {
2881                         if (Type(Desc) !== 'Object') {
2882                                 return false;
2883                         }
2884                         var allowed = {
2885                                 '[[Configurable]]': true,
2886                                 '[[Enumerable]]': true,
2887                                 '[[Get]]': true,
2888                                 '[[Set]]': true,
2889                                 '[[Value]]': true,
2890                                 '[[Writable]]': true
2891                         };
2892
2893                         for (var key in Desc) { // eslint-disable-line
2894                                 if (src(Desc, key) && !allowed[key]) {
2895                                         return false;
2896                                 }
2897                         }
2898
2899                         var isData = src(Desc, '[[Value]]');
2900                         var IsAccessor = src(Desc, '[[Get]]') || src(Desc, '[[Set]]');
2901                         if (isData && IsAccessor) {
2902                                 throw new $TypeError$3('Property Descriptors may not be both accessor and data descriptors');
2903                         }
2904                         return true;
2905                 }
2906         };
2907
2908         var assertRecord = function assertRecord(Type, recordType, argumentName, value) {
2909                 var predicate = predicates[recordType];
2910                 if (typeof predicate !== 'function') {
2911                         throw new $SyntaxError('unknown record type: ' + recordType);
2912                 }
2913                 if (!predicate(Type, value)) {
2914                         throw new $TypeError$3(argumentName + ' must be a ' + recordType);
2915                 }
2916         };
2917
2918         // https://www.ecma-international.org/ecma-262/5.1/#sec-8
2919
2920         var Type = function Type(x) {
2921                 if (x === null) {
2922                         return 'Null';
2923                 }
2924                 if (typeof x === 'undefined') {
2925                         return 'Undefined';
2926                 }
2927                 if (typeof x === 'function' || typeof x === 'object') {
2928                         return 'Object';
2929                 }
2930                 if (typeof x === 'number') {
2931                         return 'Number';
2932                 }
2933                 if (typeof x === 'boolean') {
2934                         return 'Boolean';
2935                 }
2936                 if (typeof x === 'string') {
2937                         return 'String';
2938                 }
2939         };
2940
2941         // https://ecma-international.org/ecma-262/6.0/#sec-ecmascript-data-types-and-values
2942
2943         var Type$1 = function Type$1(x) {
2944                 if (typeof x === 'symbol') {
2945                         return 'Symbol';
2946                 }
2947                 return Type(x);
2948         };
2949
2950         // https://www.ecma-international.org/ecma-262/6.0/#sec-frompropertydescriptor
2951
2952         var FromPropertyDescriptor = function FromPropertyDescriptor(Desc) {
2953                 if (typeof Desc === 'undefined') {
2954                         return Desc;
2955                 }
2956
2957                 assertRecord(Type$1, 'Property Descriptor', 'Desc', Desc);
2958
2959                 var obj = {};
2960                 if ('[[Value]]' in Desc) {
2961                         obj.value = Desc['[[Value]]'];
2962                 }
2963                 if ('[[Writable]]' in Desc) {
2964                         obj.writable = Desc['[[Writable]]'];
2965                 }
2966                 if ('[[Get]]' in Desc) {
2967                         obj.get = Desc['[[Get]]'];
2968                 }
2969                 if ('[[Set]]' in Desc) {
2970                         obj.set = Desc['[[Set]]'];
2971                 }
2972                 if ('[[Enumerable]]' in Desc) {
2973                         obj.enumerable = Desc['[[Enumerable]]'];
2974                 }
2975                 if ('[[Configurable]]' in Desc) {
2976                         obj.configurable = Desc['[[Configurable]]'];
2977                 }
2978                 return obj;
2979         };
2980
2981         var $gOPD$1 = GetIntrinsic('%Object.getOwnPropertyDescriptor%');
2982         if ($gOPD$1) {
2983                 try {
2984                         $gOPD$1([], 'length');
2985                 } catch (e) {
2986                         // IE 8 has a broken gOPD
2987                         $gOPD$1 = null;
2988                 }
2989         }
2990
2991         var getOwnPropertyDescriptor = $gOPD$1;
2992
2993         var $Array = GetIntrinsic('%Array%');
2994
2995         // eslint-disable-next-line global-require
2996         var toStr$6 = !$Array.isArray && callBound('Object.prototype.toString');
2997
2998         // https://www.ecma-international.org/ecma-262/6.0/#sec-isarray
2999
3000         var IsArray = $Array.isArray || function IsArray(argument) {
3001                 return toStr$6(argument) === '[object Array]';
3002         };
3003
3004         // https://www.ecma-international.org/ecma-262/6.0/#sec-ispropertykey
3005
3006         var IsPropertyKey = function IsPropertyKey(argument) {
3007                 return typeof argument === 'string' || typeof argument === 'symbol';
3008         };
3009
3010         var hasSymbols$4 = hasSymbols$1();
3011         var hasToStringTag$2 = hasSymbols$4 && typeof Symbol.toStringTag === 'symbol';
3012         var regexExec;
3013         var isRegexMarker;
3014         var badStringifier;
3015
3016         if (hasToStringTag$2) {
3017                 regexExec = Function.call.bind(RegExp.prototype.exec);
3018                 isRegexMarker = {};
3019
3020                 var throwRegexMarker = function () {
3021                         throw isRegexMarker;
3022                 };
3023                 badStringifier = {
3024                         toString: throwRegexMarker,
3025                         valueOf: throwRegexMarker
3026                 };
3027
3028                 if (typeof Symbol.toPrimitive === 'symbol') {
3029                         badStringifier[Symbol.toPrimitive] = throwRegexMarker;
3030                 }
3031         }
3032
3033         var toStr$7 = Object.prototype.toString;
3034         var regexClass = '[object RegExp]';
3035
3036         var isRegex = hasToStringTag$2
3037                 // eslint-disable-next-line consistent-return
3038                 ? function isRegex(value) {
3039                         if (!value || typeof value !== 'object') {
3040                                 return false;
3041                         }
3042
3043                         try {
3044                                 regexExec(value, badStringifier);
3045                         } catch (e) {
3046                                 return e === isRegexMarker;
3047                         }
3048                 }
3049                 : function isRegex(value) {
3050                         // In older browsers, typeof regex incorrectly returns 'function'
3051                         if (!value || (typeof value !== 'object' && typeof value !== 'function')) {
3052                                 return false;
3053                         }
3054
3055                         return toStr$7.call(value) === regexClass;
3056                 };
3057
3058         // http://www.ecma-international.org/ecma-262/5.1/#sec-9.2
3059
3060         var ToBoolean = function ToBoolean(value) { return !!value; };
3061
3062         var $match = GetIntrinsic('%Symbol.match%', true);
3063
3064
3065
3066
3067
3068         // https://ecma-international.org/ecma-262/6.0/#sec-isregexp
3069
3070         var IsRegExp = function IsRegExp(argument) {
3071                 if (!argument || typeof argument !== 'object') {
3072                         return false;
3073                 }
3074                 if ($match) {
3075                         var isRegExp = argument[$match];
3076                         if (typeof isRegExp !== 'undefined') {
3077                                 return ToBoolean(isRegExp);
3078                         }
3079                 }
3080                 return isRegex(argument);
3081         };
3082
3083         var $TypeError$4 = GetIntrinsic('%TypeError%');
3084
3085
3086
3087
3088
3089         // https://ecma-international.org/ecma-262/5.1/#sec-8.10.5
3090
3091         var ToPropertyDescriptor = function ToPropertyDescriptor(Obj) {
3092                 if (Type$1(Obj) !== 'Object') {
3093                         throw new $TypeError$4('ToPropertyDescriptor requires an object');
3094                 }
3095
3096                 var desc = {};
3097                 if (src(Obj, 'enumerable')) {
3098                         desc['[[Enumerable]]'] = ToBoolean(Obj.enumerable);
3099                 }
3100                 if (src(Obj, 'configurable')) {
3101                         desc['[[Configurable]]'] = ToBoolean(Obj.configurable);
3102                 }
3103                 if (src(Obj, 'value')) {
3104                         desc['[[Value]]'] = Obj.value;
3105                 }
3106                 if (src(Obj, 'writable')) {
3107                         desc['[[Writable]]'] = ToBoolean(Obj.writable);
3108                 }
3109                 if (src(Obj, 'get')) {
3110                         var getter = Obj.get;
3111                         if (typeof getter !== 'undefined' && !IsCallable(getter)) {
3112                                 throw new TypeError('getter must be a function');
3113                         }
3114                         desc['[[Get]]'] = getter;
3115                 }
3116                 if (src(Obj, 'set')) {
3117                         var setter = Obj.set;
3118                         if (typeof setter !== 'undefined' && !IsCallable(setter)) {
3119                                 throw new $TypeError$4('setter must be a function');
3120                         }
3121                         desc['[[Set]]'] = setter;
3122                 }
3123
3124                 if ((src(desc, '[[Get]]') || src(desc, '[[Set]]')) && (src(desc, '[[Value]]') || src(desc, '[[Writable]]'))) {
3125                         throw new $TypeError$4('Invalid property descriptor. Cannot both specify accessors and a value or writable attribute');
3126                 }
3127                 return desc;
3128         };
3129
3130         var $TypeError$5 = GetIntrinsic('%TypeError%');
3131
3132
3133
3134         var $isEnumerable$1 = callBound('Object.prototype.propertyIsEnumerable');
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144         // https://www.ecma-international.org/ecma-262/6.0/#sec-ordinarygetownproperty
3145
3146         var OrdinaryGetOwnProperty = function OrdinaryGetOwnProperty(O, P) {
3147                 if (Type$1(O) !== 'Object') {
3148                         throw new $TypeError$5('Assertion failed: O must be an Object');
3149                 }
3150                 if (!IsPropertyKey(P)) {
3151                         throw new $TypeError$5('Assertion failed: P must be a Property Key');
3152                 }
3153                 if (!src(O, P)) {
3154                         return void 0;
3155                 }
3156                 if (!getOwnPropertyDescriptor) {
3157                         // ES3 / IE 8 fallback
3158                         var arrayLength = IsArray(O) && P === 'length';
3159                         var regexLastIndex = IsRegExp(O) && P === 'lastIndex';
3160                         return {
3161                                 '[[Configurable]]': !(arrayLength || regexLastIndex),
3162                                 '[[Enumerable]]': $isEnumerable$1(O, P),
3163                                 '[[Value]]': O[P],
3164                                 '[[Writable]]': true
3165                         };
3166                 }
3167                 return ToPropertyDescriptor(getOwnPropertyDescriptor(O, P));
3168         };
3169
3170         // https://www.ecma-international.org/ecma-262/6.0/#sec-isdatadescriptor
3171
3172         var IsDataDescriptor = function IsDataDescriptor(Desc) {
3173                 if (typeof Desc === 'undefined') {
3174                         return false;
3175                 }
3176
3177                 assertRecord(Type$1, 'Property Descriptor', 'Desc', Desc);
3178
3179                 if (!src(Desc, '[[Value]]') && !src(Desc, '[[Writable]]')) {
3180                         return false;
3181                 }
3182
3183                 return true;
3184         };
3185
3186         var $Object$1 = GetIntrinsic('%Object%');
3187
3188
3189
3190         var $preventExtensions = $Object$1.preventExtensions;
3191         var $isExtensible = $Object$1.isExtensible;
3192
3193         // https://www.ecma-international.org/ecma-262/6.0/#sec-isextensible-o
3194
3195         var IsExtensible = $preventExtensions
3196                 ? function IsExtensible(obj) {
3197                         return !isPrimitive(obj) && $isExtensible(obj);
3198                 }
3199                 : function IsExtensible(obj) {
3200                         return !isPrimitive(obj);
3201                 };
3202
3203         // http://www.ecma-international.org/ecma-262/5.1/#sec-9.12
3204
3205         var SameValue = function SameValue(x, y) {
3206                 if (x === y) { // 0 === -0, but they are not identical.
3207                         if (x === 0) { return 1 / x === 1 / y; }
3208                         return true;
3209                 }
3210                 return _isNaN(x) && _isNaN(y);
3211         };
3212
3213         var $TypeError$6 = GetIntrinsic('%TypeError%');
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225         // https://www.ecma-international.org/ecma-262/6.0/#sec-createdataproperty
3226
3227         var CreateDataProperty = function CreateDataProperty(O, P, V) {
3228                 if (Type$1(O) !== 'Object') {
3229                         throw new $TypeError$6('Assertion failed: Type(O) is not Object');
3230                 }
3231                 if (!IsPropertyKey(P)) {
3232                         throw new $TypeError$6('Assertion failed: IsPropertyKey(P) is not true');
3233                 }
3234                 var oldDesc = OrdinaryGetOwnProperty(O, P);
3235                 var extensible = !oldDesc || IsExtensible(O);
3236                 var immutable = oldDesc && (!oldDesc['[[Writable]]'] || !oldDesc['[[Configurable]]']);
3237                 if (immutable || !extensible) {
3238                         return false;
3239                 }
3240                 return DefineOwnProperty(
3241                         IsDataDescriptor,
3242                         SameValue,
3243                         FromPropertyDescriptor,
3244                         O,
3245                         P,
3246                         {
3247                                 '[[Configurable]]': true,
3248                                 '[[Enumerable]]': true,
3249                                 '[[Value]]': V,
3250                                 '[[Writable]]': true
3251                         }
3252                 );
3253         };
3254
3255         var $TypeError$7 = GetIntrinsic('%TypeError%');
3256
3257
3258
3259
3260
3261         // // https://ecma-international.org/ecma-262/6.0/#sec-createdatapropertyorthrow
3262
3263         var CreateDataPropertyOrThrow = function CreateDataPropertyOrThrow(O, P, V) {
3264                 if (Type$1(O) !== 'Object') {
3265                         throw new $TypeError$7('Assertion failed: Type(O) is not Object');
3266                 }
3267                 if (!IsPropertyKey(P)) {
3268                         throw new $TypeError$7('Assertion failed: IsPropertyKey(P) is not true');
3269                 }
3270                 var success = CreateDataProperty(O, P, V);
3271                 if (!success) {
3272                         throw new $TypeError$7('unable to create data property');
3273                 }
3274                 return success;
3275         };
3276
3277         var hasMap = typeof Map === 'function' && Map.prototype;
3278         var mapSizeDescriptor = Object.getOwnPropertyDescriptor && hasMap ? Object.getOwnPropertyDescriptor(Map.prototype, 'size') : null;
3279         var mapSize = hasMap && mapSizeDescriptor && typeof mapSizeDescriptor.get === 'function' ? mapSizeDescriptor.get : null;
3280         var mapForEach = hasMap && Map.prototype.forEach;
3281         var hasSet = typeof Set === 'function' && Set.prototype;
3282         var setSizeDescriptor = Object.getOwnPropertyDescriptor && hasSet ? Object.getOwnPropertyDescriptor(Set.prototype, 'size') : null;
3283         var setSize = hasSet && setSizeDescriptor && typeof setSizeDescriptor.get === 'function' ? setSizeDescriptor.get : null;
3284         var setForEach = hasSet && Set.prototype.forEach;
3285         var booleanValueOf = Boolean.prototype.valueOf;
3286         var objectToString$1 = Object.prototype.toString;
3287
3288         var objectInspect = function inspect_ (obj, opts, depth, seen) {
3289             if (typeof obj === 'undefined') {
3290                 return 'undefined';
3291             }
3292             if (obj === null) {
3293                 return 'null';
3294             }
3295             if (typeof obj === 'boolean') {
3296                 return obj ? 'true' : 'false';
3297             }
3298             if (typeof obj === 'string') {
3299                 return inspectString(obj);
3300             }
3301             if (typeof obj === 'number') {
3302               if (obj === 0) {
3303                 return Infinity / obj > 0 ? '0' : '-0';
3304               }
3305               return String(obj);
3306             }
3307
3308             if (!opts) opts = {};
3309
3310             var maxDepth = typeof opts.depth === 'undefined' ? 5 : opts.depth;
3311             if (typeof depth === 'undefined') depth = 0;
3312             if (depth >= maxDepth && maxDepth > 0 && typeof obj === 'object') {
3313                 return '[Object]';
3314             }
3315
3316             if (typeof seen === 'undefined') seen = [];
3317             else if (indexOf$2(seen, obj) >= 0) {
3318                 return '[Circular]';
3319             }
3320
3321             function inspect (value, from) {
3322                 if (from) {
3323                     seen = seen.slice();
3324                     seen.push(from);
3325                 }
3326                 return inspect_(value, opts, depth + 1, seen);
3327             }
3328
3329             if (typeof obj === 'function') {
3330                 var name = nameOf(obj);
3331                 return '[Function' + (name ? ': ' + name : '') + ']';
3332             }
3333             if (isSymbol$3(obj)) {
3334                 var symString = Symbol.prototype.toString.call(obj);
3335                 return typeof obj === 'object' ? markBoxed(symString) : symString;
3336             }
3337             if (isElement(obj)) {
3338                 var s = '<' + String(obj.nodeName).toLowerCase();
3339                 var attrs = obj.attributes || [];
3340                 for (var i = 0; i < attrs.length; i++) {
3341                     s += ' ' + attrs[i].name + '="' + quote(attrs[i].value) + '"';
3342                 }
3343                 s += '>';
3344                 if (obj.childNodes && obj.childNodes.length) s += '...';
3345                 s += '</' + String(obj.nodeName).toLowerCase() + '>';
3346                 return s;
3347             }
3348             if (isArray$3(obj)) {
3349                 if (obj.length === 0) return '[]';
3350                 return '[ ' + arrObjKeys(obj, inspect).join(', ') + ' ]';
3351             }
3352             if (isError(obj)) {
3353                 var parts = arrObjKeys(obj, inspect);
3354                 if (parts.length === 0) return '[' + String(obj) + ']';
3355                 return '{ [' + String(obj) + '] ' + parts.join(', ') + ' }';
3356             }
3357             if (typeof obj === 'object' && typeof obj.inspect === 'function') {
3358                 return obj.inspect();
3359             }
3360             if (isMap(obj)) {
3361                 var parts = [];
3362                 mapForEach.call(obj, function (value, key) {
3363                     parts.push(inspect(key, obj) + ' => ' + inspect(value, obj));
3364                 });
3365                 return collectionOf('Map', mapSize.call(obj), parts);
3366             }
3367             if (isSet(obj)) {
3368                 var parts = [];
3369                 setForEach.call(obj, function (value ) {
3370                     parts.push(inspect(value, obj));
3371                 });
3372                 return collectionOf('Set', setSize.call(obj), parts);
3373             }
3374             if (isNumber(obj)) {
3375                 return markBoxed(Number(obj));
3376             }
3377             if (isBoolean(obj)) {
3378                 return markBoxed(booleanValueOf.call(obj));
3379             }
3380             if (isString$1(obj)) {
3381                 return markBoxed(inspect(String(obj)));
3382             }
3383             if (!isDate(obj) && !isRegExp(obj)) {
3384                 var xs = arrObjKeys(obj, inspect);
3385                 if (xs.length === 0) return '{}';
3386                 return '{ ' + xs.join(', ') + ' }';
3387             }
3388             return String(obj);
3389         };
3390
3391         function quote (s) {
3392             return String(s).replace(/"/g, '&quot;');
3393         }
3394
3395         function isArray$3 (obj) { return toStr$8(obj) === '[object Array]' }
3396         function isDate (obj) { return toStr$8(obj) === '[object Date]' }
3397         function isRegExp (obj) { return toStr$8(obj) === '[object RegExp]' }
3398         function isError (obj) { return toStr$8(obj) === '[object Error]' }
3399         function isSymbol$3 (obj) { return toStr$8(obj) === '[object Symbol]' }
3400         function isString$1 (obj) { return toStr$8(obj) === '[object String]' }
3401         function isNumber (obj) { return toStr$8(obj) === '[object Number]' }
3402         function isBoolean (obj) { return toStr$8(obj) === '[object Boolean]' }
3403
3404         var hasOwn = Object.prototype.hasOwnProperty || function (key) { return key in this; };
3405         function has$1 (obj, key) {
3406             return hasOwn.call(obj, key);
3407         }
3408
3409         function toStr$8 (obj) {
3410             return objectToString$1.call(obj);
3411         }
3412
3413         function nameOf (f) {
3414             if (f.name) return f.name;
3415             var m = String(f).match(/^function\s*([\w$]+)/);
3416             if (m) return m[1];
3417         }
3418
3419         function indexOf$2 (xs, x) {
3420             if (xs.indexOf) return xs.indexOf(x);
3421             for (var i = 0, l = xs.length; i < l; i++) {
3422                 if (xs[i] === x) return i;
3423             }
3424             return -1;
3425         }
3426
3427         function isMap (x) {
3428             if (!mapSize) {
3429                 return false;
3430             }
3431             try {
3432                 mapSize.call(x);
3433                 try {
3434                     setSize.call(x);
3435                 } catch (s) {
3436                     return true;
3437                 }
3438                 return x instanceof Map; // core-js workaround, pre-v2.5.0
3439             } catch (e) {}
3440             return false;
3441         }
3442
3443         function isSet (x) {
3444             if (!setSize) {
3445                 return false;
3446             }
3447             try {
3448                 setSize.call(x);
3449                 try {
3450                     mapSize.call(x);
3451                 } catch (m) {
3452                     return true;
3453                 }
3454                 return x instanceof Set; // core-js workaround, pre-v2.5.0
3455             } catch (e) {}
3456             return false;
3457         }
3458
3459         function isElement (x) {
3460             if (!x || typeof x !== 'object') return false;
3461             if (typeof HTMLElement !== 'undefined' && x instanceof HTMLElement) {
3462                 return true;
3463             }
3464             return typeof x.nodeName === 'string'
3465                 && typeof x.getAttribute === 'function'
3466             ;
3467         }
3468
3469         function inspectString (str) {
3470             var s = str.replace(/(['\\])/g, '\\$1').replace(/[\x00-\x1f]/g, lowbyte);
3471             return "'" + s + "'";
3472         }
3473
3474         function lowbyte (c) {
3475             var n = c.charCodeAt(0);
3476             var x = { 8: 'b', 9: 't', 10: 'n', 12: 'f', 13: 'r' }[n];
3477             if (x) return '\\' + x;
3478             return '\\x' + (n < 0x10 ? '0' : '') + n.toString(16);
3479         }
3480
3481         function markBoxed (str) {
3482             return 'Object(' + str + ')';
3483         }
3484
3485         function collectionOf (type, size, entries) {
3486             return type + ' (' + size + ') {' + entries.join(', ') + '}';
3487         }
3488
3489         function arrObjKeys (obj, inspect) {
3490             var isArr = isArray$3(obj);
3491             var xs = [];
3492             if (isArr) {
3493                 xs.length = obj.length;
3494                 for (var i = 0; i < obj.length; i++) {
3495                     xs[i] = has$1(obj, i) ? inspect(obj[i], obj) : '';
3496                 }
3497             }
3498             for (var key in obj) {
3499                 if (!has$1(obj, key)) continue;
3500                 if (isArr && String(Number(key)) === key && key < obj.length) continue;
3501                 if (/[^\w$]/.test(key)) {
3502                     xs.push(inspect(key, obj) + ': ' + inspect(obj[key], obj));
3503                 } else {
3504                     xs.push(key + ': ' + inspect(obj[key], obj));
3505                 }
3506             }
3507             return xs;
3508         }
3509
3510         var $TypeError$8 = GetIntrinsic('%TypeError%');
3511
3512
3513
3514
3515
3516
3517         /**
3518          * 7.3.1 Get (O, P) - https://ecma-international.org/ecma-262/6.0/#sec-get-o-p
3519          * 1. Assert: Type(O) is Object.
3520          * 2. Assert: IsPropertyKey(P) is true.
3521          * 3. Return O.[[Get]](P, O).
3522          */
3523
3524         var Get = function Get(O, P) {
3525                 // 7.3.1.1
3526                 if (Type$1(O) !== 'Object') {
3527                         throw new $TypeError$8('Assertion failed: Type(O) is not Object');
3528                 }
3529                 // 7.3.1.2
3530                 if (!IsPropertyKey(P)) {
3531                         throw new $TypeError$8('Assertion failed: IsPropertyKey(P) is not true, got ' + objectInspect(P));
3532                 }
3533                 // 7.3.1.3
3534                 return O[P];
3535         };
3536
3537         var $TypeError$9 = GetIntrinsic('%TypeError%');
3538
3539         var isPropertyDescriptor = function IsPropertyDescriptor(ES, Desc) {
3540                 if (ES.Type(Desc) !== 'Object') {
3541                         return false;
3542                 }
3543                 var allowed = {
3544                         '[[Configurable]]': true,
3545                         '[[Enumerable]]': true,
3546                         '[[Get]]': true,
3547                         '[[Set]]': true,
3548                         '[[Value]]': true,
3549                         '[[Writable]]': true
3550                 };
3551
3552                 for (var key in Desc) { // eslint-disable-line no-restricted-syntax
3553                         if (src(Desc, key) && !allowed[key]) {
3554                                 return false;
3555                         }
3556                 }
3557
3558                 if (ES.IsDataDescriptor(Desc) && ES.IsAccessorDescriptor(Desc)) {
3559                         throw new $TypeError$9('Property Descriptors may not be both accessor and data descriptors');
3560                 }
3561                 return true;
3562         };
3563
3564         // https://www.ecma-international.org/ecma-262/6.0/#sec-isaccessordescriptor
3565
3566         var IsAccessorDescriptor = function IsAccessorDescriptor(Desc) {
3567                 if (typeof Desc === 'undefined') {
3568                         return false;
3569                 }
3570
3571                 assertRecord(Type$1, 'Property Descriptor', 'Desc', Desc);
3572
3573                 if (!src(Desc, '[[Get]]') && !src(Desc, '[[Set]]')) {
3574                         return false;
3575                 }
3576
3577                 return true;
3578         };
3579
3580         var $TypeError$a = GetIntrinsic('%TypeError%');
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593         // https://www.ecma-international.org/ecma-262/6.0/#sec-definepropertyorthrow
3594
3595         var DefinePropertyOrThrow = function DefinePropertyOrThrow(O, P, desc) {
3596                 if (Type$1(O) !== 'Object') {
3597                         throw new $TypeError$a('Assertion failed: Type(O) is not Object');
3598                 }
3599
3600                 if (!IsPropertyKey(P)) {
3601                         throw new $TypeError$a('Assertion failed: IsPropertyKey(P) is not true');
3602                 }
3603
3604                 var Desc = isPropertyDescriptor({
3605                         Type: Type$1,
3606                         IsDataDescriptor: IsDataDescriptor,
3607                         IsAccessorDescriptor: IsAccessorDescriptor
3608                 }, desc) ? desc : ToPropertyDescriptor(desc);
3609                 if (!isPropertyDescriptor({
3610                         Type: Type$1,
3611                         IsDataDescriptor: IsDataDescriptor,
3612                         IsAccessorDescriptor: IsAccessorDescriptor
3613                 }, Desc)) {
3614                         throw new $TypeError$a('Assertion failed: Desc is not a valid Property Descriptor');
3615                 }
3616
3617                 return DefineOwnProperty(
3618                         IsDataDescriptor,
3619                         SameValue,
3620                         FromPropertyDescriptor,
3621                         O,
3622                         P,
3623                         Desc
3624                 );
3625         };
3626
3627         var IsConstructor = createCommonjsModule(function (module) {
3628
3629
3630
3631         var $construct = GetIntrinsic('%Reflect.construct%', true);
3632
3633         var DefinePropertyOrThrow$1 = DefinePropertyOrThrow;
3634         try {
3635                 DefinePropertyOrThrow$1({}, '', { '[[Get]]': function () {} });
3636         } catch (e) {
3637                 // Accessor properties aren't supported
3638                 DefinePropertyOrThrow$1 = null;
3639         }
3640
3641         // https://www.ecma-international.org/ecma-262/6.0/#sec-isconstructor
3642
3643         if (DefinePropertyOrThrow$1 && $construct) {
3644                 var isConstructorMarker = {};
3645                 var badArrayLike = {};
3646                 DefinePropertyOrThrow$1(badArrayLike, 'length', {
3647                         '[[Get]]': function () {
3648                                 throw isConstructorMarker;
3649                         },
3650                         '[[Enumerable]]': true
3651                 });
3652
3653                 module.exports = function IsConstructor(argument) {
3654                         try {
3655                                 // `Reflect.construct` invokes `IsConstructor(target)` before `Get(args, 'length')`:
3656                                 $construct(argument, badArrayLike);
3657                         } catch (err) {
3658                                 return err === isConstructorMarker;
3659                         }
3660                 };
3661         } else {
3662                 module.exports = function IsConstructor(argument) {
3663                         // unfortunately there's no way to truly check this without try/catch `new argument` in old environments
3664                         return typeof argument === 'function' && !!argument.prototype;
3665                 };
3666         }
3667         });
3668
3669         var $String = GetIntrinsic('%String%');
3670         var $TypeError$b = GetIntrinsic('%TypeError%');
3671
3672         // https://www.ecma-international.org/ecma-262/6.0/#sec-tostring
3673
3674         var ToString = function ToString(argument) {
3675                 if (typeof argument === 'symbol') {
3676                         throw new $TypeError$b('Cannot convert a Symbol value to a string');
3677                 }
3678                 return $String(argument);
3679         };
3680
3681         var hasToStringTag$3 = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol';
3682         var toStr$9 = Object.prototype.toString;
3683
3684         var isStandardArguments = function isArguments(value) {
3685                 if (hasToStringTag$3 && value && typeof value === 'object' && Symbol.toStringTag in value) {
3686                         return false;
3687                 }
3688                 return toStr$9.call(value) === '[object Arguments]';
3689         };
3690
3691         var isLegacyArguments = function isArguments(value) {
3692                 if (isStandardArguments(value)) {
3693                         return true;
3694                 }
3695                 return value !== null &&
3696                         typeof value === 'object' &&
3697                         typeof value.length === 'number' &&
3698                         value.length >= 0 &&
3699                         toStr$9.call(value) !== '[object Array]' &&
3700                         toStr$9.call(value.callee) === '[object Function]';
3701         };
3702
3703         var supportsStandardArguments = (function () {
3704                 return isStandardArguments(arguments);
3705         }());
3706
3707         isStandardArguments.isLegacyArguments = isLegacyArguments; // for tests
3708
3709         var isArguments$2 = supportsStandardArguments ? isStandardArguments : isLegacyArguments;
3710
3711         var toString = {}.toString;
3712
3713         var isarray = Array.isArray || function (arr) {
3714           return toString.call(arr) == '[object Array]';
3715         };
3716
3717         var strValue = String.prototype.valueOf;
3718         var tryStringObject = function tryStringObject(value) {
3719                 try {
3720                         strValue.call(value);
3721                         return true;
3722                 } catch (e) {
3723                         return false;
3724                 }
3725         };
3726         var toStr$a = Object.prototype.toString;
3727         var strClass = '[object String]';
3728         var hasToStringTag$4 = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol';
3729
3730         var isString$2 = function isString(value) {
3731                 if (typeof value === 'string') {
3732                         return true;
3733                 }
3734                 if (typeof value !== 'object') {
3735                         return false;
3736                 }
3737                 return hasToStringTag$4 ? tryStringObject(value) : toStr$a.call(value) === strClass;
3738         };
3739
3740         var $Map = typeof Map === 'function' && Map.prototype ? Map : null;
3741         var $Set = typeof Set === 'function' && Set.prototype ? Set : null;
3742
3743         var exported;
3744
3745         if (!$Map) {
3746                 // eslint-disable-next-line no-unused-vars
3747                 exported = function isMap(x) {
3748                         // `Map` is not present in this environment.
3749                         return false;
3750                 };
3751         }
3752
3753         var $mapHas = $Map ? Map.prototype.has : null;
3754         var $setHas = $Set ? Set.prototype.has : null;
3755         if (!exported && !$mapHas) {
3756                 // eslint-disable-next-line no-unused-vars
3757                 exported = function isMap(x) {
3758                         // `Map` does not have a `has` method
3759                         return false;
3760                 };
3761         }
3762
3763         var isMap$1 = exported || function isMap(x) {
3764                 if (!x || typeof x !== 'object') {
3765                         return false;
3766                 }
3767                 try {
3768                         $mapHas.call(x);
3769                         if ($setHas) {
3770                                 try {
3771                                         $setHas.call(x);
3772                                 } catch (e) {
3773                                         return true;
3774                                 }
3775                         }
3776                         return x instanceof $Map; // core-js workaround, pre-v2.5.0
3777                 } catch (e) {}
3778                 return false;
3779         };
3780
3781         var $Map$1 = typeof Map === 'function' && Map.prototype ? Map : null;
3782         var $Set$1 = typeof Set === 'function' && Set.prototype ? Set : null;
3783
3784         var exported$1;
3785
3786         if (!$Set$1) {
3787                 // eslint-disable-next-line no-unused-vars
3788                 exported$1 = function isSet(x) {
3789                         // `Set` is not present in this environment.
3790                         return false;
3791                 };
3792         }
3793
3794         var $mapHas$1 = $Map$1 ? Map.prototype.has : null;
3795         var $setHas$1 = $Set$1 ? Set.prototype.has : null;
3796         if (!exported$1 && !$setHas$1) {
3797                 // eslint-disable-next-line no-unused-vars
3798                 exported$1 = function isSet(x) {
3799                         // `Set` does not have a `has` method
3800                         return false;
3801                 };
3802         }
3803
3804         var isSet$1 = exported$1 || function isSet(x) {
3805                 if (!x || typeof x !== 'object') {
3806                         return false;
3807                 }
3808                 try {
3809                         $setHas$1.call(x);
3810                         if ($mapHas$1) {
3811                                 try {
3812                                         $mapHas$1.call(x);
3813                                 } catch (e) {
3814                                         return true;
3815                                 }
3816                         }
3817                         return x instanceof $Set$1; // core-js workaround, pre-v2.5.0
3818                 } catch (e) {}
3819                 return false;
3820         };
3821
3822         var esGetIterator = createCommonjsModule(function (module) {
3823
3824         /* eslint global-require: 0 */
3825         // the code is structured this way so that bundlers can
3826         // alias out `has-symbols` to `() => true` or `() => false` if your target
3827         // environments' Symbol capabilities are known, and then use
3828         // dead code elimination on the rest of this module.
3829         //
3830         // Similarly, `isarray` can be aliased to `Array.isArray` if
3831         // available in all target environments.
3832
3833
3834
3835         if (hasSymbols$1() || shams()) {
3836                 var $iterator = Symbol.iterator;
3837                 // Symbol is available natively or shammed
3838                 // natively:
3839                 //  - Chrome >= 38
3840                 //  - Edge 12-14?, Edge >= 15 for sure
3841                 //  - FF >= 36
3842                 //  - Safari >= 9
3843                 //  - node >= 0.12
3844                 module.exports = function getIterator(iterable) {
3845                         // alternatively, `iterable[$iterator]?.()`
3846                         if (iterable != null && typeof iterable[$iterator] !== 'undefined') {
3847                                 return iterable[$iterator]();
3848                         }
3849                         if (isArguments$2(iterable)) {
3850                                 // arguments objects lack Symbol.iterator
3851                                 // - node 0.12
3852                                 return Array.prototype[$iterator].call(iterable);
3853                         }
3854                 };
3855         } else {
3856                 // Symbol is not available, native or shammed
3857                 var isArray = isarray;
3858                 var isString = isString$2;
3859                 var GetIntrinsic$1 = GetIntrinsic;
3860                 var $Map = GetIntrinsic$1('%Map%', true);
3861                 var $Set = GetIntrinsic$1('%Set%', true);
3862                 var callBound$1 = callBound;
3863                 var $arrayPush = callBound$1('Array.prototype.push');
3864                 var $charCodeAt = callBound$1('String.prototype.charCodeAt');
3865                 var $stringSlice = callBound$1('String.prototype.slice');
3866
3867                 var advanceStringIndex = function advanceStringIndex(S, index) {
3868                         var length = S.length;
3869                         if ((index + 1) >= length) {
3870                                 return index + 1;
3871                         }
3872
3873                         var first = $charCodeAt(S, index);
3874                         if (first < 0xD800 || first > 0xDBFF) {
3875                                 return index + 1;
3876                         }
3877
3878                         var second = $charCodeAt(S, index + 1);
3879                         if (second < 0xDC00 || second > 0xDFFF) {
3880                                 return index + 1;
3881                         }
3882
3883                         return index + 2;
3884                 };
3885
3886                 var getArrayIterator = function getArrayIterator(arraylike) {
3887                         var i = 0;
3888                         return {
3889                                 next: function next() {
3890                                         var done = i >= arraylike.length;
3891                                         var value;
3892                                         if (!done) {
3893                                                 value = arraylike[i];
3894                                                 i += 1;
3895                                         }
3896                                         return {
3897                                                 done: done,
3898                                                 value: value
3899                                         };
3900                                 }
3901                         };
3902                 };
3903
3904                 var getNonCollectionIterator = function getNonCollectionIterator(iterable) {
3905                         if (isArray(iterable) || isArguments$2(iterable)) {
3906                                 return getArrayIterator(iterable);
3907                         }
3908                         if (isString(iterable)) {
3909                                 var i = 0;
3910                                 return {
3911                                         next: function next() {
3912                                                 var nextIndex = advanceStringIndex(iterable, i);
3913                                                 var value = $stringSlice(iterable, i, nextIndex);
3914                                                 i = nextIndex;
3915                                                 return {
3916                                                         done: nextIndex > iterable.length,
3917                                                         value: value
3918                                                 };
3919                                         }
3920                                 };
3921                         }
3922                 };
3923
3924                 if (!$Map && !$Set) {
3925                         // the only language iterables are Array, String, arguments
3926                         // - Safari <= 6.0
3927                         // - Chrome < 38
3928                         // - node < 0.12
3929                         // - FF < 13
3930                         // - IE < 11
3931                         // - Edge < 11
3932
3933                         module.exports = getNonCollectionIterator;
3934                 } else {
3935                         // either Map or Set are available, but Symbol is not
3936                         // - es6-shim on an ES5 browser
3937                         // - Safari 6.2 (maybe 6.1?)
3938                         // - FF v[13, 36)
3939                         // - IE 11
3940                         // - Edge 11
3941                         // - Safari v[6, 9)
3942
3943                         var isMap = isMap$1;
3944                         var isSet = isSet$1;
3945
3946                         // Firefox >= 27, IE 11, Safari 6.2 - 9, Edge 11, es6-shim in older envs, all have forEach
3947                         var $mapForEach = callBound$1('Map.prototype.forEach', true);
3948                         var $setForEach = callBound$1('Set.prototype.forEach', true);
3949                         if (typeof process === 'undefined' || !process.versions || !process.versions.node) { // "if is not node"
3950
3951                                 // Firefox 17 - 26 has `.iterator()`, whose iterator `.next()` either
3952                                 // returns a value, or throws a StopIteration object. These browsers
3953                                 // do not have any other mechanism for iteration.
3954                                 var $mapIterator = callBound$1('Map.prototype.iterator', true);
3955                                 var $setIterator = callBound$1('Set.prototype.iterator', true);
3956                                 var getStopIterationIterator = function (iterator) {
3957                                         var done = false;
3958                                         return {
3959                                                 next: function next() {
3960                                                         try {
3961                                                                 return {
3962                                                                         done: done,
3963                                                                         value: done ? undefined : iterator.next()
3964                                                                 };
3965                                                         } catch (e) {
3966                                                                 done = true;
3967                                                                 return {
3968                                                                         done: true,
3969                                                                         value: undefined
3970                                                                 };
3971                                                         }
3972                                                 }
3973                                         };
3974                                 };
3975                         }
3976                         // Firefox 27-35, and some older es6-shim versions, use a string "@@iterator" property
3977                         // this returns a proper iterator object, so we should use it instead of forEach.
3978                         // newer es6-shim versions use a string "_es6-shim iterator_" property.
3979                         var $mapAtAtIterator = callBound$1('Map.prototype.@@iterator', true) || callBound$1('Map.prototype._es6-shim iterator_', true);
3980                         var $setAtAtIterator = callBound$1('Set.prototype.@@iterator', true) || callBound$1('Set.prototype._es6-shim iterator_', true);
3981
3982                         var getCollectionIterator = function getCollectionIterator(iterable) {
3983                                 if (isMap(iterable)) {
3984                                         if ($mapIterator) {
3985                                                 return getStopIterationIterator($mapIterator(iterable));
3986                                         }
3987                                         if ($mapAtAtIterator) {
3988                                                 return $mapAtAtIterator(iterable);
3989                                         }
3990                                         if ($mapForEach) {
3991                                                 var entries = [];
3992                                                 $mapForEach(iterable, function (v, k) {
3993                                                         $arrayPush(entries, [k, v]);
3994                                                 });
3995                                                 return getArrayIterator(entries);
3996                                         }
3997                                 }
3998                                 if (isSet(iterable)) {
3999                                         if ($setIterator) {
4000                                                 return getStopIterationIterator($setIterator(iterable));
4001                                         }
4002                                         if ($setAtAtIterator) {
4003                                                 return $setAtAtIterator(iterable);
4004                                         }
4005                                         if ($setForEach) {
4006                                                 var values = [];
4007                                                 $setForEach(iterable, function (v) {
4008                                                         $arrayPush(values, v);
4009                                                 });
4010                                                 return getArrayIterator(values);
4011                                         }
4012                                 }
4013                         };
4014
4015                         module.exports = function getIterator(iterable) {
4016                                 return getCollectionIterator(iterable) || getNonCollectionIterator(iterable);
4017                         };
4018                 }
4019         }
4020         });
4021
4022         var $TypeError$c = TypeError;
4023
4024         // eslint-disable-next-line consistent-return
4025         var iterateIterator = function iterateIterator(iterator) {
4026                 if (!iterator || typeof iterator.next !== 'function') {
4027                         throw new $TypeError$c('iterator must be an object with a `next` method');
4028                 }
4029                 if (arguments.length > 1) {
4030                         var callback = arguments[1];
4031                         if (typeof callback !== 'function') {
4032                                 throw new $TypeError$c('`callback`, if provided, must be a function');
4033                         }
4034                 }
4035                 var values = callback || [];
4036                 var result;
4037                 while ((result = iterator.next()) && !result.done) {
4038                         if (callback) {
4039                                 callback(result.value); // eslint-disable-line callback-return
4040                         } else {
4041                                 values.push(result.value);
4042                         }
4043                 }
4044                 if (!callback) {
4045                         return values;
4046                 }
4047         };
4048
4049         var $TypeError$d = TypeError;
4050
4051
4052         var iterateValue = function iterateValue(iterable) {
4053                 var iterator = esGetIterator(iterable);
4054                 if (!iterator) {
4055                         throw new $TypeError$d('non-iterable value provided');
4056                 }
4057                 if (arguments.length > 1) {
4058                         return iterateIterator(iterator, arguments[1]);
4059                 }
4060                 return iterateIterator(iterator);
4061         };
4062
4063         var implementation$5 = function from(items) {
4064                 var C = this;
4065                 if (items === null || typeof items === 'undefined') {
4066                         throw new TypeError('`Array.from` requires an array-like object, not `null` or `undefined`');
4067                 }
4068                 var mapFn, T;
4069                 if (typeof arguments[1] !== 'undefined') {
4070                         mapFn = arguments[1];
4071                         if (!IsCallable(mapFn)) {
4072                                 throw new TypeError('When provided, the second argument to `Array.from` must be a function');
4073                         }
4074                         if (arguments.length > 2) {
4075                                 T = arguments[2];
4076                         }
4077                 }
4078
4079                 var values;
4080                 try {
4081                         values = iterateValue(items);
4082                 } catch (e) {
4083                         values = items;
4084                 }
4085
4086                 var arrayLike = ToObject(values);
4087                 var len = ToLength(arrayLike.length);
4088                 var A = IsConstructor(C) ? ToObject(new C(len)) : new Array(len);
4089                 var k = 0;
4090                 var kValue, mappedValue;
4091
4092                 while (k < len) {
4093                         var Pk = ToString(k);
4094                         kValue = Get(arrayLike, Pk);
4095                         if (mapFn) {
4096                                 mappedValue = typeof T === 'undefined' ? mapFn(kValue, k) : Call(mapFn, T, [kValue, k]);
4097                         } else {
4098                                 mappedValue = kValue;
4099                         }
4100                         CreateDataPropertyOrThrow(A, Pk, mappedValue);
4101                         k += 1;
4102                 }
4103                 A.length = len;
4104                 return A;
4105         };
4106
4107         var tryCall = function (fn) {
4108                 try {
4109                         return fn();
4110                 } catch (e) {
4111                         return false;
4112                 }
4113         };
4114
4115         var polyfill$8 = function getPolyfill() {
4116                 if (IsCallable(Array.from)) {
4117                         var handlesUndefMapper = tryCall(function () {
4118                                 // Microsoft Edge v0.11 throws if the mapFn argument is *provided* but undefined,
4119                                 // but the spec doesn't care if it's provided or not - undefined doesn't throw.
4120                                 return Array.from([0], undefined);
4121                         });
4122                         if (!handlesUndefMapper) {
4123                                 var origArrayFrom = Array.from;
4124                                 return function from(items) {
4125                                         /* eslint no-invalid-this: 0 */
4126                                         if (arguments.length > 1 && typeof arguments[1] !== 'undefined') {
4127                                                 return Call(origArrayFrom, this, arguments);
4128                                         } else {
4129                                                 return Call(origArrayFrom, this, [items]);
4130                                         }
4131                                 };
4132                         }
4133                         var implemented = tryCall(function () {
4134                                 // Detects a Firefox bug in v32
4135                                 // https://bugzilla.mozilla.org/show_bug.cgi?id=1063993
4136                                 return Array.from({ 'length': -1 }) === 0;
4137                         })
4138                         && tryCall(function () {
4139                                 // Detects a bug in Webkit nightly r181886
4140                                 var arr = Array.from([0].entries());
4141                                 return arr.length === 1 && IsArray(arr[0]) && arr[0][0] === 0 && arr[0][1] === 0;
4142                         })
4143                         && tryCall(function () {
4144                                 return Array.from({ 'length': -Infinity });
4145                         });
4146                         if (implemented) {
4147                                 return Array.from;
4148                         }
4149                 }
4150
4151                 return implementation$5;
4152         };
4153
4154         var shim$a = function shimArrayFrom() {
4155                 var polyfill = polyfill$8();
4156
4157                 defineProperties_1(Array, { 'from': polyfill }, {
4158                         'from': function () {
4159                                 return Array.from !== polyfill;
4160                         }
4161                 });
4162
4163                 return polyfill;
4164         };
4165
4166         var polyfill$9 = polyfill$8();
4167
4168         // eslint-disable-next-line no-unused-vars
4169         var boundFromShim = function from(items) {
4170                 // eslint-disable-next-line no-invalid-this
4171                 return polyfill$9.apply(this || Array, arguments);
4172         };
4173
4174         defineProperties_1(boundFromShim, {
4175                 'getPolyfill': polyfill$8,
4176                 'implementation': implementation$5,
4177                 'shim': shim$a
4178         });
4179
4180         var array_from = boundFromShim;
4181
4182         var $isEnumerable$2 = callBound('Object.prototype.propertyIsEnumerable');
4183
4184         var implementation$6 = function values(O) {
4185                 var obj = RequireObjectCoercible(O);
4186                 var vals = [];
4187                 for (var key in obj) {
4188                         if (src(obj, key) && $isEnumerable$2(obj, key)) {
4189                                 vals.push(obj[key]);
4190                         }
4191                 }
4192                 return vals;
4193         };
4194
4195         var polyfill$a = function getPolyfill() {
4196                 return typeof Object.values === 'function' ? Object.values : implementation$6;
4197         };
4198
4199         var shim$b = function shimValues() {
4200                 var polyfill = polyfill$a();
4201                 defineProperties_1(Object, { values: polyfill }, {
4202                         values: function testValues() {
4203                                 return Object.values !== polyfill;
4204                         }
4205                 });
4206                 return polyfill;
4207         };
4208
4209         var polyfill$b = polyfill$a();
4210
4211         defineProperties_1(polyfill$b, {
4212                 getPolyfill: polyfill$a,
4213                 implementation: implementation$6,
4214                 shim: shim$b
4215         });
4216
4217         var object_values = polyfill$b;
4218
4219         // modified from https://github.com/es-shims/es6-shim
4220
4221
4222         var canBeObject = function (obj) {
4223                 return typeof obj !== 'undefined' && obj !== null;
4224         };
4225         var hasSymbols$5 = shams();
4226         var toObject = Object;
4227         var push = functionBind.call(Function.call, Array.prototype.push);
4228         var propIsEnumerable = functionBind.call(Function.call, Object.prototype.propertyIsEnumerable);
4229         var originalGetSymbols = hasSymbols$5 ? Object.getOwnPropertySymbols : null;
4230
4231         var implementation$7 = function assign(target, source1) {
4232                 if (!canBeObject(target)) { throw new TypeError('target must be an object'); }
4233                 var objTarget = toObject(target);
4234                 var s, source, i, props, syms, value, key;
4235                 for (s = 1; s < arguments.length; ++s) {
4236                         source = toObject(arguments[s]);
4237                         props = objectKeys(source);
4238                         var getSymbols = hasSymbols$5 && (Object.getOwnPropertySymbols || originalGetSymbols);
4239                         if (getSymbols) {
4240                                 syms = getSymbols(source);
4241                                 for (i = 0; i < syms.length; ++i) {
4242                                         key = syms[i];
4243                                         if (propIsEnumerable(source, key)) {
4244                                                 push(props, key);
4245                                         }
4246                                 }
4247                         }
4248                         for (i = 0; i < props.length; ++i) {
4249                                 key = props[i];
4250                                 value = source[key];
4251                                 if (propIsEnumerable(source, key)) {
4252                                         objTarget[key] = value;
4253                                 }
4254                         }
4255                 }
4256                 return objTarget;
4257         };
4258
4259         var lacksProperEnumerationOrder = function () {
4260                 if (!Object.assign) {
4261                         return false;
4262                 }
4263                 // v8, specifically in node 4.x, has a bug with incorrect property enumeration order
4264                 // note: this does not detect the bug unless there's 20 characters
4265                 var str = 'abcdefghijklmnopqrst';
4266                 var letters = str.split('');
4267                 var map = {};
4268                 for (var i = 0; i < letters.length; ++i) {
4269                         map[letters[i]] = letters[i];
4270                 }
4271                 var obj = Object.assign({}, map);
4272                 var actual = '';
4273                 for (var k in obj) {
4274                         actual += k;
4275                 }
4276                 return str !== actual;
4277         };
4278
4279         var assignHasPendingExceptions = function () {
4280                 if (!Object.assign || !Object.preventExtensions) {
4281                         return false;
4282                 }
4283                 // Firefox 37 still has "pending exception" logic in its Object.assign implementation,
4284                 // which is 72% slower than our shim, and Firefox 40's native implementation.
4285                 var thrower = Object.preventExtensions({ 1: 2 });
4286                 try {
4287                         Object.assign(thrower, 'xy');
4288                 } catch (e) {
4289                         return thrower[1] === 'y';
4290                 }
4291                 return false;
4292         };
4293
4294         var polyfill$c = function getPolyfill() {
4295                 if (!Object.assign) {
4296                         return implementation$7;
4297                 }
4298                 if (lacksProperEnumerationOrder()) {
4299                         return implementation$7;
4300                 }
4301                 if (assignHasPendingExceptions()) {
4302                         return implementation$7;
4303                 }
4304                 return Object.assign;
4305         };
4306
4307         var shim$c = function shimAssign() {
4308                 var polyfill = polyfill$c();
4309                 defineProperties_1(
4310                         Object,
4311                         { assign: polyfill },
4312                         { assign: function () { return Object.assign !== polyfill; } }
4313                 );
4314                 return polyfill;
4315         };
4316
4317         var polyfill$d = polyfill$c();
4318
4319         defineProperties_1(polyfill$d, {
4320                 getPolyfill: polyfill$c,
4321                 implementation: implementation$7,
4322                 shim: shim$c
4323         });
4324
4325         var object_assign = polyfill$d;
4326
4327         /**
4328          * @this {Promise}
4329          */
4330         function finallyConstructor(callback) {
4331           var constructor = this.constructor;
4332           return this.then(
4333             function(value) {
4334               // @ts-ignore
4335               return constructor.resolve(callback()).then(function() {
4336                 return value;
4337               });
4338             },
4339             function(reason) {
4340               // @ts-ignore
4341               return constructor.resolve(callback()).then(function() {
4342                 // @ts-ignore
4343                 return constructor.reject(reason);
4344               });
4345             }
4346           );
4347         }
4348
4349         // Store setTimeout reference so promise-polyfill will be unaffected by
4350         // other code modifying setTimeout (like sinon.useFakeTimers())
4351         var setTimeoutFunc = setTimeout;
4352
4353         function isArray$4(x) {
4354           return Boolean(x && typeof x.length !== 'undefined');
4355         }
4356
4357         function noop$1() {}
4358
4359         // Polyfill for Function.prototype.bind
4360         function bind$2(fn, thisArg) {
4361           return function() {
4362             fn.apply(thisArg, arguments);
4363           };
4364         }
4365
4366         /**
4367          * @constructor
4368          * @param {Function} fn
4369          */
4370         function Promise$1(fn) {
4371           if (!(this instanceof Promise$1))
4372             throw new TypeError('Promises must be constructed via new');
4373           if (typeof fn !== 'function') throw new TypeError('not a function');
4374           /** @type {!number} */
4375           this._state = 0;
4376           /** @type {!boolean} */
4377           this._handled = false;
4378           /** @type {Promise|undefined} */
4379           this._value = undefined;
4380           /** @type {!Array<!Function>} */
4381           this._deferreds = [];
4382
4383           doResolve(fn, this);
4384         }
4385
4386         function handle(self, deferred) {
4387           while (self._state === 3) {
4388             self = self._value;
4389           }
4390           if (self._state === 0) {
4391             self._deferreds.push(deferred);
4392             return;
4393           }
4394           self._handled = true;
4395           Promise$1._immediateFn(function() {
4396             var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;
4397             if (cb === null) {
4398               (self._state === 1 ? resolve : reject)(deferred.promise, self._value);
4399               return;
4400             }
4401             var ret;
4402             try {
4403               ret = cb(self._value);
4404             } catch (e) {
4405               reject(deferred.promise, e);
4406               return;
4407             }
4408             resolve(deferred.promise, ret);
4409           });
4410         }
4411
4412         function resolve(self, newValue) {
4413           try {
4414             // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
4415             if (newValue === self)
4416               throw new TypeError('A promise cannot be resolved with itself.');
4417             if (
4418               newValue &&
4419               (typeof newValue === 'object' || typeof newValue === 'function')
4420             ) {
4421               var then = newValue.then;
4422               if (newValue instanceof Promise$1) {
4423                 self._state = 3;
4424                 self._value = newValue;
4425                 finale(self);
4426                 return;
4427               } else if (typeof then === 'function') {
4428                 doResolve(bind$2(then, newValue), self);
4429                 return;
4430               }
4431             }
4432             self._state = 1;
4433             self._value = newValue;
4434             finale(self);
4435           } catch (e) {
4436             reject(self, e);
4437           }
4438         }
4439
4440         function reject(self, newValue) {
4441           self._state = 2;
4442           self._value = newValue;
4443           finale(self);
4444         }
4445
4446         function finale(self) {
4447           if (self._state === 2 && self._deferreds.length === 0) {
4448             Promise$1._immediateFn(function() {
4449               if (!self._handled) {
4450                 Promise$1._unhandledRejectionFn(self._value);
4451               }
4452             });
4453           }
4454
4455           for (var i = 0, len = self._deferreds.length; i < len; i++) {
4456             handle(self, self._deferreds[i]);
4457           }
4458           self._deferreds = null;
4459         }
4460
4461         /**
4462          * @constructor
4463          */
4464         function Handler(onFulfilled, onRejected, promise) {
4465           this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
4466           this.onRejected = typeof onRejected === 'function' ? onRejected : null;
4467           this.promise = promise;
4468         }
4469
4470         /**
4471          * Take a potentially misbehaving resolver function and make sure
4472          * onFulfilled and onRejected are only called once.
4473          *
4474          * Makes no guarantees about asynchrony.
4475          */
4476         function doResolve(fn, self) {
4477           var done = false;
4478           try {
4479             fn(
4480               function(value) {
4481                 if (done) return;
4482                 done = true;
4483                 resolve(self, value);
4484               },
4485               function(reason) {
4486                 if (done) return;
4487                 done = true;
4488                 reject(self, reason);
4489               }
4490             );
4491           } catch (ex) {
4492             if (done) return;
4493             done = true;
4494             reject(self, ex);
4495           }
4496         }
4497
4498         Promise$1.prototype['catch'] = function(onRejected) {
4499           return this.then(null, onRejected);
4500         };
4501
4502         Promise$1.prototype.then = function(onFulfilled, onRejected) {
4503           // @ts-ignore
4504           var prom = new this.constructor(noop$1);
4505
4506           handle(this, new Handler(onFulfilled, onRejected, prom));
4507           return prom;
4508         };
4509
4510         Promise$1.prototype['finally'] = finallyConstructor;
4511
4512         Promise$1.all = function(arr) {
4513           return new Promise$1(function(resolve, reject) {
4514             if (!isArray$4(arr)) {
4515               return reject(new TypeError('Promise.all accepts an array'));
4516             }
4517
4518             var args = Array.prototype.slice.call(arr);
4519             if (args.length === 0) return resolve([]);
4520             var remaining = args.length;
4521
4522             function res(i, val) {
4523               try {
4524                 if (val && (typeof val === 'object' || typeof val === 'function')) {
4525                   var then = val.then;
4526                   if (typeof then === 'function') {
4527                     then.call(
4528                       val,
4529                       function(val) {
4530                         res(i, val);
4531                       },
4532                       reject
4533                     );
4534                     return;
4535                   }
4536                 }
4537                 args[i] = val;
4538                 if (--remaining === 0) {
4539                   resolve(args);
4540                 }
4541               } catch (ex) {
4542                 reject(ex);
4543               }
4544             }
4545
4546             for (var i = 0; i < args.length; i++) {
4547               res(i, args[i]);
4548             }
4549           });
4550         };
4551
4552         Promise$1.resolve = function(value) {
4553           if (value && typeof value === 'object' && value.constructor === Promise$1) {
4554             return value;
4555           }
4556
4557           return new Promise$1(function(resolve) {
4558             resolve(value);
4559           });
4560         };
4561
4562         Promise$1.reject = function(value) {
4563           return new Promise$1(function(resolve, reject) {
4564             reject(value);
4565           });
4566         };
4567
4568         Promise$1.race = function(arr) {
4569           return new Promise$1(function(resolve, reject) {
4570             if (!isArray$4(arr)) {
4571               return reject(new TypeError('Promise.race accepts an array'));
4572             }
4573
4574             for (var i = 0, len = arr.length; i < len; i++) {
4575               Promise$1.resolve(arr[i]).then(resolve, reject);
4576             }
4577           });
4578         };
4579
4580         // Use polyfill for setImmediate for performance gains
4581         Promise$1._immediateFn =
4582           // @ts-ignore
4583           (typeof setImmediate === 'function' &&
4584             function(fn) {
4585               // @ts-ignore
4586               setImmediate(fn);
4587             }) ||
4588           function(fn) {
4589             setTimeoutFunc(fn, 0);
4590           };
4591
4592         Promise$1._unhandledRejectionFn = function _unhandledRejectionFn(err) {
4593           if (typeof console !== 'undefined' && console) {
4594             console.warn('Possible Unhandled Promise Rejection:', err); // eslint-disable-line no-console
4595           }
4596         };
4597
4598         /** @suppress {undefinedVars} */
4599         var globalNS = (function() {
4600           // the only reliable means to get the global object is
4601           // `Function('return this')()`
4602           // However, this causes CSP violations in Chrome apps.
4603           if (typeof self !== 'undefined') {
4604             return self;
4605           }
4606           if (typeof window !== 'undefined') {
4607             return window;
4608           }
4609           if (typeof global !== 'undefined') {
4610             return global;
4611           }
4612           throw new Error('unable to locate global object');
4613         })();
4614
4615         if (!('Promise' in globalNS)) {
4616           globalNS['Promise'] = Promise$1;
4617         } else if (!globalNS.Promise.prototype['finally']) {
4618           globalNS.Promise.prototype['finally'] = finallyConstructor;
4619         }
4620
4621         var polyfill$e = /*#__PURE__*/Object.freeze({
4622                 __proto__: null
4623         });
4624
4625         var setAsap = createCommonjsModule(function (module) {
4626         (function (thisVar, undefined$1) {
4627                 var main = (typeof window === 'object' && window) || (typeof commonjsGlobal === 'object' && commonjsGlobal) ||
4628                         typeof self === 'object' && self || thisVar;
4629
4630                 var hasSetImmediate = typeof setImmediate === 'function';
4631                 var hasNextTick = typeof process === 'object' && !!process && typeof process.nextTick === 'function';
4632                 var index = 0;
4633
4634                 function getNewIndex() {
4635                         if (index === 9007199254740991) {
4636                                 return 0;
4637                         }
4638                         return ++index;
4639                 }
4640
4641                 var setAsap = (function () {
4642                         var hiddenDiv, scriptEl, timeoutFn, callbacks;
4643
4644                         // Modern browsers, fastest async
4645                         if (main.MutationObserver) {
4646                                 return function setAsap(callback) {
4647                                         hiddenDiv = document.createElement("div");
4648                                         (new MutationObserver(function() {
4649                                                 callback();
4650                                                 hiddenDiv = null;
4651                                         })).observe(hiddenDiv, { attributes: true });
4652                                         hiddenDiv.setAttribute('i', '1');
4653                                 };
4654
4655                         // Browsers that support postMessage
4656                         } else if (!hasSetImmediate && main.postMessage && !main.importScripts && main.addEventListener) {
4657
4658                                 var MESSAGE_PREFIX = "com.setImmediate" + Math.random();
4659                                 callbacks = {};
4660
4661                                 var onGlobalMessage = function (event) {
4662                                         if (event.source === main && event.data.indexOf(MESSAGE_PREFIX) === 0) {
4663                                                 var i = +event.data.split(':')[1];
4664                                                 callbacks[i]();
4665                                                 delete callbacks[i];
4666                                         }
4667                                 };
4668
4669                                 main.addEventListener("message", onGlobalMessage, false);
4670
4671                                 return function setAsap(callback) {
4672                                         var i = getNewIndex();
4673                                         callbacks[i] = callback;
4674                                         main.postMessage(MESSAGE_PREFIX + ':' + i, "*");
4675                                 };
4676
4677                                 // IE browsers without postMessage
4678                         } else if (!hasSetImmediate && main.document && 'onreadystatechange' in document.createElement('script')) {
4679
4680                                 return function setAsap(callback) {
4681                                         scriptEl = document.createElement("script");
4682                                         scriptEl.onreadystatechange = function onreadystatechange() {
4683                                                 scriptEl.onreadystatechange = null;
4684                                                 scriptEl.parentNode.removeChild(scriptEl);
4685                                                 scriptEl = null;
4686                                                 callback();
4687                                         };
4688                                         document.body.appendChild(scriptEl);
4689                                 };
4690
4691                         // All other browsers and node
4692                         } else {
4693
4694                                 timeoutFn = (hasSetImmediate && setImmediate) || (hasNextTick && process.nextTick) || setTimeout;
4695                                 return function setAsap(callback) {
4696                                         timeoutFn(callback);
4697                                 };
4698                         }
4699
4700                 })();
4701
4702                 if ( module.exports) {
4703                         module.exports = setAsap;
4704                 } else if (typeof commonjsRequire !== 'undefined' && commonjsRequire.amd) {
4705                         undefined$1(function () {
4706                                 return setAsap;
4707                         });
4708                 } else {
4709                         main.setAsap = setAsap;
4710                 }
4711         })(commonjsGlobal);
4712         });
4713
4714         var performanceNow = createCommonjsModule(function (module) {
4715         // Generated by CoffeeScript 1.12.2
4716         (function() {
4717           var getNanoSeconds, hrtime, loadTime, moduleLoadTime, nodeLoadTime, upTime;
4718
4719           if ((typeof performance !== "undefined" && performance !== null) && performance.now) {
4720             module.exports = function() {
4721               return performance.now();
4722             };
4723           } else if ((typeof process !== "undefined" && process !== null) && process.hrtime) {
4724             module.exports = function() {
4725               return (getNanoSeconds() - nodeLoadTime) / 1e6;
4726             };
4727             hrtime = process.hrtime;
4728             getNanoSeconds = function() {
4729               var hr;
4730               hr = hrtime();
4731               return hr[0] * 1e9 + hr[1];
4732             };
4733             moduleLoadTime = getNanoSeconds();
4734             upTime = process.uptime() * 1e9;
4735             nodeLoadTime = moduleLoadTime - upTime;
4736           } else if (Date.now) {
4737             module.exports = function() {
4738               return Date.now() - loadTime;
4739             };
4740             loadTime = Date.now();
4741           } else {
4742             module.exports = function() {
4743               return new Date().getTime() - loadTime;
4744             };
4745             loadTime = new Date().getTime();
4746           }
4747
4748         }).call(commonjsGlobal);
4749
4750
4751         });
4752
4753         var root = typeof window === 'undefined' ? commonjsGlobal : window
4754           , vendors = ['moz', 'webkit']
4755           , suffix = 'AnimationFrame'
4756           , raf = root['request' + suffix]
4757           , caf = root['cancel' + suffix] || root['cancelRequest' + suffix];
4758
4759         for(var i = 0; !raf && i < vendors.length; i++) {
4760           raf = root[vendors[i] + 'Request' + suffix];
4761           caf = root[vendors[i] + 'Cancel' + suffix]
4762               || root[vendors[i] + 'CancelRequest' + suffix];
4763         }
4764
4765         // Some versions of FF have rAF but not cAF
4766         if(!raf || !caf) {
4767           var last = 0
4768             , id$2 = 0
4769             , queue = []
4770             , frameDuration = 1000 / 60;
4771
4772           raf = function(callback) {
4773             if(queue.length === 0) {
4774               var _now = performanceNow()
4775                 , next = Math.max(0, frameDuration - (_now - last));
4776               last = next + _now;
4777               setTimeout(function() {
4778                 var cp = queue.slice(0);
4779                 // Clear queue here to prevent
4780                 // callbacks from appending listeners
4781                 // to the current frame's queue
4782                 queue.length = 0;
4783                 for(var i = 0; i < cp.length; i++) {
4784                   if(!cp[i].cancelled) {
4785                     try{
4786                       cp[i].callback(last);
4787                     } catch(e) {
4788                       setTimeout(function() { throw e }, 0);
4789                     }
4790                   }
4791                 }
4792               }, Math.round(next));
4793             }
4794             queue.push({
4795               handle: ++id$2,
4796               callback: callback,
4797               cancelled: false
4798             });
4799             return id$2
4800           };
4801
4802           caf = function(handle) {
4803             for(var i = 0; i < queue.length; i++) {
4804               if(queue[i].handle === handle) {
4805                 queue[i].cancelled = true;
4806               }
4807             }
4808           };
4809         }
4810
4811         var raf_1 = function(fn) {
4812           // Wrap in a new function to prevent
4813           // `cancel` potentially being assigned
4814           // to the native rAF function
4815           return raf.call(root, fn)
4816         };
4817         var cancel = function() {
4818           caf.apply(root, arguments);
4819         };
4820         var polyfill$f = function(object) {
4821           if (!object) {
4822             object = root;
4823           }
4824           object.requestAnimationFrame = raf;
4825           object.cancelAnimationFrame = caf;
4826         };
4827         raf_1.cancel = cancel;
4828         raf_1.polyfill = polyfill$f;
4829
4830         var global$1 = (function(self) {
4831           return self
4832           // eslint-disable-next-line no-invalid-this
4833         })(typeof self !== 'undefined' ? self : undefined);
4834         var support = {
4835           searchParams: 'URLSearchParams' in global$1,
4836           iterable: 'Symbol' in global$1 && 'iterator' in Symbol,
4837           blob:
4838             'FileReader' in global$1 &&
4839             'Blob' in global$1 &&
4840             (function() {
4841               try {
4842                 new Blob();
4843                 return true
4844               } catch (e) {
4845                 return false
4846               }
4847             })(),
4848           formData: 'FormData' in global$1,
4849           arrayBuffer: 'ArrayBuffer' in global$1
4850         };
4851
4852         function isDataView(obj) {
4853           return obj && DataView.prototype.isPrototypeOf(obj)
4854         }
4855
4856         if (support.arrayBuffer) {
4857           var viewClasses = [
4858             '[object Int8Array]',
4859             '[object Uint8Array]',
4860             '[object Uint8ClampedArray]',
4861             '[object Int16Array]',
4862             '[object Uint16Array]',
4863             '[object Int32Array]',
4864             '[object Uint32Array]',
4865             '[object Float32Array]',
4866             '[object Float64Array]'
4867           ];
4868
4869           var isArrayBufferView =
4870             ArrayBuffer.isView ||
4871             function(obj) {
4872               return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1
4873             };
4874         }
4875
4876         function normalizeName(name) {
4877           if (typeof name !== 'string') {
4878             name = String(name);
4879           }
4880           if (/[^a-z0-9\-#$%&'*+.^_`|~!]/i.test(name) || name === '') {
4881             throw new TypeError('Invalid character in header field name')
4882           }
4883           return name.toLowerCase()
4884         }
4885
4886         function normalizeValue(value) {
4887           if (typeof value !== 'string') {
4888             value = String(value);
4889           }
4890           return value
4891         }
4892
4893         // Build a destructive iterator for the value list
4894         function iteratorFor(items) {
4895           var iterator = {
4896             next: function() {
4897               var value = items.shift();
4898               return {done: value === undefined, value: value}
4899             }
4900           };
4901
4902           if (support.iterable) {
4903             iterator[Symbol.iterator] = function() {
4904               return iterator
4905             };
4906           }
4907
4908           return iterator
4909         }
4910
4911         function Headers(headers) {
4912           this.map = {};
4913
4914           if (headers instanceof Headers) {
4915             headers.forEach(function(value, name) {
4916               this.append(name, value);
4917             }, this);
4918           } else if (Array.isArray(headers)) {
4919             headers.forEach(function(header) {
4920               this.append(header[0], header[1]);
4921             }, this);
4922           } else if (headers) {
4923             Object.getOwnPropertyNames(headers).forEach(function(name) {
4924               this.append(name, headers[name]);
4925             }, this);
4926           }
4927         }
4928
4929         Headers.prototype.append = function(name, value) {
4930           name = normalizeName(name);
4931           value = normalizeValue(value);
4932           var oldValue = this.map[name];
4933           this.map[name] = oldValue ? oldValue + ', ' + value : value;
4934         };
4935
4936         Headers.prototype['delete'] = function(name) {
4937           delete this.map[normalizeName(name)];
4938         };
4939
4940         Headers.prototype.get = function(name) {
4941           name = normalizeName(name);
4942           return this.has(name) ? this.map[name] : null
4943         };
4944
4945         Headers.prototype.has = function(name) {
4946           return this.map.hasOwnProperty(normalizeName(name))
4947         };
4948
4949         Headers.prototype.set = function(name, value) {
4950           this.map[normalizeName(name)] = normalizeValue(value);
4951         };
4952
4953         Headers.prototype.forEach = function(callback, thisArg) {
4954           for (var name in this.map) {
4955             if (this.map.hasOwnProperty(name)) {
4956               callback.call(thisArg, this.map[name], name, this);
4957             }
4958           }
4959         };
4960
4961         Headers.prototype.keys = function() {
4962           var items = [];
4963           this.forEach(function(value, name) {
4964             items.push(name);
4965           });
4966           return iteratorFor(items)
4967         };
4968
4969         Headers.prototype.values = function() {
4970           var items = [];
4971           this.forEach(function(value) {
4972             items.push(value);
4973           });
4974           return iteratorFor(items)
4975         };
4976
4977         Headers.prototype.entries = function() {
4978           var items = [];
4979           this.forEach(function(value, name) {
4980             items.push([name, value]);
4981           });
4982           return iteratorFor(items)
4983         };
4984
4985         if (support.iterable) {
4986           Headers.prototype[Symbol.iterator] = Headers.prototype.entries;
4987         }
4988
4989         function consumed(body) {
4990           if (body.bodyUsed) {
4991             return Promise.reject(new TypeError('Already read'))
4992           }
4993           body.bodyUsed = true;
4994         }
4995
4996         function fileReaderReady(reader) {
4997           return new Promise(function(resolve, reject) {
4998             reader.onload = function() {
4999               resolve(reader.result);
5000             };
5001             reader.onerror = function() {
5002               reject(reader.error);
5003             };
5004           })
5005         }
5006
5007         function readBlobAsArrayBuffer(blob) {
5008           var reader = new FileReader();
5009           var promise = fileReaderReady(reader);
5010           reader.readAsArrayBuffer(blob);
5011           return promise
5012         }
5013
5014         function readBlobAsText(blob) {
5015           var reader = new FileReader();
5016           var promise = fileReaderReady(reader);
5017           reader.readAsText(blob);
5018           return promise
5019         }
5020
5021         function readArrayBufferAsText(buf) {
5022           var view = new Uint8Array(buf);
5023           var chars = new Array(view.length);
5024
5025           for (var i = 0; i < view.length; i++) {
5026             chars[i] = String.fromCharCode(view[i]);
5027           }
5028           return chars.join('')
5029         }
5030
5031         function bufferClone(buf) {
5032           if (buf.slice) {
5033             return buf.slice(0)
5034           } else {
5035             var view = new Uint8Array(buf.byteLength);
5036             view.set(new Uint8Array(buf));
5037             return view.buffer
5038           }
5039         }
5040
5041         function Body() {
5042           this.bodyUsed = false;
5043
5044           this._initBody = function(body) {
5045             /*
5046               fetch-mock wraps the Response object in an ES6 Proxy to
5047               provide useful test harness features such as flush. However, on
5048               ES5 browsers without fetch or Proxy support pollyfills must be used;
5049               the proxy-pollyfill is unable to proxy an attribute unless it exists
5050               on the object before the Proxy is created. This change ensures
5051               Response.bodyUsed exists on the instance, while maintaining the
5052               semantic of setting Request.bodyUsed in the constructor before
5053               _initBody is called.
5054             */
5055             this.bodyUsed = this.bodyUsed;
5056             this._bodyInit = body;
5057             if (!body) {
5058               this._bodyText = '';
5059             } else if (typeof body === 'string') {
5060               this._bodyText = body;
5061             } else if (support.blob && Blob.prototype.isPrototypeOf(body)) {
5062               this._bodyBlob = body;
5063             } else if (support.formData && FormData.prototype.isPrototypeOf(body)) {
5064               this._bodyFormData = body;
5065             } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
5066               this._bodyText = body.toString();
5067             } else if (support.arrayBuffer && support.blob && isDataView(body)) {
5068               this._bodyArrayBuffer = bufferClone(body.buffer);
5069               // IE 10-11 can't handle a DataView body.
5070               this._bodyInit = new Blob([this._bodyArrayBuffer]);
5071             } else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) {
5072               this._bodyArrayBuffer = bufferClone(body);
5073             } else {
5074               this._bodyText = body = Object.prototype.toString.call(body);
5075             }
5076
5077             if (!this.headers.get('content-type')) {
5078               if (typeof body === 'string') {
5079                 this.headers.set('content-type', 'text/plain;charset=UTF-8');
5080               } else if (this._bodyBlob && this._bodyBlob.type) {
5081                 this.headers.set('content-type', this._bodyBlob.type);
5082               } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
5083                 this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8');
5084               }
5085             }
5086           };
5087
5088           if (support.blob) {
5089             this.blob = function() {
5090               var rejected = consumed(this);
5091               if (rejected) {
5092                 return rejected
5093               }
5094
5095               if (this._bodyBlob) {
5096                 return Promise.resolve(this._bodyBlob)
5097               } else if (this._bodyArrayBuffer) {
5098                 return Promise.resolve(new Blob([this._bodyArrayBuffer]))
5099               } else if (this._bodyFormData) {
5100                 throw new Error('could not read FormData body as blob')
5101               } else {
5102                 return Promise.resolve(new Blob([this._bodyText]))
5103               }
5104             };
5105
5106             this.arrayBuffer = function() {
5107               if (this._bodyArrayBuffer) {
5108                 return consumed(this) || Promise.resolve(this._bodyArrayBuffer)
5109               } else {
5110                 return this.blob().then(readBlobAsArrayBuffer)
5111               }
5112             };
5113           }
5114
5115           this.text = function() {
5116             var rejected = consumed(this);
5117             if (rejected) {
5118               return rejected
5119             }
5120
5121             if (this._bodyBlob) {
5122               return readBlobAsText(this._bodyBlob)
5123             } else if (this._bodyArrayBuffer) {
5124               return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer))
5125             } else if (this._bodyFormData) {
5126               throw new Error('could not read FormData body as text')
5127             } else {
5128               return Promise.resolve(this._bodyText)
5129             }
5130           };
5131
5132           if (support.formData) {
5133             this.formData = function() {
5134               return this.text().then(decode)
5135             };
5136           }
5137
5138           this.json = function() {
5139             return this.text().then(JSON.parse)
5140           };
5141
5142           return this
5143         }
5144
5145         // HTTP methods whose capitalization should be normalized
5146         var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'];
5147
5148         function normalizeMethod(method) {
5149           var upcased = method.toUpperCase();
5150           return methods.indexOf(upcased) > -1 ? upcased : method
5151         }
5152
5153         function Request(input, options) {
5154           options = options || {};
5155           var body = options.body;
5156
5157           if (input instanceof Request) {
5158             if (input.bodyUsed) {
5159               throw new TypeError('Already read')
5160             }
5161             this.url = input.url;
5162             this.credentials = input.credentials;
5163             if (!options.headers) {
5164               this.headers = new Headers(input.headers);
5165             }
5166             this.method = input.method;
5167             this.mode = input.mode;
5168             this.signal = input.signal;
5169             if (!body && input._bodyInit != null) {
5170               body = input._bodyInit;
5171               input.bodyUsed = true;
5172             }
5173           } else {
5174             this.url = String(input);
5175           }
5176
5177           this.credentials = options.credentials || this.credentials || 'same-origin';
5178           if (options.headers || !this.headers) {
5179             this.headers = new Headers(options.headers);
5180           }
5181           this.method = normalizeMethod(options.method || this.method || 'GET');
5182           this.mode = options.mode || this.mode || null;
5183           this.signal = options.signal || this.signal;
5184           this.referrer = null;
5185
5186           if ((this.method === 'GET' || this.method === 'HEAD') && body) {
5187             throw new TypeError('Body not allowed for GET or HEAD requests')
5188           }
5189           this._initBody(body);
5190
5191           if (this.method === 'GET' || this.method === 'HEAD') {
5192             if (options.cache === 'no-store' || options.cache === 'no-cache') {
5193               // Search for a '_' parameter in the query string
5194               var reParamSearch = /([?&])_=[^&]*/;
5195               if (reParamSearch.test(this.url)) {
5196                 // If it already exists then set the value with the current time
5197                 this.url = this.url.replace(reParamSearch, '$1_=' + new Date().getTime());
5198               } else {
5199                 // Otherwise add a new '_' parameter to the end with the current time
5200                 var reQueryString = /\?/;
5201                 this.url += (reQueryString.test(this.url) ? '&' : '?') + '_=' + new Date().getTime();
5202               }
5203             }
5204           }
5205         }
5206
5207         Request.prototype.clone = function() {
5208           return new Request(this, {body: this._bodyInit})
5209         };
5210
5211         function decode(body) {
5212           var form = new FormData();
5213           body
5214             .trim()
5215             .split('&')
5216             .forEach(function(bytes) {
5217               if (bytes) {
5218                 var split = bytes.split('=');
5219                 var name = split.shift().replace(/\+/g, ' ');
5220                 var value = split.join('=').replace(/\+/g, ' ');
5221                 form.append(decodeURIComponent(name), decodeURIComponent(value));
5222               }
5223             });
5224           return form
5225         }
5226
5227         function parseHeaders(rawHeaders) {
5228           var headers = new Headers();
5229           // Replace instances of \r\n and \n followed by at least one space or horizontal tab with a space
5230           // https://tools.ietf.org/html/rfc7230#section-3.2
5231           var preProcessedHeaders = rawHeaders.replace(/\r?\n[\t ]+/g, ' ');
5232           preProcessedHeaders.split(/\r?\n/).forEach(function(line) {
5233             var parts = line.split(':');
5234             var key = parts.shift().trim();
5235             if (key) {
5236               var value = parts.join(':').trim();
5237               headers.append(key, value);
5238             }
5239           });
5240           return headers
5241         }
5242
5243         Body.call(Request.prototype);
5244
5245         function Response(bodyInit, options) {
5246           if (!options) {
5247             options = {};
5248           }
5249
5250           this.type = 'default';
5251           this.status = options.status === undefined ? 200 : options.status;
5252           this.ok = this.status >= 200 && this.status < 300;
5253           this.statusText = 'statusText' in options ? options.statusText : '';
5254           this.headers = new Headers(options.headers);
5255           this.url = options.url || '';
5256           this._initBody(bodyInit);
5257         }
5258
5259         Body.call(Response.prototype);
5260
5261         Response.prototype.clone = function() {
5262           return new Response(this._bodyInit, {
5263             status: this.status,
5264             statusText: this.statusText,
5265             headers: new Headers(this.headers),
5266             url: this.url
5267           })
5268         };
5269
5270         Response.error = function() {
5271           var response = new Response(null, {status: 0, statusText: ''});
5272           response.type = 'error';
5273           return response
5274         };
5275
5276         var redirectStatuses = [301, 302, 303, 307, 308];
5277
5278         Response.redirect = function(url, status) {
5279           if (redirectStatuses.indexOf(status) === -1) {
5280             throw new RangeError('Invalid status code')
5281           }
5282
5283           return new Response(null, {status: status, headers: {location: url}})
5284         };
5285
5286         var DOMException$1 = global$1.DOMException;
5287
5288         if (typeof DOMException$1 !== 'function') {
5289           DOMException$1 = function(message, name) {
5290             this.message = message;
5291             this.name = name;
5292             var error = Error(message);
5293             this.stack = error.stack;
5294           };
5295           DOMException$1.prototype = Object.create(Error.prototype);
5296           DOMException$1.prototype.constructor = DOMException$1;
5297         }
5298
5299         function fetch$1(input, init) {
5300           return new Promise(function(resolve, reject) {
5301             var request = new Request(input, init);
5302
5303             if (request.signal && request.signal.aborted) {
5304               return reject(new DOMException$1('Aborted', 'AbortError'))
5305             }
5306
5307             var xhr = new XMLHttpRequest();
5308
5309             function abortXhr() {
5310               xhr.abort();
5311             }
5312
5313             xhr.onload = function() {
5314               var options = {
5315                 status: xhr.status,
5316                 statusText: xhr.statusText,
5317                 headers: parseHeaders(xhr.getAllResponseHeaders() || '')
5318               };
5319               options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL');
5320               var body = 'response' in xhr ? xhr.response : xhr.responseText;
5321               setTimeout(function() {
5322                 resolve(new Response(body, options));
5323               }, 0);
5324             };
5325
5326             xhr.onerror = function() {
5327               setTimeout(function() {
5328                 reject(new TypeError('Network request failed'));
5329               }, 0);
5330             };
5331
5332             xhr.ontimeout = function() {
5333               setTimeout(function() {
5334                 reject(new TypeError('Network request failed'));
5335               }, 0);
5336             };
5337
5338             xhr.onabort = function() {
5339               setTimeout(function() {
5340                 reject(new DOMException$1('Aborted', 'AbortError'));
5341               }, 0);
5342             };
5343
5344             function fixUrl(url) {
5345               try {
5346                 return url === '' && global$1.location.href ? global$1.location.href : url
5347               } catch (e) {
5348                 return url
5349               }
5350             }
5351
5352             xhr.open(request.method, fixUrl(request.url), true);
5353
5354             if (request.credentials === 'include') {
5355               xhr.withCredentials = true;
5356             } else if (request.credentials === 'omit') {
5357               xhr.withCredentials = false;
5358             }
5359
5360             if ('responseType' in xhr) {
5361               if (support.blob) {
5362                 xhr.responseType = 'blob';
5363               } else if (
5364                 support.arrayBuffer &&
5365                 request.headers.get('Content-Type') &&
5366                 request.headers.get('Content-Type').indexOf('application/octet-stream') !== -1
5367               ) {
5368                 xhr.responseType = 'arraybuffer';
5369               }
5370             }
5371
5372             request.headers.forEach(function(value, name) {
5373               xhr.setRequestHeader(name, value);
5374             });
5375
5376             if (request.signal) {
5377               request.signal.addEventListener('abort', abortXhr);
5378
5379               xhr.onreadystatechange = function() {
5380                 // DONE (success or failure)
5381                 if (xhr.readyState === 4) {
5382                   request.signal.removeEventListener('abort', abortXhr);
5383                 }
5384               };
5385             }
5386
5387             xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit);
5388           })
5389         }
5390
5391         fetch$1.polyfill = true;
5392
5393         if (!global$1.fetch) {
5394           global$1.fetch = fetch$1;
5395           global$1.Headers = Headers;
5396           global$1.Request = Request;
5397           global$1.Response = Response;
5398         }
5399
5400         var lib = createCommonjsModule(function (module, exports) {
5401         Object.defineProperty(exports, "__esModule", { value: true });
5402
5403
5404
5405
5406
5407
5408
5409
5410
5411
5412         if (!window.Set) {
5413             window.Set = es6Set;
5414         }
5415         if (!window.Map) {
5416             window.Map = es6Map;
5417         }
5418         if (!window.Promise) {
5419             window.Promise = polyfill$e;
5420             window.Promise._immediateFn = setAsap;
5421         }
5422         if (!Array.prototype.find) {
5423             array_prototype_find.shim();
5424         }
5425         if (!Array.prototype.findIndex) {
5426             array_prototype_findindex.shim();
5427         }
5428         if (!Array.from) {
5429             array_from.shim();
5430         }
5431         if (!Object.values) {
5432             object_values.shim();
5433         }
5434         if (!Object.assign) {
5435             object_assign.shim();
5436         }
5437         if (!window.requestAnimationFrame || !window.cancelAnimationFrame) {
5438             window.requestAnimationFrame = raf_1;
5439             window.cancelAnimationFrame = raf_1.cancel;
5440         }
5441
5442         var finalFetch = window.fetch;
5443         var finalPromise = window.Promise;
5444         window.fetch = function (input, init) {
5445             try {
5446                 return finalFetch(input, init);
5447             }
5448             catch (error) {
5449                 return new finalPromise(function (_, reject) { return reject(error); });
5450             }
5451         };
5452         });
5453
5454         var $Math$2 = GetIntrinsic('%Math%');
5455
5456         var $floor$1 = $Math$2.floor;
5457         var $abs$1 = $Math$2.abs;
5458
5459
5460
5461
5462         // https://www.ecma-international.org/ecma-262/6.0/#sec-isinteger
5463
5464         var IsInteger = function IsInteger(argument) {
5465                 if (typeof argument !== 'number' || _isNaN(argument) || !_isFinite(argument)) {
5466                         return false;
5467                 }
5468                 var abs = $abs$1(argument);
5469                 return $floor$1(abs) === abs;
5470         };
5471
5472         var ArrayPush = callBound('Array.prototype.push');
5473         var StringFromCharCodeSpread = callBind.apply(String.fromCharCode, null);
5474
5475         var implementation$8 = function fromCodePoint(_ /* fromCodePoint.length is 1 */) {
5476                 var MAX_SIZE = 0x4000;
5477                 var codeUnits = [];
5478                 var highSurrogate;
5479                 var lowSurrogate;
5480                 var index = -1;
5481                 var length = arguments.length;
5482                 if (!length) {
5483                         return '';
5484                 }
5485                 var result = '';
5486                 while (++index < length) {
5487                         var codePoint = ToNumber$1(arguments[index]);
5488                         if (
5489                                 !IsInteger(codePoint) ||
5490                                 codePoint < 0 || codePoint > 0x10FFFF // not a valid Unicode code point
5491                         ) {
5492                                 throw RangeError('Invalid code point: ' + codePoint);
5493                         }
5494                         if (codePoint <= 0xFFFF) { // BMP code point
5495                                 ArrayPush(codeUnits, codePoint);
5496                         } else { // Astral code point; split in surrogate halves
5497                                 // https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
5498                                 codePoint -= 0x10000;
5499                                 highSurrogate = (codePoint >> 10) + 0xD800;
5500                                 lowSurrogate = (codePoint % 0x400) + 0xDC00;
5501                                 ArrayPush(codeUnits, highSurrogate, lowSurrogate);
5502                         }
5503                         if (index + 1 == length || codeUnits.length > MAX_SIZE) {
5504                                 result += StringFromCharCodeSpread(codeUnits);
5505                                 codeUnits.length = 0;
5506                         }
5507                 }
5508                 return result;
5509         };
5510
5511         var polyfill$g = function getPolyfill() {
5512                 return String.fromCodePoint || implementation$8;
5513         };
5514
5515         var shim$d = function shimFromCodePoint() {
5516                 var polyfill = polyfill$g();
5517
5518                 if (String.fromCodePoint !== polyfill) {
5519                         defineProperties_1(String, { fromCodePoint: polyfill });
5520                 }
5521
5522                 return polyfill;
5523         };
5524
5525         /*! https://mths.be/fromcodepoint v1.0.0 by @mathias */
5526
5527         shim$d();
5528
5529         (function (factory) {
5530           
5531           factory();
5532         }((function () {
5533           function _classCallCheck(instance, Constructor) {
5534             if (!(instance instanceof Constructor)) {
5535               throw new TypeError("Cannot call a class as a function");
5536             }
5537           }
5538
5539           function _defineProperties(target, props) {
5540             for (var i = 0; i < props.length; i++) {
5541               var descriptor = props[i];
5542               descriptor.enumerable = descriptor.enumerable || false;
5543               descriptor.configurable = true;
5544               if ("value" in descriptor) descriptor.writable = true;
5545               Object.defineProperty(target, descriptor.key, descriptor);
5546             }
5547           }
5548
5549           function _createClass(Constructor, protoProps, staticProps) {
5550             if (protoProps) _defineProperties(Constructor.prototype, protoProps);
5551             if (staticProps) _defineProperties(Constructor, staticProps);
5552             return Constructor;
5553           }
5554
5555           function _inherits(subClass, superClass) {
5556             if (typeof superClass !== "function" && superClass !== null) {
5557               throw new TypeError("Super expression must either be null or a function");
5558             }
5559
5560             subClass.prototype = Object.create(superClass && superClass.prototype, {
5561               constructor: {
5562                 value: subClass,
5563                 writable: true,
5564                 configurable: true
5565               }
5566             });
5567             if (superClass) _setPrototypeOf(subClass, superClass);
5568           }
5569
5570           function _getPrototypeOf(o) {
5571             _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
5572               return o.__proto__ || Object.getPrototypeOf(o);
5573             };
5574             return _getPrototypeOf(o);
5575           }
5576
5577           function _setPrototypeOf(o, p) {
5578             _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
5579               o.__proto__ = p;
5580               return o;
5581             };
5582
5583             return _setPrototypeOf(o, p);
5584           }
5585
5586           function _assertThisInitialized(self) {
5587             if (self === void 0) {
5588               throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
5589             }
5590
5591             return self;
5592           }
5593
5594           function _possibleConstructorReturn(self, call) {
5595             if (call && (typeof call === "object" || typeof call === "function")) {
5596               return call;
5597             }
5598
5599             return _assertThisInitialized(self);
5600           }
5601
5602           function _superPropBase(object, property) {
5603             while (!Object.prototype.hasOwnProperty.call(object, property)) {
5604               object = _getPrototypeOf(object);
5605               if (object === null) break;
5606             }
5607
5608             return object;
5609           }
5610
5611           function _get(target, property, receiver) {
5612             if (typeof Reflect !== "undefined" && Reflect.get) {
5613               _get = Reflect.get;
5614             } else {
5615               _get = function _get(target, property, receiver) {
5616                 var base = _superPropBase(target, property);
5617
5618                 if (!base) return;
5619                 var desc = Object.getOwnPropertyDescriptor(base, property);
5620
5621                 if (desc.get) {
5622                   return desc.get.call(receiver);
5623                 }
5624
5625                 return desc.value;
5626               };
5627             }
5628
5629             return _get(target, property, receiver || target);
5630           }
5631
5632           var Emitter =
5633           /*#__PURE__*/
5634           function () {
5635             function Emitter() {
5636               _classCallCheck(this, Emitter);
5637
5638               Object.defineProperty(this, 'listeners', {
5639                 value: {},
5640                 writable: true,
5641                 configurable: true
5642               });
5643             }
5644
5645             _createClass(Emitter, [{
5646               key: "addEventListener",
5647               value: function addEventListener(type, callback) {
5648                 if (!(type in this.listeners)) {
5649                   this.listeners[type] = [];
5650                 }
5651
5652                 this.listeners[type].push(callback);
5653               }
5654             }, {
5655               key: "removeEventListener",
5656               value: function removeEventListener(type, callback) {
5657                 if (!(type in this.listeners)) {
5658                   return;
5659                 }
5660
5661                 var stack = this.listeners[type];
5662
5663                 for (var i = 0, l = stack.length; i < l; i++) {
5664                   if (stack[i] === callback) {
5665                     stack.splice(i, 1);
5666                     return;
5667                   }
5668                 }
5669               }
5670             }, {
5671               key: "dispatchEvent",
5672               value: function dispatchEvent(event) {
5673                 var _this = this;
5674
5675                 if (!(event.type in this.listeners)) {
5676                   return;
5677                 }
5678
5679                 var debounce = function debounce(callback) {
5680                   setTimeout(function () {
5681                     return callback.call(_this, event);
5682                   });
5683                 };
5684
5685                 var stack = this.listeners[event.type];
5686
5687                 for (var i = 0, l = stack.length; i < l; i++) {
5688                   debounce(stack[i]);
5689                 }
5690
5691                 return !event.defaultPrevented;
5692               }
5693             }]);
5694
5695             return Emitter;
5696           }();
5697
5698           var AbortSignal =
5699           /*#__PURE__*/
5700           function (_Emitter) {
5701             _inherits(AbortSignal, _Emitter);
5702
5703             function AbortSignal() {
5704               var _this2;
5705
5706               _classCallCheck(this, AbortSignal);
5707
5708               _this2 = _possibleConstructorReturn(this, _getPrototypeOf(AbortSignal).call(this)); // Some versions of babel does not transpile super() correctly for IE <= 10, if the parent
5709               // constructor has failed to run, then "this.listeners" will still be undefined and then we call
5710               // the parent constructor directly instead as a workaround. For general details, see babel bug:
5711               // https://github.com/babel/babel/issues/3041
5712               // This hack was added as a fix for the issue described here:
5713               // https://github.com/Financial-Times/polyfill-library/pull/59#issuecomment-477558042
5714
5715               if (!_this2.listeners) {
5716                 Emitter.call(_assertThisInitialized(_this2));
5717               } // Compared to assignment, Object.defineProperty makes properties non-enumerable by default and
5718               // we want Object.keys(new AbortController().signal) to be [] for compat with the native impl
5719
5720
5721               Object.defineProperty(_assertThisInitialized(_this2), 'aborted', {
5722                 value: false,
5723                 writable: true,
5724                 configurable: true
5725               });
5726               Object.defineProperty(_assertThisInitialized(_this2), 'onabort', {
5727                 value: null,
5728                 writable: true,
5729                 configurable: true
5730               });
5731               return _this2;
5732             }
5733
5734             _createClass(AbortSignal, [{
5735               key: "toString",
5736               value: function toString() {
5737                 return '[object AbortSignal]';
5738               }
5739             }, {
5740               key: "dispatchEvent",
5741               value: function dispatchEvent(event) {
5742                 if (event.type === 'abort') {
5743                   this.aborted = true;
5744
5745                   if (typeof this.onabort === 'function') {
5746                     this.onabort.call(this, event);
5747                   }
5748                 }
5749
5750                 _get(_getPrototypeOf(AbortSignal.prototype), "dispatchEvent", this).call(this, event);
5751               }
5752             }]);
5753
5754             return AbortSignal;
5755           }(Emitter);
5756           var AbortController =
5757           /*#__PURE__*/
5758           function () {
5759             function AbortController() {
5760               _classCallCheck(this, AbortController);
5761
5762               // Compared to assignment, Object.defineProperty makes properties non-enumerable by default and
5763               // we want Object.keys(new AbortController()) to be [] for compat with the native impl
5764               Object.defineProperty(this, 'signal', {
5765                 value: new AbortSignal(),
5766                 writable: true,
5767                 configurable: true
5768               });
5769             }
5770
5771             _createClass(AbortController, [{
5772               key: "abort",
5773               value: function abort() {
5774                 var event;
5775
5776                 try {
5777                   event = new Event('abort');
5778                 } catch (e) {
5779                   if (typeof document !== 'undefined') {
5780                     if (!document.createEvent) {
5781                       // For Internet Explorer 8:
5782                       event = document.createEventObject();
5783                       event.type = 'abort';
5784                     } else {
5785                       // For Internet Explorer 11:
5786                       event = document.createEvent('Event');
5787                       event.initEvent('abort', false, false);
5788                     }
5789                   } else {
5790                     // Fallback where document isn't available:
5791                     event = {
5792                       type: 'abort',
5793                       bubbles: false,
5794                       cancelable: false
5795                     };
5796                   }
5797                 }
5798
5799                 this.signal.dispatchEvent(event);
5800               }
5801             }, {
5802               key: "toString",
5803               value: function toString() {
5804                 return '[object AbortController]';
5805               }
5806             }]);
5807
5808             return AbortController;
5809           }();
5810
5811           if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
5812             // These are necessary to make sure that we get correct output for:
5813             // Object.prototype.toString.call(new AbortController())
5814             AbortController.prototype[Symbol.toStringTag] = 'AbortController';
5815             AbortSignal.prototype[Symbol.toStringTag] = 'AbortSignal';
5816           }
5817
5818           function polyfillNeeded(self) {
5819             if (self.__FORCE_INSTALL_ABORTCONTROLLER_POLYFILL) {
5820               console.log('__FORCE_INSTALL_ABORTCONTROLLER_POLYFILL=true is set, will force install polyfill');
5821               return true;
5822             } // Note that the "unfetch" minimal fetch polyfill defines fetch() without
5823             // defining window.Request, and this polyfill need to work on top of unfetch
5824             // so the below feature detection needs the !self.AbortController part.
5825             // The Request.prototype check is also needed because Safari versions 11.1.2
5826             // up to and including 12.1.x has a window.AbortController present but still
5827             // does NOT correctly implement abortable fetch:
5828             // https://bugs.webkit.org/show_bug.cgi?id=174980#c2
5829
5830
5831             return typeof self.Request === 'function' && !self.Request.prototype.hasOwnProperty('signal') || !self.AbortController;
5832           }
5833
5834           /**
5835            * Note: the "fetch.Request" default value is available for fetch imported from
5836            * the "node-fetch" package and not in browsers. This is OK since browsers
5837            * will be importing umd-polyfill.js from that path "self" is passed the
5838            * decorator so the default value will not be used (because browsers that define
5839            * fetch also has Request). One quirky setup where self.fetch exists but
5840            * self.Request does not is when the "unfetch" minimal fetch polyfill is used
5841            * on top of IE11; for this case the browser will try to use the fetch.Request
5842            * default value which in turn will be undefined but then then "if (Request)"
5843            * will ensure that you get a patched fetch but still no Request (as expected).
5844            * @param {fetch, Request = fetch.Request}
5845            * @returns {fetch: abortableFetch, Request: AbortableRequest}
5846            */
5847
5848           function abortableFetchDecorator(patchTargets) {
5849             if ('function' === typeof patchTargets) {
5850               patchTargets = {
5851                 fetch: patchTargets
5852               };
5853             }
5854
5855             var _patchTargets = patchTargets,
5856                 fetch = _patchTargets.fetch,
5857                 _patchTargets$Request = _patchTargets.Request,
5858                 NativeRequest = _patchTargets$Request === void 0 ? fetch.Request : _patchTargets$Request,
5859                 NativeAbortController = _patchTargets.AbortController,
5860                 _patchTargets$__FORCE = _patchTargets.__FORCE_INSTALL_ABORTCONTROLLER_POLYFILL,
5861                 __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL = _patchTargets$__FORCE === void 0 ? false : _patchTargets$__FORCE;
5862
5863             if (!polyfillNeeded({
5864               fetch: fetch,
5865               Request: NativeRequest,
5866               AbortController: NativeAbortController,
5867               __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL: __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL
5868             })) {
5869               return {
5870                 fetch: fetch,
5871                 Request: Request
5872               };
5873             }
5874
5875             var Request = NativeRequest; // Note that the "unfetch" minimal fetch polyfill defines fetch() without
5876             // defining window.Request, and this polyfill need to work on top of unfetch
5877             // hence we only patch it if it's available. Also we don't patch it if signal
5878             // is already available on the Request prototype because in this case support
5879             // is present and the patching below can cause a crash since it assigns to
5880             // request.signal which is technically a read-only property. This latter error
5881             // happens when you run the main5.js node-fetch example in the repo
5882             // "abortcontroller-polyfill-examples". The exact error is:
5883             //   request.signal = init.signal;
5884             //   ^
5885             // TypeError: Cannot set property signal of #<Request> which has only a getter
5886
5887             if (Request && !Request.prototype.hasOwnProperty('signal') || __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL) {
5888               Request = function Request(input, init) {
5889                 var signal;
5890
5891                 if (init && init.signal) {
5892                   signal = init.signal; // Never pass init.signal to the native Request implementation when the polyfill has
5893                   // been installed because if we're running on top of a browser with a
5894                   // working native AbortController (i.e. the polyfill was installed due to
5895                   // __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL being set), then passing our
5896                   // fake AbortSignal to the native fetch will trigger:
5897                   // TypeError: Failed to construct 'Request': member signal is not of type AbortSignal.
5898
5899                   delete init.signal;
5900                 }
5901
5902                 var request = new NativeRequest(input, init);
5903
5904                 if (signal) {
5905                   Object.defineProperty(request, 'signal', {
5906                     writable: false,
5907                     enumerable: false,
5908                     configurable: true,
5909                     value: signal
5910                   });
5911                 }
5912
5913                 return request;
5914               };
5915
5916               Request.prototype = NativeRequest.prototype;
5917             }
5918
5919             var realFetch = fetch;
5920
5921             var abortableFetch = function abortableFetch(input, init) {
5922               var signal = Request && Request.prototype.isPrototypeOf(input) ? input.signal : init ? init.signal : undefined;
5923
5924               if (signal) {
5925                 var abortError;
5926
5927                 try {
5928                   abortError = new DOMException('Aborted', 'AbortError');
5929                 } catch (err) {
5930                   // IE 11 does not support calling the DOMException constructor, use a
5931                   // regular error object on it instead.
5932                   abortError = new Error('Aborted');
5933                   abortError.name = 'AbortError';
5934                 } // Return early if already aborted, thus avoiding making an HTTP request
5935
5936
5937                 if (signal.aborted) {
5938                   return Promise.reject(abortError);
5939                 } // Turn an event into a promise, reject it once `abort` is dispatched
5940
5941
5942                 var cancellation = new Promise(function (_, reject) {
5943                   signal.addEventListener('abort', function () {
5944                     return reject(abortError);
5945                   }, {
5946                     once: true
5947                   });
5948                 });
5949
5950                 if (init && init.signal) {
5951                   // Never pass .signal to the native implementation when the polyfill has
5952                   // been installed because if we're running on top of a browser with a
5953                   // working native AbortController (i.e. the polyfill was installed due to
5954                   // __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL being set), then passing our
5955                   // fake AbortSignal to the native fetch will trigger:
5956                   // TypeError: Failed to execute 'fetch' on 'Window': member signal is not of type AbortSignal.
5957                   delete init.signal;
5958                 } // Return the fastest promise (don't need to wait for request to finish)
5959
5960
5961                 return Promise.race([cancellation, realFetch(input, init)]);
5962               }
5963
5964               return realFetch(input, init);
5965             };
5966
5967             return {
5968               fetch: abortableFetch,
5969               Request: Request
5970             };
5971           }
5972
5973           (function (self) {
5974
5975             if (!polyfillNeeded(self)) {
5976               return;
5977             }
5978
5979             if (!self.fetch) {
5980               console.warn('fetch() is not available, cannot install abortcontroller-polyfill');
5981               return;
5982             }
5983
5984             var _abortableFetch = abortableFetchDecorator(self),
5985                 fetch = _abortableFetch.fetch,
5986                 Request = _abortableFetch.Request;
5987
5988             self.fetch = fetch;
5989             self.Request = Request;
5990             Object.defineProperty(self, 'AbortController', {
5991               writable: true,
5992               enumerable: false,
5993               configurable: true,
5994               value: AbortController
5995             });
5996             Object.defineProperty(self, 'AbortSignal', {
5997               writable: true,
5998               enumerable: false,
5999               configurable: true,
6000               value: AbortSignal
6001             });
6002           })(typeof self !== 'undefined' ? self : commonjsGlobal);
6003
6004         })));
6005
6006         function actionAddEntity(way) {
6007             return function(graph) {
6008                 return graph.replace(way);
6009             };
6010         }
6011
6012         /*
6013         Order the nodes of a way in reverse order and reverse any direction dependent tags
6014         other than `oneway`. (We assume that correcting a backwards oneway is the primary
6015         reason for reversing a way.)
6016
6017         In addition, numeric-valued `incline` tags are negated.
6018
6019         The JOSM implementation was used as a guide, but transformations that were of unclear benefit
6020         or adjusted tags that don't seem to be used in practice were omitted.
6021
6022         References:
6023             http://wiki.openstreetmap.org/wiki/Forward_%26_backward,_left_%26_right
6024             http://wiki.openstreetmap.org/wiki/Key:direction#Steps
6025             http://wiki.openstreetmap.org/wiki/Key:incline
6026             http://wiki.openstreetmap.org/wiki/Route#Members
6027             http://josm.openstreetmap.de/browser/josm/trunk/src/org/openstreetmap/josm/corrector/ReverseWayTagCorrector.java
6028             http://wiki.openstreetmap.org/wiki/Tag:highway%3Dstop
6029             http://wiki.openstreetmap.org/wiki/Key:traffic_sign#On_a_way_or_area
6030         */
6031         function actionReverse(entityID, options) {
6032             var ignoreKey = /^.*(_|:)?(description|name|note|website|ref|source|comment|watch|attribution)(_|:)?/;
6033             var numeric = /^([+\-]?)(?=[\d.])/;
6034             var directionKey = /direction$/;
6035             var turn_lanes = /^turn:lanes:?/;
6036             var keyReplacements = [
6037                 [/:right$/, ':left'],
6038                 [/:left$/, ':right'],
6039                 [/:forward$/, ':backward'],
6040                 [/:backward$/, ':forward'],
6041                 [/:right:/, ':left:'],
6042                 [/:left:/, ':right:'],
6043                 [/:forward:/, ':backward:'],
6044                 [/:backward:/, ':forward:']
6045             ];
6046             var valueReplacements = {
6047                 left: 'right',
6048                 right: 'left',
6049                 up: 'down',
6050                 down: 'up',
6051                 forward: 'backward',
6052                 backward: 'forward',
6053                 forwards: 'backward',
6054                 backwards: 'forward',
6055             };
6056             var roleReplacements = {
6057                 forward: 'backward',
6058                 backward: 'forward',
6059                 forwards: 'backward',
6060                 backwards: 'forward'
6061             };
6062             var onewayReplacements = {
6063                 yes: '-1',
6064                 '1': '-1',
6065                 '-1': 'yes'
6066             };
6067
6068             var compassReplacements = {
6069                 N: 'S',
6070                 NNE: 'SSW',
6071                 NE: 'SW',
6072                 ENE: 'WSW',
6073                 E: 'W',
6074                 ESE: 'WNW',
6075                 SE: 'NW',
6076                 SSE: 'NNW',
6077                 S: 'N',
6078                 SSW: 'NNE',
6079                 SW: 'NE',
6080                 WSW: 'ENE',
6081                 W: 'E',
6082                 WNW: 'ESE',
6083                 NW: 'SE',
6084                 NNW: 'SSE'
6085             };
6086
6087
6088             function reverseKey(key) {
6089                 for (var i = 0; i < keyReplacements.length; ++i) {
6090                     var replacement = keyReplacements[i];
6091                     if (replacement[0].test(key)) {
6092                         return key.replace(replacement[0], replacement[1]);
6093                     }
6094                 }
6095                 return key;
6096             }
6097
6098
6099             function reverseValue(key, value, includeAbsolute) {
6100                 if (ignoreKey.test(key)) return value;
6101
6102                 // Turn lanes are left/right to key (not way) direction - #5674
6103                 if (turn_lanes.test(key)) {
6104                     return value;
6105
6106                 } else if (key === 'incline' && numeric.test(value)) {
6107                     return value.replace(numeric, function(_, sign) { return sign === '-' ? '' : '-'; });
6108
6109                 } else if (options && options.reverseOneway && key === 'oneway') {
6110                     return onewayReplacements[value] || value;
6111
6112                 } else if (includeAbsolute && directionKey.test(key)) {
6113                     if (compassReplacements[value]) return compassReplacements[value];
6114
6115                     var degrees = parseFloat(value);
6116                     if (typeof degrees === 'number' && !isNaN(degrees)) {
6117                         if (degrees < 180) {
6118                             degrees += 180;
6119                         } else {
6120                             degrees -= 180;
6121                         }
6122                         return degrees.toString();
6123                     }
6124                 }
6125
6126                 return valueReplacements[value] || value;
6127             }
6128
6129
6130             // Reverse the direction of tags attached to the nodes - #3076
6131             function reverseNodeTags(graph, nodeIDs) {
6132                 for (var i = 0; i < nodeIDs.length; i++) {
6133                     var node = graph.hasEntity(nodeIDs[i]);
6134                     if (!node || !Object.keys(node.tags).length) continue;
6135
6136                     var tags = {};
6137                     for (var key in node.tags) {
6138                         tags[reverseKey(key)] = reverseValue(key, node.tags[key], node.id === entityID);
6139                     }
6140                     graph = graph.replace(node.update({tags: tags}));
6141                 }
6142                 return graph;
6143             }
6144
6145
6146             function reverseWay(graph, way) {
6147                 var nodes = way.nodes.slice().reverse();
6148                 var tags = {};
6149                 var role;
6150
6151                 for (var key in way.tags) {
6152                     tags[reverseKey(key)] = reverseValue(key, way.tags[key]);
6153                 }
6154
6155                 graph.parentRelations(way).forEach(function(relation) {
6156                     relation.members.forEach(function(member, index) {
6157                         if (member.id === way.id && (role = roleReplacements[member.role])) {
6158                             relation = relation.updateMember({role: role}, index);
6159                             graph = graph.replace(relation);
6160                         }
6161                     });
6162                 });
6163
6164                 // Reverse any associated directions on nodes on the way and then replace
6165                 // the way itself with the reversed node ids and updated way tags
6166                 return reverseNodeTags(graph, nodes)
6167                     .replace(way.update({nodes: nodes, tags: tags}));
6168             }
6169
6170
6171             var action = function(graph) {
6172                 var entity = graph.entity(entityID);
6173                 if (entity.type === 'way') {
6174                     return reverseWay(graph, entity);
6175                 }
6176                 return reverseNodeTags(graph, [entityID]);
6177             };
6178
6179             action.disabled = function(graph) {
6180                 var entity = graph.hasEntity(entityID);
6181                 if (!entity || entity.type === 'way') return false;
6182
6183                 for (var key in entity.tags) {
6184                     var value = entity.tags[key];
6185                     if (reverseKey(key) !== key || reverseValue(key, value, true) !== value) {
6186                         return false;
6187                     }
6188                 }
6189                 return 'nondirectional_node';
6190             };
6191
6192             action.entityID = function() {
6193                 return entityID;
6194             };
6195
6196             return action;
6197         }
6198
6199         function osmIsInterestingTag(key) {
6200             return key !== 'attribution' &&
6201                 key !== 'created_by' &&
6202                 key !== 'source' &&
6203                 key !== 'odbl' &&
6204                 key.indexOf('source:') !== 0 &&
6205                 key.indexOf('source_ref') !== 0 && // purposely exclude colon
6206                 key.indexOf('tiger:') !== 0;
6207         }
6208
6209         var osmAreaKeys = {};
6210         function osmSetAreaKeys(value) {
6211             osmAreaKeys = value;
6212         }
6213
6214         // returns an object with the tag from `tags` that implies an area geometry, if any
6215         function osmTagSuggestingArea(tags) {
6216             if (tags.area === 'yes') return { area: 'yes' };
6217             if (tags.area === 'no') return null;
6218
6219             // `highway` and `railway` are typically linear features, but there
6220             // are a few exceptions that should be treated as areas, even in the
6221             // absence of a proper `area=yes` or `areaKeys` tag.. see #4194
6222             var lineKeys = {
6223                 highway: {
6224                     rest_area: true,
6225                     services: true
6226                 },
6227                 railway: {
6228                     roundhouse: true,
6229                     station: true,
6230                     traverser: true,
6231                     turntable: true,
6232                     wash: true
6233                 }
6234             };
6235             var returnTags = {};
6236             for (var key in tags) {
6237                 if (key in osmAreaKeys && !(tags[key] in osmAreaKeys[key])) {
6238                     returnTags[key] = tags[key];
6239                     return returnTags;
6240                 }
6241                 if (key in lineKeys && tags[key] in lineKeys[key]) {
6242                     returnTags[key] = tags[key];
6243                     return returnTags;
6244                 }
6245             }
6246             return null;
6247         }
6248
6249         // Tags that indicate a node can be a standalone point
6250         // e.g. { amenity: { bar: true, parking: true, ... } ... }
6251         var osmPointTags = {};
6252         function osmSetPointTags(value) {
6253             osmPointTags = value;
6254         }
6255         // Tags that indicate a node can be part of a way
6256         // e.g. { amenity: { parking: true, ... }, highway: { stop: true ... } ... }
6257         var osmVertexTags = {};
6258         function osmSetVertexTags(value) {
6259             osmVertexTags = value;
6260         }
6261
6262         function osmNodeGeometriesForTags(nodeTags) {
6263             var geometries = {};
6264             for (var key in nodeTags) {
6265                 if (osmPointTags[key] &&
6266                     (osmPointTags[key]['*'] || osmPointTags[key][nodeTags[key]])) {
6267                     geometries.point = true;
6268                 }
6269                 if (osmVertexTags[key] &&
6270                     (osmVertexTags[key]['*'] || osmVertexTags[key][nodeTags[key]])) {
6271                     geometries.vertex = true;
6272                 }
6273                 // break early if both are already supported
6274                 if (geometries.point && geometries.vertex) break;
6275             }
6276             return geometries;
6277         }
6278
6279         var osmOneWayTags = {
6280             'aerialway': {
6281                 'chair_lift': true,
6282                 'drag_lift': true,
6283                 'j-bar': true,
6284                 'magic_carpet': true,
6285                 'mixed_lift': true,
6286                 'platter': true,
6287                 'rope_tow': true,
6288                 't-bar': true,
6289                 'zip_line': true
6290             },
6291             'highway': {
6292                 'motorway': true
6293             },
6294             'junction': {
6295                 'circular': true,
6296                 'roundabout': true
6297             },
6298             'man_made': {
6299                 'goods_conveyor': true,
6300                 'piste:halfpipe': true
6301             },
6302             'piste:type': {
6303                 'downhill': true,
6304                 'sled': true,
6305                 'yes': true
6306             },
6307             'waterway': {
6308                 'canal': true,
6309                 'ditch': true,
6310                 'drain': true,
6311                 'fish_pass': true,
6312                 'river': true,
6313                 'stream': true,
6314                 'tidal_channel': true
6315             }
6316         };
6317
6318         // solid and smooth surfaces akin to the assumed default road surface in OSM
6319         var osmPavedTags = {
6320             'surface': {
6321                 'paved': true,
6322                 'asphalt': true,
6323                 'concrete': true,
6324                 'concrete:lanes': true,
6325                 'concrete:plates': true
6326             },
6327             'tracktype': {
6328                 'grade1': true
6329             }
6330         };
6331
6332         // solid, if somewhat uncommon surfaces with a high range of smoothness
6333         var osmSemipavedTags = {
6334             'surface': {
6335                 'cobblestone': true,
6336                 'cobblestone:flattened': true,
6337                 'unhewn_cobblestone': true,
6338                 'sett': true,
6339                 'paving_stones': true,
6340                 'metal': true,
6341                 'wood': true
6342             }
6343         };
6344
6345         var osmRightSideIsInsideTags = {
6346             'natural': {
6347                 'cliff': true,
6348                 'coastline': 'coastline',
6349             },
6350             'barrier': {
6351                 'retaining_wall': true,
6352                 'kerb': true,
6353                 'guard_rail': true,
6354                 'city_wall': true,
6355             },
6356             'man_made': {
6357                 'embankment': true
6358             },
6359             'waterway': {
6360                 'weir': true
6361             }
6362         };
6363
6364         // "highway" tag values for pedestrian or vehicle right-of-ways that make up the routable network
6365         // (does not include `raceway`)
6366         var osmRoutableHighwayTagValues = {
6367             motorway: true, trunk: true, primary: true, secondary: true, tertiary: true, residential: true,
6368             motorway_link: true, trunk_link: true, primary_link: true, secondary_link: true, tertiary_link: true,
6369             unclassified: true, road: true, service: true, track: true, living_street: true, bus_guideway: true,
6370             path: true, footway: true, cycleway: true, bridleway: true, pedestrian: true, corridor: true, steps: true
6371         };
6372         // "highway" tag values that generally do not allow motor vehicles
6373         var osmPathHighwayTagValues = {
6374             path: true, footway: true, cycleway: true, bridleway: true, pedestrian: true, corridor: true, steps: true
6375         };
6376
6377         // "railway" tag values representing existing railroad tracks (purposely does not include 'abandoned')
6378         var osmRailwayTrackTagValues = {
6379             rail: true, light_rail: true, tram: true, subway: true,
6380             monorail: true, funicular: true, miniature: true, narrow_gauge: true,
6381             disused: true, preserved: true
6382         };
6383
6384         // "waterway" tag values for line features representing water flow
6385         var osmFlowingWaterwayTagValues = {
6386             canal: true, ditch: true, drain: true, fish_pass: true, river: true, stream: true, tidal_channel: true
6387         };
6388
6389         // Adds floating point numbers with twice the normal precision.
6390         // Reference: J. R. Shewchuk, Adaptive Precision Floating-Point Arithmetic and
6391         // Fast Robust Geometric Predicates, Discrete & Computational Geometry 18(3)
6392         // 305–363 (1997).
6393         // Code adapted from GeographicLib by Charles F. F. Karney,
6394         // http://geographiclib.sourceforge.net/
6395
6396         function adder() {
6397           return new Adder;
6398         }
6399
6400         function Adder() {
6401           this.reset();
6402         }
6403
6404         Adder.prototype = {
6405           constructor: Adder,
6406           reset: function() {
6407             this.s = // rounded value
6408             this.t = 0; // exact error
6409           },
6410           add: function(y) {
6411             add(temp, y, this.t);
6412             add(this, temp.s, this.s);
6413             if (this.s) this.t += temp.t;
6414             else this.s = temp.t;
6415           },
6416           valueOf: function() {
6417             return this.s;
6418           }
6419         };
6420
6421         var temp = new Adder;
6422
6423         function add(adder, a, b) {
6424           var x = adder.s = a + b,
6425               bv = x - a,
6426               av = x - bv;
6427           adder.t = (a - av) + (b - bv);
6428         }
6429
6430         var epsilon = 1e-6;
6431         var epsilon2 = 1e-12;
6432         var pi = Math.PI;
6433         var halfPi = pi / 2;
6434         var quarterPi = pi / 4;
6435         var tau = pi * 2;
6436
6437         var degrees = 180 / pi;
6438         var radians = pi / 180;
6439
6440         var abs$2 = Math.abs;
6441         var atan = Math.atan;
6442         var atan2 = Math.atan2;
6443         var cos = Math.cos;
6444         var exp = Math.exp;
6445         var log = Math.log;
6446         var sin = Math.sin;
6447         var sign$2 = Math.sign || function(x) { return x > 0 ? 1 : x < 0 ? -1 : 0; };
6448         var sqrt = Math.sqrt;
6449         var tan = Math.tan;
6450
6451         function acos(x) {
6452           return x > 1 ? 0 : x < -1 ? pi : Math.acos(x);
6453         }
6454
6455         function asin(x) {
6456           return x > 1 ? halfPi : x < -1 ? -halfPi : Math.asin(x);
6457         }
6458
6459         function noop$2() {}
6460
6461         function streamGeometry(geometry, stream) {
6462           if (geometry && streamGeometryType.hasOwnProperty(geometry.type)) {
6463             streamGeometryType[geometry.type](geometry, stream);
6464           }
6465         }
6466
6467         var streamObjectType = {
6468           Feature: function(object, stream) {
6469             streamGeometry(object.geometry, stream);
6470           },
6471           FeatureCollection: function(object, stream) {
6472             var features = object.features, i = -1, n = features.length;
6473             while (++i < n) streamGeometry(features[i].geometry, stream);
6474           }
6475         };
6476
6477         var streamGeometryType = {
6478           Sphere: function(object, stream) {
6479             stream.sphere();
6480           },
6481           Point: function(object, stream) {
6482             object = object.coordinates;
6483             stream.point(object[0], object[1], object[2]);
6484           },
6485           MultiPoint: function(object, stream) {
6486             var coordinates = object.coordinates, i = -1, n = coordinates.length;
6487             while (++i < n) object = coordinates[i], stream.point(object[0], object[1], object[2]);
6488           },
6489           LineString: function(object, stream) {
6490             streamLine(object.coordinates, stream, 0);
6491           },
6492           MultiLineString: function(object, stream) {
6493             var coordinates = object.coordinates, i = -1, n = coordinates.length;
6494             while (++i < n) streamLine(coordinates[i], stream, 0);
6495           },
6496           Polygon: function(object, stream) {
6497             streamPolygon(object.coordinates, stream);
6498           },
6499           MultiPolygon: function(object, stream) {
6500             var coordinates = object.coordinates, i = -1, n = coordinates.length;
6501             while (++i < n) streamPolygon(coordinates[i], stream);
6502           },
6503           GeometryCollection: function(object, stream) {
6504             var geometries = object.geometries, i = -1, n = geometries.length;
6505             while (++i < n) streamGeometry(geometries[i], stream);
6506           }
6507         };
6508
6509         function streamLine(coordinates, stream, closed) {
6510           var i = -1, n = coordinates.length - closed, coordinate;
6511           stream.lineStart();
6512           while (++i < n) coordinate = coordinates[i], stream.point(coordinate[0], coordinate[1], coordinate[2]);
6513           stream.lineEnd();
6514         }
6515
6516         function streamPolygon(coordinates, stream) {
6517           var i = -1, n = coordinates.length;
6518           stream.polygonStart();
6519           while (++i < n) streamLine(coordinates[i], stream, 1);
6520           stream.polygonEnd();
6521         }
6522
6523         function d3_geoStream(object, stream) {
6524           if (object && streamObjectType.hasOwnProperty(object.type)) {
6525             streamObjectType[object.type](object, stream);
6526           } else {
6527             streamGeometry(object, stream);
6528           }
6529         }
6530
6531         var areaRingSum = adder();
6532
6533         var areaSum = adder(),
6534             lambda00,
6535             phi00,
6536             lambda0,
6537             cosPhi0,
6538             sinPhi0;
6539
6540         var areaStream = {
6541           point: noop$2,
6542           lineStart: noop$2,
6543           lineEnd: noop$2,
6544           polygonStart: function() {
6545             areaRingSum.reset();
6546             areaStream.lineStart = areaRingStart;
6547             areaStream.lineEnd = areaRingEnd;
6548           },
6549           polygonEnd: function() {
6550             var areaRing = +areaRingSum;
6551             areaSum.add(areaRing < 0 ? tau + areaRing : areaRing);
6552             this.lineStart = this.lineEnd = this.point = noop$2;
6553           },
6554           sphere: function() {
6555             areaSum.add(tau);
6556           }
6557         };
6558
6559         function areaRingStart() {
6560           areaStream.point = areaPointFirst;
6561         }
6562
6563         function areaRingEnd() {
6564           areaPoint(lambda00, phi00);
6565         }
6566
6567         function areaPointFirst(lambda, phi) {
6568           areaStream.point = areaPoint;
6569           lambda00 = lambda, phi00 = phi;
6570           lambda *= radians, phi *= radians;
6571           lambda0 = lambda, cosPhi0 = cos(phi = phi / 2 + quarterPi), sinPhi0 = sin(phi);
6572         }
6573
6574         function areaPoint(lambda, phi) {
6575           lambda *= radians, phi *= radians;
6576           phi = phi / 2 + quarterPi; // half the angular distance from south pole
6577
6578           // Spherical excess E for a spherical triangle with vertices: south pole,
6579           // previous point, current point.  Uses a formula derived from Cagnoli’s
6580           // theorem.  See Todhunter, Spherical Trig. (1871), Sec. 103, Eq. (2).
6581           var dLambda = lambda - lambda0,
6582               sdLambda = dLambda >= 0 ? 1 : -1,
6583               adLambda = sdLambda * dLambda,
6584               cosPhi = cos(phi),
6585               sinPhi = sin(phi),
6586               k = sinPhi0 * sinPhi,
6587               u = cosPhi0 * cosPhi + k * cos(adLambda),
6588               v = k * sdLambda * sin(adLambda);
6589           areaRingSum.add(atan2(v, u));
6590
6591           // Advance the previous points.
6592           lambda0 = lambda, cosPhi0 = cosPhi, sinPhi0 = sinPhi;
6593         }
6594
6595         function d3_geoArea(object) {
6596           areaSum.reset();
6597           d3_geoStream(object, areaStream);
6598           return areaSum * 2;
6599         }
6600
6601         function spherical(cartesian) {
6602           return [atan2(cartesian[1], cartesian[0]), asin(cartesian[2])];
6603         }
6604
6605         function cartesian(spherical) {
6606           var lambda = spherical[0], phi = spherical[1], cosPhi = cos(phi);
6607           return [cosPhi * cos(lambda), cosPhi * sin(lambda), sin(phi)];
6608         }
6609
6610         function cartesianDot(a, b) {
6611           return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
6612         }
6613
6614         function cartesianCross(a, b) {
6615           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]];
6616         }
6617
6618         // TODO return a
6619         function cartesianAddInPlace(a, b) {
6620           a[0] += b[0], a[1] += b[1], a[2] += b[2];
6621         }
6622
6623         function cartesianScale(vector, k) {
6624           return [vector[0] * k, vector[1] * k, vector[2] * k];
6625         }
6626
6627         // TODO return d
6628         function cartesianNormalizeInPlace(d) {
6629           var l = sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]);
6630           d[0] /= l, d[1] /= l, d[2] /= l;
6631         }
6632
6633         var lambda0$1, phi0, lambda1, phi1, // bounds
6634             lambda2, // previous lambda-coordinate
6635             lambda00$1, phi00$1, // first point
6636             p0, // previous 3D point
6637             deltaSum = adder(),
6638             ranges,
6639             range;
6640
6641         var boundsStream = {
6642           point: boundsPoint,
6643           lineStart: boundsLineStart,
6644           lineEnd: boundsLineEnd,
6645           polygonStart: function() {
6646             boundsStream.point = boundsRingPoint;
6647             boundsStream.lineStart = boundsRingStart;
6648             boundsStream.lineEnd = boundsRingEnd;
6649             deltaSum.reset();
6650             areaStream.polygonStart();
6651           },
6652           polygonEnd: function() {
6653             areaStream.polygonEnd();
6654             boundsStream.point = boundsPoint;
6655             boundsStream.lineStart = boundsLineStart;
6656             boundsStream.lineEnd = boundsLineEnd;
6657             if (areaRingSum < 0) lambda0$1 = -(lambda1 = 180), phi0 = -(phi1 = 90);
6658             else if (deltaSum > epsilon) phi1 = 90;
6659             else if (deltaSum < -epsilon) phi0 = -90;
6660             range[0] = lambda0$1, range[1] = lambda1;
6661           },
6662           sphere: function() {
6663             lambda0$1 = -(lambda1 = 180), phi0 = -(phi1 = 90);
6664           }
6665         };
6666
6667         function boundsPoint(lambda, phi) {
6668           ranges.push(range = [lambda0$1 = lambda, lambda1 = lambda]);
6669           if (phi < phi0) phi0 = phi;
6670           if (phi > phi1) phi1 = phi;
6671         }
6672
6673         function linePoint(lambda, phi) {
6674           var p = cartesian([lambda * radians, phi * radians]);
6675           if (p0) {
6676             var normal = cartesianCross(p0, p),
6677                 equatorial = [normal[1], -normal[0], 0],
6678                 inflection = cartesianCross(equatorial, normal);
6679             cartesianNormalizeInPlace(inflection);
6680             inflection = spherical(inflection);
6681             var delta = lambda - lambda2,
6682                 sign = delta > 0 ? 1 : -1,
6683                 lambdai = inflection[0] * degrees * sign,
6684                 phii,
6685                 antimeridian = abs$2(delta) > 180;
6686             if (antimeridian ^ (sign * lambda2 < lambdai && lambdai < sign * lambda)) {
6687               phii = inflection[1] * degrees;
6688               if (phii > phi1) phi1 = phii;
6689             } else if (lambdai = (lambdai + 360) % 360 - 180, antimeridian ^ (sign * lambda2 < lambdai && lambdai < sign * lambda)) {
6690               phii = -inflection[1] * degrees;
6691               if (phii < phi0) phi0 = phii;
6692             } else {
6693               if (phi < phi0) phi0 = phi;
6694               if (phi > phi1) phi1 = phi;
6695             }
6696             if (antimeridian) {
6697               if (lambda < lambda2) {
6698                 if (angle(lambda0$1, lambda) > angle(lambda0$1, lambda1)) lambda1 = lambda;
6699               } else {
6700                 if (angle(lambda, lambda1) > angle(lambda0$1, lambda1)) lambda0$1 = lambda;
6701               }
6702             } else {
6703               if (lambda1 >= lambda0$1) {
6704                 if (lambda < lambda0$1) lambda0$1 = lambda;
6705                 if (lambda > lambda1) lambda1 = lambda;
6706               } else {
6707                 if (lambda > lambda2) {
6708                   if (angle(lambda0$1, lambda) > angle(lambda0$1, lambda1)) lambda1 = lambda;
6709                 } else {
6710                   if (angle(lambda, lambda1) > angle(lambda0$1, lambda1)) lambda0$1 = lambda;
6711                 }
6712               }
6713             }
6714           } else {
6715             ranges.push(range = [lambda0$1 = lambda, lambda1 = lambda]);
6716           }
6717           if (phi < phi0) phi0 = phi;
6718           if (phi > phi1) phi1 = phi;
6719           p0 = p, lambda2 = lambda;
6720         }
6721
6722         function boundsLineStart() {
6723           boundsStream.point = linePoint;
6724         }
6725
6726         function boundsLineEnd() {
6727           range[0] = lambda0$1, range[1] = lambda1;
6728           boundsStream.point = boundsPoint;
6729           p0 = null;
6730         }
6731
6732         function boundsRingPoint(lambda, phi) {
6733           if (p0) {
6734             var delta = lambda - lambda2;
6735             deltaSum.add(abs$2(delta) > 180 ? delta + (delta > 0 ? 360 : -360) : delta);
6736           } else {
6737             lambda00$1 = lambda, phi00$1 = phi;
6738           }
6739           areaStream.point(lambda, phi);
6740           linePoint(lambda, phi);
6741         }
6742
6743         function boundsRingStart() {
6744           areaStream.lineStart();
6745         }
6746
6747         function boundsRingEnd() {
6748           boundsRingPoint(lambda00$1, phi00$1);
6749           areaStream.lineEnd();
6750           if (abs$2(deltaSum) > epsilon) lambda0$1 = -(lambda1 = 180);
6751           range[0] = lambda0$1, range[1] = lambda1;
6752           p0 = null;
6753         }
6754
6755         // Finds the left-right distance between two longitudes.
6756         // This is almost the same as (lambda1 - lambda0 + 360°) % 360°, except that we want
6757         // the distance between ±180° to be 360°.
6758         function angle(lambda0, lambda1) {
6759           return (lambda1 -= lambda0) < 0 ? lambda1 + 360 : lambda1;
6760         }
6761
6762         function rangeCompare(a, b) {
6763           return a[0] - b[0];
6764         }
6765
6766         function rangeContains(range, x) {
6767           return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x;
6768         }
6769
6770         function d3_geoBounds(feature) {
6771           var i, n, a, b, merged, deltaMax, delta;
6772
6773           phi1 = lambda1 = -(lambda0$1 = phi0 = Infinity);
6774           ranges = [];
6775           d3_geoStream(feature, boundsStream);
6776
6777           // First, sort ranges by their minimum longitudes.
6778           if (n = ranges.length) {
6779             ranges.sort(rangeCompare);
6780
6781             // Then, merge any ranges that overlap.
6782             for (i = 1, a = ranges[0], merged = [a]; i < n; ++i) {
6783               b = ranges[i];
6784               if (rangeContains(a, b[0]) || rangeContains(a, b[1])) {
6785                 if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1];
6786                 if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0];
6787               } else {
6788                 merged.push(a = b);
6789               }
6790             }
6791
6792             // Finally, find the largest gap between the merged ranges.
6793             // The final bounding box will be the inverse of this gap.
6794             for (deltaMax = -Infinity, n = merged.length - 1, i = 0, a = merged[n]; i <= n; a = b, ++i) {
6795               b = merged[i];
6796               if ((delta = angle(a[1], b[0])) > deltaMax) deltaMax = delta, lambda0$1 = b[0], lambda1 = a[1];
6797             }
6798           }
6799
6800           ranges = range = null;
6801
6802           return lambda0$1 === Infinity || phi0 === Infinity
6803               ? [[NaN, NaN], [NaN, NaN]]
6804               : [[lambda0$1, phi0], [lambda1, phi1]];
6805         }
6806
6807         var W0, W1,
6808             X0, Y0, Z0,
6809             X1, Y1, Z1,
6810             X2, Y2, Z2,
6811             lambda00$2, phi00$2, // first point
6812             x0, y0, z0; // previous point
6813
6814         var centroidStream = {
6815           sphere: noop$2,
6816           point: centroidPoint,
6817           lineStart: centroidLineStart,
6818           lineEnd: centroidLineEnd,
6819           polygonStart: function() {
6820             centroidStream.lineStart = centroidRingStart;
6821             centroidStream.lineEnd = centroidRingEnd;
6822           },
6823           polygonEnd: function() {
6824             centroidStream.lineStart = centroidLineStart;
6825             centroidStream.lineEnd = centroidLineEnd;
6826           }
6827         };
6828
6829         // Arithmetic mean of Cartesian vectors.
6830         function centroidPoint(lambda, phi) {
6831           lambda *= radians, phi *= radians;
6832           var cosPhi = cos(phi);
6833           centroidPointCartesian(cosPhi * cos(lambda), cosPhi * sin(lambda), sin(phi));
6834         }
6835
6836         function centroidPointCartesian(x, y, z) {
6837           ++W0;
6838           X0 += (x - X0) / W0;
6839           Y0 += (y - Y0) / W0;
6840           Z0 += (z - Z0) / W0;
6841         }
6842
6843         function centroidLineStart() {
6844           centroidStream.point = centroidLinePointFirst;
6845         }
6846
6847         function centroidLinePointFirst(lambda, phi) {
6848           lambda *= radians, phi *= radians;
6849           var cosPhi = cos(phi);
6850           x0 = cosPhi * cos(lambda);
6851           y0 = cosPhi * sin(lambda);
6852           z0 = sin(phi);
6853           centroidStream.point = centroidLinePoint;
6854           centroidPointCartesian(x0, y0, z0);
6855         }
6856
6857         function centroidLinePoint(lambda, phi) {
6858           lambda *= radians, phi *= radians;
6859           var cosPhi = cos(phi),
6860               x = cosPhi * cos(lambda),
6861               y = cosPhi * sin(lambda),
6862               z = sin(phi),
6863               w = atan2(sqrt((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w), x0 * x + y0 * y + z0 * z);
6864           W1 += w;
6865           X1 += w * (x0 + (x0 = x));
6866           Y1 += w * (y0 + (y0 = y));
6867           Z1 += w * (z0 + (z0 = z));
6868           centroidPointCartesian(x0, y0, z0);
6869         }
6870
6871         function centroidLineEnd() {
6872           centroidStream.point = centroidPoint;
6873         }
6874
6875         // See J. E. Brock, The Inertia Tensor for a Spherical Triangle,
6876         // J. Applied Mechanics 42, 239 (1975).
6877         function centroidRingStart() {
6878           centroidStream.point = centroidRingPointFirst;
6879         }
6880
6881         function centroidRingEnd() {
6882           centroidRingPoint(lambda00$2, phi00$2);
6883           centroidStream.point = centroidPoint;
6884         }
6885
6886         function centroidRingPointFirst(lambda, phi) {
6887           lambda00$2 = lambda, phi00$2 = phi;
6888           lambda *= radians, phi *= radians;
6889           centroidStream.point = centroidRingPoint;
6890           var cosPhi = cos(phi);
6891           x0 = cosPhi * cos(lambda);
6892           y0 = cosPhi * sin(lambda);
6893           z0 = sin(phi);
6894           centroidPointCartesian(x0, y0, z0);
6895         }
6896
6897         function centroidRingPoint(lambda, phi) {
6898           lambda *= radians, phi *= radians;
6899           var cosPhi = cos(phi),
6900               x = cosPhi * cos(lambda),
6901               y = cosPhi * sin(lambda),
6902               z = sin(phi),
6903               cx = y0 * z - z0 * y,
6904               cy = z0 * x - x0 * z,
6905               cz = x0 * y - y0 * x,
6906               m = sqrt(cx * cx + cy * cy + cz * cz),
6907               w = asin(m), // line weight = angle
6908               v = m && -w / m; // area weight multiplier
6909           X2 += v * cx;
6910           Y2 += v * cy;
6911           Z2 += v * cz;
6912           W1 += w;
6913           X1 += w * (x0 + (x0 = x));
6914           Y1 += w * (y0 + (y0 = y));
6915           Z1 += w * (z0 + (z0 = z));
6916           centroidPointCartesian(x0, y0, z0);
6917         }
6918
6919         function d3_geoCentroid(object) {
6920           W0 = W1 =
6921           X0 = Y0 = Z0 =
6922           X1 = Y1 = Z1 =
6923           X2 = Y2 = Z2 = 0;
6924           d3_geoStream(object, centroidStream);
6925
6926           var x = X2,
6927               y = Y2,
6928               z = Z2,
6929               m = x * x + y * y + z * z;
6930
6931           // If the area-weighted ccentroid is undefined, fall back to length-weighted ccentroid.
6932           if (m < epsilon2) {
6933             x = X1, y = Y1, z = Z1;
6934             // If the feature has zero length, fall back to arithmetic mean of point vectors.
6935             if (W1 < epsilon) x = X0, y = Y0, z = Z0;
6936             m = x * x + y * y + z * z;
6937             // If the feature still has an undefined ccentroid, then return.
6938             if (m < epsilon2) return [NaN, NaN];
6939           }
6940
6941           return [atan2(y, x) * degrees, asin(z / sqrt(m)) * degrees];
6942         }
6943
6944         function compose(a, b) {
6945
6946           function compose(x, y) {
6947             return x = a(x, y), b(x[0], x[1]);
6948           }
6949
6950           if (a.invert && b.invert) compose.invert = function(x, y) {
6951             return x = b.invert(x, y), x && a.invert(x[0], x[1]);
6952           };
6953
6954           return compose;
6955         }
6956
6957         function rotationIdentity(lambda, phi) {
6958           return [abs$2(lambda) > pi ? lambda + Math.round(-lambda / tau) * tau : lambda, phi];
6959         }
6960
6961         rotationIdentity.invert = rotationIdentity;
6962
6963         function rotateRadians(deltaLambda, deltaPhi, deltaGamma) {
6964           return (deltaLambda %= tau) ? (deltaPhi || deltaGamma ? compose(rotationLambda(deltaLambda), rotationPhiGamma(deltaPhi, deltaGamma))
6965             : rotationLambda(deltaLambda))
6966             : (deltaPhi || deltaGamma ? rotationPhiGamma(deltaPhi, deltaGamma)
6967             : rotationIdentity);
6968         }
6969
6970         function forwardRotationLambda(deltaLambda) {
6971           return function(lambda, phi) {
6972             return lambda += deltaLambda, [lambda > pi ? lambda - tau : lambda < -pi ? lambda + tau : lambda, phi];
6973           };
6974         }
6975
6976         function rotationLambda(deltaLambda) {
6977           var rotation = forwardRotationLambda(deltaLambda);
6978           rotation.invert = forwardRotationLambda(-deltaLambda);
6979           return rotation;
6980         }
6981
6982         function rotationPhiGamma(deltaPhi, deltaGamma) {
6983           var cosDeltaPhi = cos(deltaPhi),
6984               sinDeltaPhi = sin(deltaPhi),
6985               cosDeltaGamma = cos(deltaGamma),
6986               sinDeltaGamma = sin(deltaGamma);
6987
6988           function rotation(lambda, phi) {
6989             var cosPhi = cos(phi),
6990                 x = cos(lambda) * cosPhi,
6991                 y = sin(lambda) * cosPhi,
6992                 z = sin(phi),
6993                 k = z * cosDeltaPhi + x * sinDeltaPhi;
6994             return [
6995               atan2(y * cosDeltaGamma - k * sinDeltaGamma, x * cosDeltaPhi - z * sinDeltaPhi),
6996               asin(k * cosDeltaGamma + y * sinDeltaGamma)
6997             ];
6998           }
6999
7000           rotation.invert = function(lambda, phi) {
7001             var cosPhi = cos(phi),
7002                 x = cos(lambda) * cosPhi,
7003                 y = sin(lambda) * cosPhi,
7004                 z = sin(phi),
7005                 k = z * cosDeltaGamma - y * sinDeltaGamma;
7006             return [
7007               atan2(y * cosDeltaGamma + z * sinDeltaGamma, x * cosDeltaPhi + k * sinDeltaPhi),
7008               asin(k * cosDeltaPhi - x * sinDeltaPhi)
7009             ];
7010           };
7011
7012           return rotation;
7013         }
7014
7015         function rotation(rotate) {
7016           rotate = rotateRadians(rotate[0] * radians, rotate[1] * radians, rotate.length > 2 ? rotate[2] * radians : 0);
7017
7018           function forward(coordinates) {
7019             coordinates = rotate(coordinates[0] * radians, coordinates[1] * radians);
7020             return coordinates[0] *= degrees, coordinates[1] *= degrees, coordinates;
7021           }
7022
7023           forward.invert = function(coordinates) {
7024             coordinates = rotate.invert(coordinates[0] * radians, coordinates[1] * radians);
7025             return coordinates[0] *= degrees, coordinates[1] *= degrees, coordinates;
7026           };
7027
7028           return forward;
7029         }
7030
7031         // Generates a circle centered at [0°, 0°], with a given radius and precision.
7032         function circleStream(stream, radius, delta, direction, t0, t1) {
7033           if (!delta) return;
7034           var cosRadius = cos(radius),
7035               sinRadius = sin(radius),
7036               step = direction * delta;
7037           if (t0 == null) {
7038             t0 = radius + direction * tau;
7039             t1 = radius - step / 2;
7040           } else {
7041             t0 = circleRadius(cosRadius, t0);
7042             t1 = circleRadius(cosRadius, t1);
7043             if (direction > 0 ? t0 < t1 : t0 > t1) t0 += direction * tau;
7044           }
7045           for (var point, t = t0; direction > 0 ? t > t1 : t < t1; t -= step) {
7046             point = spherical([cosRadius, -sinRadius * cos(t), -sinRadius * sin(t)]);
7047             stream.point(point[0], point[1]);
7048           }
7049         }
7050
7051         // Returns the signed angle of a cartesian point relative to [cosRadius, 0, 0].
7052         function circleRadius(cosRadius, point) {
7053           point = cartesian(point), point[0] -= cosRadius;
7054           cartesianNormalizeInPlace(point);
7055           var radius = acos(-point[1]);
7056           return ((-point[2] < 0 ? -radius : radius) + tau - epsilon) % tau;
7057         }
7058
7059         function clipBuffer() {
7060           var lines = [],
7061               line;
7062           return {
7063             point: function(x, y, m) {
7064               line.push([x, y, m]);
7065             },
7066             lineStart: function() {
7067               lines.push(line = []);
7068             },
7069             lineEnd: noop$2,
7070             rejoin: function() {
7071               if (lines.length > 1) lines.push(lines.pop().concat(lines.shift()));
7072             },
7073             result: function() {
7074               var result = lines;
7075               lines = [];
7076               line = null;
7077               return result;
7078             }
7079           };
7080         }
7081
7082         function pointEqual(a, b) {
7083           return abs$2(a[0] - b[0]) < epsilon && abs$2(a[1] - b[1]) < epsilon;
7084         }
7085
7086         function Intersection(point, points, other, entry) {
7087           this.x = point;
7088           this.z = points;
7089           this.o = other; // another intersection
7090           this.e = entry; // is an entry?
7091           this.v = false; // visited
7092           this.n = this.p = null; // next & previous
7093         }
7094
7095         // A generalized polygon clipping algorithm: given a polygon that has been cut
7096         // into its visible line segments, and rejoins the segments by interpolating
7097         // along the clip edge.
7098         function clipRejoin(segments, compareIntersection, startInside, interpolate, stream) {
7099           var subject = [],
7100               clip = [],
7101               i,
7102               n;
7103
7104           segments.forEach(function(segment) {
7105             if ((n = segment.length - 1) <= 0) return;
7106             var n, p0 = segment[0], p1 = segment[n], x;
7107
7108             if (pointEqual(p0, p1)) {
7109               if (!p0[2] && !p1[2]) {
7110                 stream.lineStart();
7111                 for (i = 0; i < n; ++i) stream.point((p0 = segment[i])[0], p0[1]);
7112                 stream.lineEnd();
7113                 return;
7114               }
7115               // handle degenerate cases by moving the point
7116               p1[0] += 2 * epsilon;
7117             }
7118
7119             subject.push(x = new Intersection(p0, segment, null, true));
7120             clip.push(x.o = new Intersection(p0, null, x, false));
7121             subject.push(x = new Intersection(p1, segment, null, false));
7122             clip.push(x.o = new Intersection(p1, null, x, true));
7123           });
7124
7125           if (!subject.length) return;
7126
7127           clip.sort(compareIntersection);
7128           link(subject);
7129           link(clip);
7130
7131           for (i = 0, n = clip.length; i < n; ++i) {
7132             clip[i].e = startInside = !startInside;
7133           }
7134
7135           var start = subject[0],
7136               points,
7137               point;
7138
7139           while (1) {
7140             // Find first unvisited intersection.
7141             var current = start,
7142                 isSubject = true;
7143             while (current.v) if ((current = current.n) === start) return;
7144             points = current.z;
7145             stream.lineStart();
7146             do {
7147               current.v = current.o.v = true;
7148               if (current.e) {
7149                 if (isSubject) {
7150                   for (i = 0, n = points.length; i < n; ++i) stream.point((point = points[i])[0], point[1]);
7151                 } else {
7152                   interpolate(current.x, current.n.x, 1, stream);
7153                 }
7154                 current = current.n;
7155               } else {
7156                 if (isSubject) {
7157                   points = current.p.z;
7158                   for (i = points.length - 1; i >= 0; --i) stream.point((point = points[i])[0], point[1]);
7159                 } else {
7160                   interpolate(current.x, current.p.x, -1, stream);
7161                 }
7162                 current = current.p;
7163               }
7164               current = current.o;
7165               points = current.z;
7166               isSubject = !isSubject;
7167             } while (!current.v);
7168             stream.lineEnd();
7169           }
7170         }
7171
7172         function link(array) {
7173           if (!(n = array.length)) return;
7174           var n,
7175               i = 0,
7176               a = array[0],
7177               b;
7178           while (++i < n) {
7179             a.n = b = array[i];
7180             b.p = a;
7181             a = b;
7182           }
7183           a.n = b = array[0];
7184           b.p = a;
7185         }
7186
7187         var sum = adder();
7188
7189         function longitude(point) {
7190           if (abs$2(point[0]) <= pi)
7191             return point[0];
7192           else
7193             return sign$2(point[0]) * ((abs$2(point[0]) + pi) % tau - pi);
7194         }
7195
7196         function polygonContains(polygon, point) {
7197           var lambda = longitude(point),
7198               phi = point[1],
7199               sinPhi = sin(phi),
7200               normal = [sin(lambda), -cos(lambda), 0],
7201               angle = 0,
7202               winding = 0;
7203
7204           sum.reset();
7205
7206           if (sinPhi === 1) phi = halfPi + epsilon;
7207           else if (sinPhi === -1) phi = -halfPi - epsilon;
7208
7209           for (var i = 0, n = polygon.length; i < n; ++i) {
7210             if (!(m = (ring = polygon[i]).length)) continue;
7211             var ring,
7212                 m,
7213                 point0 = ring[m - 1],
7214                 lambda0 = longitude(point0),
7215                 phi0 = point0[1] / 2 + quarterPi,
7216                 sinPhi0 = sin(phi0),
7217                 cosPhi0 = cos(phi0);
7218
7219             for (var j = 0; j < m; ++j, lambda0 = lambda1, sinPhi0 = sinPhi1, cosPhi0 = cosPhi1, point0 = point1) {
7220               var point1 = ring[j],
7221                   lambda1 = longitude(point1),
7222                   phi1 = point1[1] / 2 + quarterPi,
7223                   sinPhi1 = sin(phi1),
7224                   cosPhi1 = cos(phi1),
7225                   delta = lambda1 - lambda0,
7226                   sign = delta >= 0 ? 1 : -1,
7227                   absDelta = sign * delta,
7228                   antimeridian = absDelta > pi,
7229                   k = sinPhi0 * sinPhi1;
7230
7231               sum.add(atan2(k * sign * sin(absDelta), cosPhi0 * cosPhi1 + k * cos(absDelta)));
7232               angle += antimeridian ? delta + sign * tau : delta;
7233
7234               // Are the longitudes either side of the point’s meridian (lambda),
7235               // and are the latitudes smaller than the parallel (phi)?
7236               if (antimeridian ^ lambda0 >= lambda ^ lambda1 >= lambda) {
7237                 var arc = cartesianCross(cartesian(point0), cartesian(point1));
7238                 cartesianNormalizeInPlace(arc);
7239                 var intersection = cartesianCross(normal, arc);
7240                 cartesianNormalizeInPlace(intersection);
7241                 var phiArc = (antimeridian ^ delta >= 0 ? -1 : 1) * asin(intersection[2]);
7242                 if (phi > phiArc || phi === phiArc && (arc[0] || arc[1])) {
7243                   winding += antimeridian ^ delta >= 0 ? 1 : -1;
7244                 }
7245               }
7246             }
7247           }
7248
7249           // First, determine whether the South pole is inside or outside:
7250           //
7251           // It is inside if:
7252           // * the polygon winds around it in a clockwise direction.
7253           // * the polygon does not (cumulatively) wind around it, but has a negative
7254           //   (counter-clockwise) area.
7255           //
7256           // Second, count the (signed) number of times a segment crosses a lambda
7257           // from the point to the South pole.  If it is zero, then the point is the
7258           // same side as the South pole.
7259
7260           return (angle < -epsilon || angle < epsilon && sum < -epsilon) ^ (winding & 1);
7261         }
7262
7263         function d3_ascending(a, b) {
7264           return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
7265         }
7266
7267         function d3_bisector(compare) {
7268           if (compare.length === 1) compare = ascendingComparator(compare);
7269           return {
7270             left: function(a, x, lo, hi) {
7271               if (lo == null) lo = 0;
7272               if (hi == null) hi = a.length;
7273               while (lo < hi) {
7274                 var mid = lo + hi >>> 1;
7275                 if (compare(a[mid], x) < 0) lo = mid + 1;
7276                 else hi = mid;
7277               }
7278               return lo;
7279             },
7280             right: function(a, x, lo, hi) {
7281               if (lo == null) lo = 0;
7282               if (hi == null) hi = a.length;
7283               while (lo < hi) {
7284                 var mid = lo + hi >>> 1;
7285                 if (compare(a[mid], x) > 0) hi = mid;
7286                 else lo = mid + 1;
7287               }
7288               return lo;
7289             }
7290           };
7291         }
7292
7293         function ascendingComparator(f) {
7294           return function(d, x) {
7295             return d3_ascending(f(d), x);
7296           };
7297         }
7298
7299         var ascendingBisect = d3_bisector(d3_ascending);
7300         var bisectRight = ascendingBisect.right;
7301
7302         function d3_descending(a, b) {
7303           return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
7304         }
7305
7306         function number(x) {
7307           return x === null ? NaN : +x;
7308         }
7309
7310         function range$1(start, stop, step) {
7311           start = +start, stop = +stop, step = (n = arguments.length) < 2 ? (stop = start, start = 0, 1) : n < 3 ? 1 : +step;
7312
7313           var i = -1,
7314               n = Math.max(0, Math.ceil((stop - start) / step)) | 0,
7315               range = new Array(n);
7316
7317           while (++i < n) {
7318             range[i] = start + i * step;
7319           }
7320
7321           return range;
7322         }
7323
7324         var e10 = Math.sqrt(50),
7325             e5 = Math.sqrt(10),
7326             e2 = Math.sqrt(2);
7327
7328         function ticks(start, stop, count) {
7329           var reverse,
7330               i = -1,
7331               n,
7332               ticks,
7333               step;
7334
7335           stop = +stop, start = +start, count = +count;
7336           if (start === stop && count > 0) return [start];
7337           if (reverse = stop < start) n = start, start = stop, stop = n;
7338           if ((step = tickIncrement(start, stop, count)) === 0 || !isFinite(step)) return [];
7339
7340           if (step > 0) {
7341             start = Math.ceil(start / step);
7342             stop = Math.floor(stop / step);
7343             ticks = new Array(n = Math.ceil(stop - start + 1));
7344             while (++i < n) ticks[i] = (start + i) * step;
7345           } else {
7346             start = Math.floor(start * step);
7347             stop = Math.ceil(stop * step);
7348             ticks = new Array(n = Math.ceil(start - stop + 1));
7349             while (++i < n) ticks[i] = (start - i) / step;
7350           }
7351
7352           if (reverse) ticks.reverse();
7353
7354           return ticks;
7355         }
7356
7357         function tickIncrement(start, stop, count) {
7358           var step = (stop - start) / Math.max(0, count),
7359               power = Math.floor(Math.log(step) / Math.LN10),
7360               error = step / Math.pow(10, power);
7361           return power >= 0
7362               ? (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1) * Math.pow(10, power)
7363               : -Math.pow(10, -power) / (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1);
7364         }
7365
7366         function tickStep(start, stop, count) {
7367           var step0 = Math.abs(stop - start) / Math.max(0, count),
7368               step1 = Math.pow(10, Math.floor(Math.log(step0) / Math.LN10)),
7369               error = step0 / step1;
7370           if (error >= e10) step1 *= 10;
7371           else if (error >= e5) step1 *= 5;
7372           else if (error >= e2) step1 *= 2;
7373           return stop < start ? -step1 : step1;
7374         }
7375
7376         function threshold(values, p, valueof) {
7377           if (valueof == null) valueof = number;
7378           if (!(n = values.length)) return;
7379           if ((p = +p) <= 0 || n < 2) return +valueof(values[0], 0, values);
7380           if (p >= 1) return +valueof(values[n - 1], n - 1, values);
7381           var n,
7382               i = (n - 1) * p,
7383               i0 = Math.floor(i),
7384               value0 = +valueof(values[i0], i0, values),
7385               value1 = +valueof(values[i0 + 1], i0 + 1, values);
7386           return value0 + (value1 - value0) * (i - i0);
7387         }
7388
7389         function d3_median(values, valueof) {
7390           var n = values.length,
7391               i = -1,
7392               value,
7393               numbers = [];
7394
7395           if (valueof == null) {
7396             while (++i < n) {
7397               if (!isNaN(value = number(values[i]))) {
7398                 numbers.push(value);
7399               }
7400             }
7401           }
7402
7403           else {
7404             while (++i < n) {
7405               if (!isNaN(value = number(valueof(values[i], i, values)))) {
7406                 numbers.push(value);
7407               }
7408             }
7409           }
7410
7411           return threshold(numbers.sort(d3_ascending), 0.5);
7412         }
7413
7414         function merge(arrays) {
7415           var n = arrays.length,
7416               m,
7417               i = -1,
7418               j = 0,
7419               merged,
7420               array;
7421
7422           while (++i < n) j += arrays[i].length;
7423           merged = new Array(j);
7424
7425           while (--n >= 0) {
7426             array = arrays[n];
7427             m = array.length;
7428             while (--m >= 0) {
7429               merged[--j] = array[m];
7430             }
7431           }
7432
7433           return merged;
7434         }
7435
7436         function clip(pointVisible, clipLine, interpolate, start) {
7437           return function(sink) {
7438             var line = clipLine(sink),
7439                 ringBuffer = clipBuffer(),
7440                 ringSink = clipLine(ringBuffer),
7441                 polygonStarted = false,
7442                 polygon,
7443                 segments,
7444                 ring;
7445
7446             var clip = {
7447               point: point,
7448               lineStart: lineStart,
7449               lineEnd: lineEnd,
7450               polygonStart: function() {
7451                 clip.point = pointRing;
7452                 clip.lineStart = ringStart;
7453                 clip.lineEnd = ringEnd;
7454                 segments = [];
7455                 polygon = [];
7456               },
7457               polygonEnd: function() {
7458                 clip.point = point;
7459                 clip.lineStart = lineStart;
7460                 clip.lineEnd = lineEnd;
7461                 segments = merge(segments);
7462                 var startInside = polygonContains(polygon, start);
7463                 if (segments.length) {
7464                   if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
7465                   clipRejoin(segments, compareIntersection, startInside, interpolate, sink);
7466                 } else if (startInside) {
7467                   if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
7468                   sink.lineStart();
7469                   interpolate(null, null, 1, sink);
7470                   sink.lineEnd();
7471                 }
7472                 if (polygonStarted) sink.polygonEnd(), polygonStarted = false;
7473                 segments = polygon = null;
7474               },
7475               sphere: function() {
7476                 sink.polygonStart();
7477                 sink.lineStart();
7478                 interpolate(null, null, 1, sink);
7479                 sink.lineEnd();
7480                 sink.polygonEnd();
7481               }
7482             };
7483
7484             function point(lambda, phi) {
7485               if (pointVisible(lambda, phi)) sink.point(lambda, phi);
7486             }
7487
7488             function pointLine(lambda, phi) {
7489               line.point(lambda, phi);
7490             }
7491
7492             function lineStart() {
7493               clip.point = pointLine;
7494               line.lineStart();
7495             }
7496
7497             function lineEnd() {
7498               clip.point = point;
7499               line.lineEnd();
7500             }
7501
7502             function pointRing(lambda, phi) {
7503               ring.push([lambda, phi]);
7504               ringSink.point(lambda, phi);
7505             }
7506
7507             function ringStart() {
7508               ringSink.lineStart();
7509               ring = [];
7510             }
7511
7512             function ringEnd() {
7513               pointRing(ring[0][0], ring[0][1]);
7514               ringSink.lineEnd();
7515
7516               var clean = ringSink.clean(),
7517                   ringSegments = ringBuffer.result(),
7518                   i, n = ringSegments.length, m,
7519                   segment,
7520                   point;
7521
7522               ring.pop();
7523               polygon.push(ring);
7524               ring = null;
7525
7526               if (!n) return;
7527
7528               // No intersections.
7529               if (clean & 1) {
7530                 segment = ringSegments[0];
7531                 if ((m = segment.length - 1) > 0) {
7532                   if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
7533                   sink.lineStart();
7534                   for (i = 0; i < m; ++i) sink.point((point = segment[i])[0], point[1]);
7535                   sink.lineEnd();
7536                 }
7537                 return;
7538               }
7539
7540               // Rejoin connected segments.
7541               // TODO reuse ringBuffer.rejoin()?
7542               if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift()));
7543
7544               segments.push(ringSegments.filter(validSegment));
7545             }
7546
7547             return clip;
7548           };
7549         }
7550
7551         function validSegment(segment) {
7552           return segment.length > 1;
7553         }
7554
7555         // Intersections are sorted along the clip edge. For both antimeridian cutting
7556         // and circle clipping, the same comparison is used.
7557         function compareIntersection(a, b) {
7558           return ((a = a.x)[0] < 0 ? a[1] - halfPi - epsilon : halfPi - a[1])
7559                - ((b = b.x)[0] < 0 ? b[1] - halfPi - epsilon : halfPi - b[1]);
7560         }
7561
7562         var clipAntimeridian = clip(
7563           function() { return true; },
7564           clipAntimeridianLine,
7565           clipAntimeridianInterpolate,
7566           [-pi, -halfPi]
7567         );
7568
7569         // Takes a line and cuts into visible segments. Return values: 0 - there were
7570         // intersections or the line was empty; 1 - no intersections; 2 - there were
7571         // intersections, and the first and last segments should be rejoined.
7572         function clipAntimeridianLine(stream) {
7573           var lambda0 = NaN,
7574               phi0 = NaN,
7575               sign0 = NaN,
7576               clean; // no intersections
7577
7578           return {
7579             lineStart: function() {
7580               stream.lineStart();
7581               clean = 1;
7582             },
7583             point: function(lambda1, phi1) {
7584               var sign1 = lambda1 > 0 ? pi : -pi,
7585                   delta = abs$2(lambda1 - lambda0);
7586               if (abs$2(delta - pi) < epsilon) { // line crosses a pole
7587                 stream.point(lambda0, phi0 = (phi0 + phi1) / 2 > 0 ? halfPi : -halfPi);
7588                 stream.point(sign0, phi0);
7589                 stream.lineEnd();
7590                 stream.lineStart();
7591                 stream.point(sign1, phi0);
7592                 stream.point(lambda1, phi0);
7593                 clean = 0;
7594               } else if (sign0 !== sign1 && delta >= pi) { // line crosses antimeridian
7595                 if (abs$2(lambda0 - sign0) < epsilon) lambda0 -= sign0 * epsilon; // handle degeneracies
7596                 if (abs$2(lambda1 - sign1) < epsilon) lambda1 -= sign1 * epsilon;
7597                 phi0 = clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1);
7598                 stream.point(sign0, phi0);
7599                 stream.lineEnd();
7600                 stream.lineStart();
7601                 stream.point(sign1, phi0);
7602                 clean = 0;
7603               }
7604               stream.point(lambda0 = lambda1, phi0 = phi1);
7605               sign0 = sign1;
7606             },
7607             lineEnd: function() {
7608               stream.lineEnd();
7609               lambda0 = phi0 = NaN;
7610             },
7611             clean: function() {
7612               return 2 - clean; // if intersections, rejoin first and last segments
7613             }
7614           };
7615         }
7616
7617         function clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1) {
7618           var cosPhi0,
7619               cosPhi1,
7620               sinLambda0Lambda1 = sin(lambda0 - lambda1);
7621           return abs$2(sinLambda0Lambda1) > epsilon
7622               ? atan((sin(phi0) * (cosPhi1 = cos(phi1)) * sin(lambda1)
7623                   - sin(phi1) * (cosPhi0 = cos(phi0)) * sin(lambda0))
7624                   / (cosPhi0 * cosPhi1 * sinLambda0Lambda1))
7625               : (phi0 + phi1) / 2;
7626         }
7627
7628         function clipAntimeridianInterpolate(from, to, direction, stream) {
7629           var phi;
7630           if (from == null) {
7631             phi = direction * halfPi;
7632             stream.point(-pi, phi);
7633             stream.point(0, phi);
7634             stream.point(pi, phi);
7635             stream.point(pi, 0);
7636             stream.point(pi, -phi);
7637             stream.point(0, -phi);
7638             stream.point(-pi, -phi);
7639             stream.point(-pi, 0);
7640             stream.point(-pi, phi);
7641           } else if (abs$2(from[0] - to[0]) > epsilon) {
7642             var lambda = from[0] < to[0] ? pi : -pi;
7643             phi = direction * lambda / 2;
7644             stream.point(-lambda, phi);
7645             stream.point(0, phi);
7646             stream.point(lambda, phi);
7647           } else {
7648             stream.point(to[0], to[1]);
7649           }
7650         }
7651
7652         function clipCircle(radius) {
7653           var cr = cos(radius),
7654               delta = 6 * radians,
7655               smallRadius = cr > 0,
7656               notHemisphere = abs$2(cr) > epsilon; // TODO optimise for this common case
7657
7658           function interpolate(from, to, direction, stream) {
7659             circleStream(stream, radius, delta, direction, from, to);
7660           }
7661
7662           function visible(lambda, phi) {
7663             return cos(lambda) * cos(phi) > cr;
7664           }
7665
7666           // Takes a line and cuts into visible segments. Return values used for polygon
7667           // clipping: 0 - there were intersections or the line was empty; 1 - no
7668           // intersections 2 - there were intersections, and the first and last segments
7669           // should be rejoined.
7670           function clipLine(stream) {
7671             var point0, // previous point
7672                 c0, // code for previous point
7673                 v0, // visibility of previous point
7674                 v00, // visibility of first point
7675                 clean; // no intersections
7676             return {
7677               lineStart: function() {
7678                 v00 = v0 = false;
7679                 clean = 1;
7680               },
7681               point: function(lambda, phi) {
7682                 var point1 = [lambda, phi],
7683                     point2,
7684                     v = visible(lambda, phi),
7685                     c = smallRadius
7686                       ? v ? 0 : code(lambda, phi)
7687                       : v ? code(lambda + (lambda < 0 ? pi : -pi), phi) : 0;
7688                 if (!point0 && (v00 = v0 = v)) stream.lineStart();
7689                 if (v !== v0) {
7690                   point2 = intersect(point0, point1);
7691                   if (!point2 || pointEqual(point0, point2) || pointEqual(point1, point2))
7692                     point1[2] = 1;
7693                 }
7694                 if (v !== v0) {
7695                   clean = 0;
7696                   if (v) {
7697                     // outside going in
7698                     stream.lineStart();
7699                     point2 = intersect(point1, point0);
7700                     stream.point(point2[0], point2[1]);
7701                   } else {
7702                     // inside going out
7703                     point2 = intersect(point0, point1);
7704                     stream.point(point2[0], point2[1], 2);
7705                     stream.lineEnd();
7706                   }
7707                   point0 = point2;
7708                 } else if (notHemisphere && point0 && smallRadius ^ v) {
7709                   var t;
7710                   // If the codes for two points are different, or are both zero,
7711                   // and there this segment intersects with the small circle.
7712                   if (!(c & c0) && (t = intersect(point1, point0, true))) {
7713                     clean = 0;
7714                     if (smallRadius) {
7715                       stream.lineStart();
7716                       stream.point(t[0][0], t[0][1]);
7717                       stream.point(t[1][0], t[1][1]);
7718                       stream.lineEnd();
7719                     } else {
7720                       stream.point(t[1][0], t[1][1]);
7721                       stream.lineEnd();
7722                       stream.lineStart();
7723                       stream.point(t[0][0], t[0][1], 3);
7724                     }
7725                   }
7726                 }
7727                 if (v && (!point0 || !pointEqual(point0, point1))) {
7728                   stream.point(point1[0], point1[1]);
7729                 }
7730                 point0 = point1, v0 = v, c0 = c;
7731               },
7732               lineEnd: function() {
7733                 if (v0) stream.lineEnd();
7734                 point0 = null;
7735               },
7736               // Rejoin first and last segments if there were intersections and the first
7737               // and last points were visible.
7738               clean: function() {
7739                 return clean | ((v00 && v0) << 1);
7740               }
7741             };
7742           }
7743
7744           // Intersects the great circle between a and b with the clip circle.
7745           function intersect(a, b, two) {
7746             var pa = cartesian(a),
7747                 pb = cartesian(b);
7748
7749             // We have two planes, n1.p = d1 and n2.p = d2.
7750             // Find intersection line p(t) = c1 n1 + c2 n2 + t (n1 ⨯ n2).
7751             var n1 = [1, 0, 0], // normal
7752                 n2 = cartesianCross(pa, pb),
7753                 n2n2 = cartesianDot(n2, n2),
7754                 n1n2 = n2[0], // cartesianDot(n1, n2),
7755                 determinant = n2n2 - n1n2 * n1n2;
7756
7757             // Two polar points.
7758             if (!determinant) return !two && a;
7759
7760             var c1 =  cr * n2n2 / determinant,
7761                 c2 = -cr * n1n2 / determinant,
7762                 n1xn2 = cartesianCross(n1, n2),
7763                 A = cartesianScale(n1, c1),
7764                 B = cartesianScale(n2, c2);
7765             cartesianAddInPlace(A, B);
7766
7767             // Solve |p(t)|^2 = 1.
7768             var u = n1xn2,
7769                 w = cartesianDot(A, u),
7770                 uu = cartesianDot(u, u),
7771                 t2 = w * w - uu * (cartesianDot(A, A) - 1);
7772
7773             if (t2 < 0) return;
7774
7775             var t = sqrt(t2),
7776                 q = cartesianScale(u, (-w - t) / uu);
7777             cartesianAddInPlace(q, A);
7778             q = spherical(q);
7779
7780             if (!two) return q;
7781
7782             // Two intersection points.
7783             var lambda0 = a[0],
7784                 lambda1 = b[0],
7785                 phi0 = a[1],
7786                 phi1 = b[1],
7787                 z;
7788
7789             if (lambda1 < lambda0) z = lambda0, lambda0 = lambda1, lambda1 = z;
7790
7791             var delta = lambda1 - lambda0,
7792                 polar = abs$2(delta - pi) < epsilon,
7793                 meridian = polar || delta < epsilon;
7794
7795             if (!polar && phi1 < phi0) z = phi0, phi0 = phi1, phi1 = z;
7796
7797             // Check that the first point is between a and b.
7798             if (meridian
7799                 ? polar
7800                   ? phi0 + phi1 > 0 ^ q[1] < (abs$2(q[0] - lambda0) < epsilon ? phi0 : phi1)
7801                   : phi0 <= q[1] && q[1] <= phi1
7802                 : delta > pi ^ (lambda0 <= q[0] && q[0] <= lambda1)) {
7803               var q1 = cartesianScale(u, (-w + t) / uu);
7804               cartesianAddInPlace(q1, A);
7805               return [q, spherical(q1)];
7806             }
7807           }
7808
7809           // Generates a 4-bit vector representing the location of a point relative to
7810           // the small circle's bounding box.
7811           function code(lambda, phi) {
7812             var r = smallRadius ? radius : pi - radius,
7813                 code = 0;
7814             if (lambda < -r) code |= 1; // left
7815             else if (lambda > r) code |= 2; // right
7816             if (phi < -r) code |= 4; // below
7817             else if (phi > r) code |= 8; // above
7818             return code;
7819           }
7820
7821           return clip(visible, clipLine, interpolate, smallRadius ? [0, -radius] : [-pi, radius - pi]);
7822         }
7823
7824         function clipLine(a, b, x0, y0, x1, y1) {
7825           var ax = a[0],
7826               ay = a[1],
7827               bx = b[0],
7828               by = b[1],
7829               t0 = 0,
7830               t1 = 1,
7831               dx = bx - ax,
7832               dy = by - ay,
7833               r;
7834
7835           r = x0 - ax;
7836           if (!dx && r > 0) return;
7837           r /= dx;
7838           if (dx < 0) {
7839             if (r < t0) return;
7840             if (r < t1) t1 = r;
7841           } else if (dx > 0) {
7842             if (r > t1) return;
7843             if (r > t0) t0 = r;
7844           }
7845
7846           r = x1 - ax;
7847           if (!dx && r < 0) return;
7848           r /= dx;
7849           if (dx < 0) {
7850             if (r > t1) return;
7851             if (r > t0) t0 = r;
7852           } else if (dx > 0) {
7853             if (r < t0) return;
7854             if (r < t1) t1 = r;
7855           }
7856
7857           r = y0 - ay;
7858           if (!dy && r > 0) return;
7859           r /= dy;
7860           if (dy < 0) {
7861             if (r < t0) return;
7862             if (r < t1) t1 = r;
7863           } else if (dy > 0) {
7864             if (r > t1) return;
7865             if (r > t0) t0 = r;
7866           }
7867
7868           r = y1 - ay;
7869           if (!dy && r < 0) return;
7870           r /= dy;
7871           if (dy < 0) {
7872             if (r > t1) return;
7873             if (r > t0) t0 = r;
7874           } else if (dy > 0) {
7875             if (r < t0) return;
7876             if (r < t1) t1 = r;
7877           }
7878
7879           if (t0 > 0) a[0] = ax + t0 * dx, a[1] = ay + t0 * dy;
7880           if (t1 < 1) b[0] = ax + t1 * dx, b[1] = ay + t1 * dy;
7881           return true;
7882         }
7883
7884         var clipMax = 1e9, clipMin = -clipMax;
7885
7886         // TODO Use d3-polygon’s polygonContains here for the ring check?
7887         // TODO Eliminate duplicate buffering in clipBuffer and polygon.push?
7888
7889         function clipRectangle(x0, y0, x1, y1) {
7890
7891           function visible(x, y) {
7892             return x0 <= x && x <= x1 && y0 <= y && y <= y1;
7893           }
7894
7895           function interpolate(from, to, direction, stream) {
7896             var a = 0, a1 = 0;
7897             if (from == null
7898                 || (a = corner(from, direction)) !== (a1 = corner(to, direction))
7899                 || comparePoint(from, to) < 0 ^ direction > 0) {
7900               do stream.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0);
7901               while ((a = (a + direction + 4) % 4) !== a1);
7902             } else {
7903               stream.point(to[0], to[1]);
7904             }
7905           }
7906
7907           function corner(p, direction) {
7908             return abs$2(p[0] - x0) < epsilon ? direction > 0 ? 0 : 3
7909                 : abs$2(p[0] - x1) < epsilon ? direction > 0 ? 2 : 1
7910                 : abs$2(p[1] - y0) < epsilon ? direction > 0 ? 1 : 0
7911                 : direction > 0 ? 3 : 2; // abs(p[1] - y1) < epsilon
7912           }
7913
7914           function compareIntersection(a, b) {
7915             return comparePoint(a.x, b.x);
7916           }
7917
7918           function comparePoint(a, b) {
7919             var ca = corner(a, 1),
7920                 cb = corner(b, 1);
7921             return ca !== cb ? ca - cb
7922                 : ca === 0 ? b[1] - a[1]
7923                 : ca === 1 ? a[0] - b[0]
7924                 : ca === 2 ? a[1] - b[1]
7925                 : b[0] - a[0];
7926           }
7927
7928           return function(stream) {
7929             var activeStream = stream,
7930                 bufferStream = clipBuffer(),
7931                 segments,
7932                 polygon,
7933                 ring,
7934                 x__, y__, v__, // first point
7935                 x_, y_, v_, // previous point
7936                 first,
7937                 clean;
7938
7939             var clipStream = {
7940               point: point,
7941               lineStart: lineStart,
7942               lineEnd: lineEnd,
7943               polygonStart: polygonStart,
7944               polygonEnd: polygonEnd
7945             };
7946
7947             function point(x, y) {
7948               if (visible(x, y)) activeStream.point(x, y);
7949             }
7950
7951             function polygonInside() {
7952               var winding = 0;
7953
7954               for (var i = 0, n = polygon.length; i < n; ++i) {
7955                 for (var ring = polygon[i], j = 1, m = ring.length, point = ring[0], a0, a1, b0 = point[0], b1 = point[1]; j < m; ++j) {
7956                   a0 = b0, a1 = b1, point = ring[j], b0 = point[0], b1 = point[1];
7957                   if (a1 <= y1) { if (b1 > y1 && (b0 - a0) * (y1 - a1) > (b1 - a1) * (x0 - a0)) ++winding; }
7958                   else { if (b1 <= y1 && (b0 - a0) * (y1 - a1) < (b1 - a1) * (x0 - a0)) --winding; }
7959                 }
7960               }
7961
7962               return winding;
7963             }
7964
7965             // Buffer geometry within a polygon and then clip it en masse.
7966             function polygonStart() {
7967               activeStream = bufferStream, segments = [], polygon = [], clean = true;
7968             }
7969
7970             function polygonEnd() {
7971               var startInside = polygonInside(),
7972                   cleanInside = clean && startInside,
7973                   visible = (segments = merge(segments)).length;
7974               if (cleanInside || visible) {
7975                 stream.polygonStart();
7976                 if (cleanInside) {
7977                   stream.lineStart();
7978                   interpolate(null, null, 1, stream);
7979                   stream.lineEnd();
7980                 }
7981                 if (visible) {
7982                   clipRejoin(segments, compareIntersection, startInside, interpolate, stream);
7983                 }
7984                 stream.polygonEnd();
7985               }
7986               activeStream = stream, segments = polygon = ring = null;
7987             }
7988
7989             function lineStart() {
7990               clipStream.point = linePoint;
7991               if (polygon) polygon.push(ring = []);
7992               first = true;
7993               v_ = false;
7994               x_ = y_ = NaN;
7995             }
7996
7997             // TODO rather than special-case polygons, simply handle them separately.
7998             // Ideally, coincident intersection points should be jittered to avoid
7999             // clipping issues.
8000             function lineEnd() {
8001               if (segments) {
8002                 linePoint(x__, y__);
8003                 if (v__ && v_) bufferStream.rejoin();
8004                 segments.push(bufferStream.result());
8005               }
8006               clipStream.point = point;
8007               if (v_) activeStream.lineEnd();
8008             }
8009
8010             function linePoint(x, y) {
8011               var v = visible(x, y);
8012               if (polygon) ring.push([x, y]);
8013               if (first) {
8014                 x__ = x, y__ = y, v__ = v;
8015                 first = false;
8016                 if (v) {
8017                   activeStream.lineStart();
8018                   activeStream.point(x, y);
8019                 }
8020               } else {
8021                 if (v && v_) activeStream.point(x, y);
8022                 else {
8023                   var a = [x_ = Math.max(clipMin, Math.min(clipMax, x_)), y_ = Math.max(clipMin, Math.min(clipMax, y_))],
8024                       b = [x = Math.max(clipMin, Math.min(clipMax, x)), y = Math.max(clipMin, Math.min(clipMax, y))];
8025                   if (clipLine(a, b, x0, y0, x1, y1)) {
8026                     if (!v_) {
8027                       activeStream.lineStart();
8028                       activeStream.point(a[0], a[1]);
8029                     }
8030                     activeStream.point(b[0], b[1]);
8031                     if (!v) activeStream.lineEnd();
8032                     clean = false;
8033                   } else if (v) {
8034                     activeStream.lineStart();
8035                     activeStream.point(x, y);
8036                     clean = false;
8037                   }
8038                 }
8039               }
8040               x_ = x, y_ = y, v_ = v;
8041             }
8042
8043             return clipStream;
8044           };
8045         }
8046
8047         var lengthSum = adder(),
8048             lambda0$2,
8049             sinPhi0$1,
8050             cosPhi0$1;
8051
8052         var lengthStream = {
8053           sphere: noop$2,
8054           point: noop$2,
8055           lineStart: lengthLineStart,
8056           lineEnd: noop$2,
8057           polygonStart: noop$2,
8058           polygonEnd: noop$2
8059         };
8060
8061         function lengthLineStart() {
8062           lengthStream.point = lengthPointFirst;
8063           lengthStream.lineEnd = lengthLineEnd;
8064         }
8065
8066         function lengthLineEnd() {
8067           lengthStream.point = lengthStream.lineEnd = noop$2;
8068         }
8069
8070         function lengthPointFirst(lambda, phi) {
8071           lambda *= radians, phi *= radians;
8072           lambda0$2 = lambda, sinPhi0$1 = sin(phi), cosPhi0$1 = cos(phi);
8073           lengthStream.point = lengthPoint;
8074         }
8075
8076         function lengthPoint(lambda, phi) {
8077           lambda *= radians, phi *= radians;
8078           var sinPhi = sin(phi),
8079               cosPhi = cos(phi),
8080               delta = abs$2(lambda - lambda0$2),
8081               cosDelta = cos(delta),
8082               sinDelta = sin(delta),
8083               x = cosPhi * sinDelta,
8084               y = cosPhi0$1 * sinPhi - sinPhi0$1 * cosPhi * cosDelta,
8085               z = sinPhi0$1 * sinPhi + cosPhi0$1 * cosPhi * cosDelta;
8086           lengthSum.add(atan2(sqrt(x * x + y * y), z));
8087           lambda0$2 = lambda, sinPhi0$1 = sinPhi, cosPhi0$1 = cosPhi;
8088         }
8089
8090         function d3_geoLength(object) {
8091           lengthSum.reset();
8092           d3_geoStream(object, lengthStream);
8093           return +lengthSum;
8094         }
8095
8096         function identity(x) {
8097           return x;
8098         }
8099
8100         var areaSum$1 = adder(),
8101             areaRingSum$1 = adder(),
8102             x00,
8103             y00,
8104             x0$1,
8105             y0$1;
8106
8107         var areaStream$1 = {
8108           point: noop$2,
8109           lineStart: noop$2,
8110           lineEnd: noop$2,
8111           polygonStart: function() {
8112             areaStream$1.lineStart = areaRingStart$1;
8113             areaStream$1.lineEnd = areaRingEnd$1;
8114           },
8115           polygonEnd: function() {
8116             areaStream$1.lineStart = areaStream$1.lineEnd = areaStream$1.point = noop$2;
8117             areaSum$1.add(abs$2(areaRingSum$1));
8118             areaRingSum$1.reset();
8119           },
8120           result: function() {
8121             var area = areaSum$1 / 2;
8122             areaSum$1.reset();
8123             return area;
8124           }
8125         };
8126
8127         function areaRingStart$1() {
8128           areaStream$1.point = areaPointFirst$1;
8129         }
8130
8131         function areaPointFirst$1(x, y) {
8132           areaStream$1.point = areaPoint$1;
8133           x00 = x0$1 = x, y00 = y0$1 = y;
8134         }
8135
8136         function areaPoint$1(x, y) {
8137           areaRingSum$1.add(y0$1 * x - x0$1 * y);
8138           x0$1 = x, y0$1 = y;
8139         }
8140
8141         function areaRingEnd$1() {
8142           areaPoint$1(x00, y00);
8143         }
8144
8145         var x0$2 = Infinity,
8146             y0$2 = x0$2,
8147             x1 = -x0$2,
8148             y1 = x1;
8149
8150         var boundsStream$1 = {
8151           point: boundsPoint$1,
8152           lineStart: noop$2,
8153           lineEnd: noop$2,
8154           polygonStart: noop$2,
8155           polygonEnd: noop$2,
8156           result: function() {
8157             var bounds = [[x0$2, y0$2], [x1, y1]];
8158             x1 = y1 = -(y0$2 = x0$2 = Infinity);
8159             return bounds;
8160           }
8161         };
8162
8163         function boundsPoint$1(x, y) {
8164           if (x < x0$2) x0$2 = x;
8165           if (x > x1) x1 = x;
8166           if (y < y0$2) y0$2 = y;
8167           if (y > y1) y1 = y;
8168         }
8169
8170         // TODO Enforce positive area for exterior, negative area for interior?
8171
8172         var X0$1 = 0,
8173             Y0$1 = 0,
8174             Z0$1 = 0,
8175             X1$1 = 0,
8176             Y1$1 = 0,
8177             Z1$1 = 0,
8178             X2$1 = 0,
8179             Y2$1 = 0,
8180             Z2$1 = 0,
8181             x00$1,
8182             y00$1,
8183             x0$3,
8184             y0$3;
8185
8186         var centroidStream$1 = {
8187           point: centroidPoint$1,
8188           lineStart: centroidLineStart$1,
8189           lineEnd: centroidLineEnd$1,
8190           polygonStart: function() {
8191             centroidStream$1.lineStart = centroidRingStart$1;
8192             centroidStream$1.lineEnd = centroidRingEnd$1;
8193           },
8194           polygonEnd: function() {
8195             centroidStream$1.point = centroidPoint$1;
8196             centroidStream$1.lineStart = centroidLineStart$1;
8197             centroidStream$1.lineEnd = centroidLineEnd$1;
8198           },
8199           result: function() {
8200             var centroid = Z2$1 ? [X2$1 / Z2$1, Y2$1 / Z2$1]
8201                 : Z1$1 ? [X1$1 / Z1$1, Y1$1 / Z1$1]
8202                 : Z0$1 ? [X0$1 / Z0$1, Y0$1 / Z0$1]
8203                 : [NaN, NaN];
8204             X0$1 = Y0$1 = Z0$1 =
8205             X1$1 = Y1$1 = Z1$1 =
8206             X2$1 = Y2$1 = Z2$1 = 0;
8207             return centroid;
8208           }
8209         };
8210
8211         function centroidPoint$1(x, y) {
8212           X0$1 += x;
8213           Y0$1 += y;
8214           ++Z0$1;
8215         }
8216
8217         function centroidLineStart$1() {
8218           centroidStream$1.point = centroidPointFirstLine;
8219         }
8220
8221         function centroidPointFirstLine(x, y) {
8222           centroidStream$1.point = centroidPointLine;
8223           centroidPoint$1(x0$3 = x, y0$3 = y);
8224         }
8225
8226         function centroidPointLine(x, y) {
8227           var dx = x - x0$3, dy = y - y0$3, z = sqrt(dx * dx + dy * dy);
8228           X1$1 += z * (x0$3 + x) / 2;
8229           Y1$1 += z * (y0$3 + y) / 2;
8230           Z1$1 += z;
8231           centroidPoint$1(x0$3 = x, y0$3 = y);
8232         }
8233
8234         function centroidLineEnd$1() {
8235           centroidStream$1.point = centroidPoint$1;
8236         }
8237
8238         function centroidRingStart$1() {
8239           centroidStream$1.point = centroidPointFirstRing;
8240         }
8241
8242         function centroidRingEnd$1() {
8243           centroidPointRing(x00$1, y00$1);
8244         }
8245
8246         function centroidPointFirstRing(x, y) {
8247           centroidStream$1.point = centroidPointRing;
8248           centroidPoint$1(x00$1 = x0$3 = x, y00$1 = y0$3 = y);
8249         }
8250
8251         function centroidPointRing(x, y) {
8252           var dx = x - x0$3,
8253               dy = y - y0$3,
8254               z = sqrt(dx * dx + dy * dy);
8255
8256           X1$1 += z * (x0$3 + x) / 2;
8257           Y1$1 += z * (y0$3 + y) / 2;
8258           Z1$1 += z;
8259
8260           z = y0$3 * x - x0$3 * y;
8261           X2$1 += z * (x0$3 + x);
8262           Y2$1 += z * (y0$3 + y);
8263           Z2$1 += z * 3;
8264           centroidPoint$1(x0$3 = x, y0$3 = y);
8265         }
8266
8267         function PathContext(context) {
8268           this._context = context;
8269         }
8270
8271         PathContext.prototype = {
8272           _radius: 4.5,
8273           pointRadius: function(_) {
8274             return this._radius = _, this;
8275           },
8276           polygonStart: function() {
8277             this._line = 0;
8278           },
8279           polygonEnd: function() {
8280             this._line = NaN;
8281           },
8282           lineStart: function() {
8283             this._point = 0;
8284           },
8285           lineEnd: function() {
8286             if (this._line === 0) this._context.closePath();
8287             this._point = NaN;
8288           },
8289           point: function(x, y) {
8290             switch (this._point) {
8291               case 0: {
8292                 this._context.moveTo(x, y);
8293                 this._point = 1;
8294                 break;
8295               }
8296               case 1: {
8297                 this._context.lineTo(x, y);
8298                 break;
8299               }
8300               default: {
8301                 this._context.moveTo(x + this._radius, y);
8302                 this._context.arc(x, y, this._radius, 0, tau);
8303                 break;
8304               }
8305             }
8306           },
8307           result: noop$2
8308         };
8309
8310         var lengthSum$1 = adder(),
8311             lengthRing,
8312             x00$2,
8313             y00$2,
8314             x0$4,
8315             y0$4;
8316
8317         var lengthStream$1 = {
8318           point: noop$2,
8319           lineStart: function() {
8320             lengthStream$1.point = lengthPointFirst$1;
8321           },
8322           lineEnd: function() {
8323             if (lengthRing) lengthPoint$1(x00$2, y00$2);
8324             lengthStream$1.point = noop$2;
8325           },
8326           polygonStart: function() {
8327             lengthRing = true;
8328           },
8329           polygonEnd: function() {
8330             lengthRing = null;
8331           },
8332           result: function() {
8333             var length = +lengthSum$1;
8334             lengthSum$1.reset();
8335             return length;
8336           }
8337         };
8338
8339         function lengthPointFirst$1(x, y) {
8340           lengthStream$1.point = lengthPoint$1;
8341           x00$2 = x0$4 = x, y00$2 = y0$4 = y;
8342         }
8343
8344         function lengthPoint$1(x, y) {
8345           x0$4 -= x, y0$4 -= y;
8346           lengthSum$1.add(sqrt(x0$4 * x0$4 + y0$4 * y0$4));
8347           x0$4 = x, y0$4 = y;
8348         }
8349
8350         function PathString() {
8351           this._string = [];
8352         }
8353
8354         PathString.prototype = {
8355           _radius: 4.5,
8356           _circle: circle(4.5),
8357           pointRadius: function(_) {
8358             if ((_ = +_) !== this._radius) this._radius = _, this._circle = null;
8359             return this;
8360           },
8361           polygonStart: function() {
8362             this._line = 0;
8363           },
8364           polygonEnd: function() {
8365             this._line = NaN;
8366           },
8367           lineStart: function() {
8368             this._point = 0;
8369           },
8370           lineEnd: function() {
8371             if (this._line === 0) this._string.push("Z");
8372             this._point = NaN;
8373           },
8374           point: function(x, y) {
8375             switch (this._point) {
8376               case 0: {
8377                 this._string.push("M", x, ",", y);
8378                 this._point = 1;
8379                 break;
8380               }
8381               case 1: {
8382                 this._string.push("L", x, ",", y);
8383                 break;
8384               }
8385               default: {
8386                 if (this._circle == null) this._circle = circle(this._radius);
8387                 this._string.push("M", x, ",", y, this._circle);
8388                 break;
8389               }
8390             }
8391           },
8392           result: function() {
8393             if (this._string.length) {
8394               var result = this._string.join("");
8395               this._string = [];
8396               return result;
8397             } else {
8398               return null;
8399             }
8400           }
8401         };
8402
8403         function circle(radius) {
8404           return "m0," + radius
8405               + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius
8406               + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius
8407               + "z";
8408         }
8409
8410         function d3_geoPath(projection, context) {
8411           var pointRadius = 4.5,
8412               projectionStream,
8413               contextStream;
8414
8415           function path(object) {
8416             if (object) {
8417               if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments));
8418               d3_geoStream(object, projectionStream(contextStream));
8419             }
8420             return contextStream.result();
8421           }
8422
8423           path.area = function(object) {
8424             d3_geoStream(object, projectionStream(areaStream$1));
8425             return areaStream$1.result();
8426           };
8427
8428           path.measure = function(object) {
8429             d3_geoStream(object, projectionStream(lengthStream$1));
8430             return lengthStream$1.result();
8431           };
8432
8433           path.bounds = function(object) {
8434             d3_geoStream(object, projectionStream(boundsStream$1));
8435             return boundsStream$1.result();
8436           };
8437
8438           path.centroid = function(object) {
8439             d3_geoStream(object, projectionStream(centroidStream$1));
8440             return centroidStream$1.result();
8441           };
8442
8443           path.projection = function(_) {
8444             return arguments.length ? (projectionStream = _ == null ? (projection = null, identity) : (projection = _).stream, path) : projection;
8445           };
8446
8447           path.context = function(_) {
8448             if (!arguments.length) return context;
8449             contextStream = _ == null ? (context = null, new PathString) : new PathContext(context = _);
8450             if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius);
8451             return path;
8452           };
8453
8454           path.pointRadius = function(_) {
8455             if (!arguments.length) return pointRadius;
8456             pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_);
8457             return path;
8458           };
8459
8460           return path.projection(projection).context(context);
8461         }
8462
8463         function d3_geoTransform(methods) {
8464           return {
8465             stream: transformer(methods)
8466           };
8467         }
8468
8469         function transformer(methods) {
8470           return function(stream) {
8471             var s = new TransformStream;
8472             for (var key in methods) s[key] = methods[key];
8473             s.stream = stream;
8474             return s;
8475           };
8476         }
8477
8478         function TransformStream() {}
8479
8480         TransformStream.prototype = {
8481           constructor: TransformStream,
8482           point: function(x, y) { this.stream.point(x, y); },
8483           sphere: function() { this.stream.sphere(); },
8484           lineStart: function() { this.stream.lineStart(); },
8485           lineEnd: function() { this.stream.lineEnd(); },
8486           polygonStart: function() { this.stream.polygonStart(); },
8487           polygonEnd: function() { this.stream.polygonEnd(); }
8488         };
8489
8490         function fit(projection, fitBounds, object) {
8491           var clip = projection.clipExtent && projection.clipExtent();
8492           projection.scale(150).translate([0, 0]);
8493           if (clip != null) projection.clipExtent(null);
8494           d3_geoStream(object, projection.stream(boundsStream$1));
8495           fitBounds(boundsStream$1.result());
8496           if (clip != null) projection.clipExtent(clip);
8497           return projection;
8498         }
8499
8500         function fitExtent(projection, extent, object) {
8501           return fit(projection, function(b) {
8502             var w = extent[1][0] - extent[0][0],
8503                 h = extent[1][1] - extent[0][1],
8504                 k = Math.min(w / (b[1][0] - b[0][0]), h / (b[1][1] - b[0][1])),
8505                 x = +extent[0][0] + (w - k * (b[1][0] + b[0][0])) / 2,
8506                 y = +extent[0][1] + (h - k * (b[1][1] + b[0][1])) / 2;
8507             projection.scale(150 * k).translate([x, y]);
8508           }, object);
8509         }
8510
8511         function fitSize(projection, size, object) {
8512           return fitExtent(projection, [[0, 0], size], object);
8513         }
8514
8515         function fitWidth(projection, width, object) {
8516           return fit(projection, function(b) {
8517             var w = +width,
8518                 k = w / (b[1][0] - b[0][0]),
8519                 x = (w - k * (b[1][0] + b[0][0])) / 2,
8520                 y = -k * b[0][1];
8521             projection.scale(150 * k).translate([x, y]);
8522           }, object);
8523         }
8524
8525         function fitHeight(projection, height, object) {
8526           return fit(projection, function(b) {
8527             var h = +height,
8528                 k = h / (b[1][1] - b[0][1]),
8529                 x = -k * b[0][0],
8530                 y = (h - k * (b[1][1] + b[0][1])) / 2;
8531             projection.scale(150 * k).translate([x, y]);
8532           }, object);
8533         }
8534
8535         var maxDepth = 16, // maximum depth of subdivision
8536             cosMinDistance = cos(30 * radians); // cos(minimum angular distance)
8537
8538         function resample(project, delta2) {
8539           return +delta2 ? resample$1(project, delta2) : resampleNone(project);
8540         }
8541
8542         function resampleNone(project) {
8543           return transformer({
8544             point: function(x, y) {
8545               x = project(x, y);
8546               this.stream.point(x[0], x[1]);
8547             }
8548           });
8549         }
8550
8551         function resample$1(project, delta2) {
8552
8553           function resampleLineTo(x0, y0, lambda0, a0, b0, c0, x1, y1, lambda1, a1, b1, c1, depth, stream) {
8554             var dx = x1 - x0,
8555                 dy = y1 - y0,
8556                 d2 = dx * dx + dy * dy;
8557             if (d2 > 4 * delta2 && depth--) {
8558               var a = a0 + a1,
8559                   b = b0 + b1,
8560                   c = c0 + c1,
8561                   m = sqrt(a * a + b * b + c * c),
8562                   phi2 = asin(c /= m),
8563                   lambda2 = abs$2(abs$2(c) - 1) < epsilon || abs$2(lambda0 - lambda1) < epsilon ? (lambda0 + lambda1) / 2 : atan2(b, a),
8564                   p = project(lambda2, phi2),
8565                   x2 = p[0],
8566                   y2 = p[1],
8567                   dx2 = x2 - x0,
8568                   dy2 = y2 - y0,
8569                   dz = dy * dx2 - dx * dy2;
8570               if (dz * dz / d2 > delta2 // perpendicular projected distance
8571                   || abs$2((dx * dx2 + dy * dy2) / d2 - 0.5) > 0.3 // midpoint close to an end
8572                   || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) { // angular distance
8573                 resampleLineTo(x0, y0, lambda0, a0, b0, c0, x2, y2, lambda2, a /= m, b /= m, c, depth, stream);
8574                 stream.point(x2, y2);
8575                 resampleLineTo(x2, y2, lambda2, a, b, c, x1, y1, lambda1, a1, b1, c1, depth, stream);
8576               }
8577             }
8578           }
8579           return function(stream) {
8580             var lambda00, x00, y00, a00, b00, c00, // first point
8581                 lambda0, x0, y0, a0, b0, c0; // previous point
8582
8583             var resampleStream = {
8584               point: point,
8585               lineStart: lineStart,
8586               lineEnd: lineEnd,
8587               polygonStart: function() { stream.polygonStart(); resampleStream.lineStart = ringStart; },
8588               polygonEnd: function() { stream.polygonEnd(); resampleStream.lineStart = lineStart; }
8589             };
8590
8591             function point(x, y) {
8592               x = project(x, y);
8593               stream.point(x[0], x[1]);
8594             }
8595
8596             function lineStart() {
8597               x0 = NaN;
8598               resampleStream.point = linePoint;
8599               stream.lineStart();
8600             }
8601
8602             function linePoint(lambda, phi) {
8603               var c = cartesian([lambda, phi]), p = project(lambda, phi);
8604               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);
8605               stream.point(x0, y0);
8606             }
8607
8608             function lineEnd() {
8609               resampleStream.point = point;
8610               stream.lineEnd();
8611             }
8612
8613             function ringStart() {
8614               lineStart();
8615               resampleStream.point = ringPoint;
8616               resampleStream.lineEnd = ringEnd;
8617             }
8618
8619             function ringPoint(lambda, phi) {
8620               linePoint(lambda00 = lambda, phi), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0;
8621               resampleStream.point = linePoint;
8622             }
8623
8624             function ringEnd() {
8625               resampleLineTo(x0, y0, lambda0, a0, b0, c0, x00, y00, lambda00, a00, b00, c00, maxDepth, stream);
8626               resampleStream.lineEnd = lineEnd;
8627               lineEnd();
8628             }
8629
8630             return resampleStream;
8631           };
8632         }
8633
8634         var transformRadians = transformer({
8635           point: function(x, y) {
8636             this.stream.point(x * radians, y * radians);
8637           }
8638         });
8639
8640         function transformRotate(rotate) {
8641           return transformer({
8642             point: function(x, y) {
8643               var r = rotate(x, y);
8644               return this.stream.point(r[0], r[1]);
8645             }
8646           });
8647         }
8648
8649         function scaleTranslate(k, dx, dy, sx, sy) {
8650           function transform(x, y) {
8651             x *= sx; y *= sy;
8652             return [dx + k * x, dy - k * y];
8653           }
8654           transform.invert = function(x, y) {
8655             return [(x - dx) / k * sx, (dy - y) / k * sy];
8656           };
8657           return transform;
8658         }
8659
8660         function scaleTranslateRotate(k, dx, dy, sx, sy, alpha) {
8661           var cosAlpha = cos(alpha),
8662               sinAlpha = sin(alpha),
8663               a = cosAlpha * k,
8664               b = sinAlpha * k,
8665               ai = cosAlpha / k,
8666               bi = sinAlpha / k,
8667               ci = (sinAlpha * dy - cosAlpha * dx) / k,
8668               fi = (sinAlpha * dx + cosAlpha * dy) / k;
8669           function transform(x, y) {
8670             x *= sx; y *= sy;
8671             return [a * x - b * y + dx, dy - b * x - a * y];
8672           }
8673           transform.invert = function(x, y) {
8674             return [sx * (ai * x - bi * y + ci), sy * (fi - bi * x - ai * y)];
8675           };
8676           return transform;
8677         }
8678
8679         function projection(project) {
8680           return projectionMutator(function() { return project; })();
8681         }
8682
8683         function projectionMutator(projectAt) {
8684           var project,
8685               k = 150, // scale
8686               x = 480, y = 250, // translate
8687               lambda = 0, phi = 0, // center
8688               deltaLambda = 0, deltaPhi = 0, deltaGamma = 0, rotate, // pre-rotate
8689               alpha = 0, // post-rotate angle
8690               sx = 1, // reflectX
8691               sy = 1, // reflectX
8692               theta = null, preclip = clipAntimeridian, // pre-clip angle
8693               x0 = null, y0, x1, y1, postclip = identity, // post-clip extent
8694               delta2 = 0.5, // precision
8695               projectResample,
8696               projectTransform,
8697               projectRotateTransform,
8698               cache,
8699               cacheStream;
8700
8701           function projection(point) {
8702             return projectRotateTransform(point[0] * radians, point[1] * radians);
8703           }
8704
8705           function invert(point) {
8706             point = projectRotateTransform.invert(point[0], point[1]);
8707             return point && [point[0] * degrees, point[1] * degrees];
8708           }
8709
8710           projection.stream = function(stream) {
8711             return cache && cacheStream === stream ? cache : cache = transformRadians(transformRotate(rotate)(preclip(projectResample(postclip(cacheStream = stream)))));
8712           };
8713
8714           projection.preclip = function(_) {
8715             return arguments.length ? (preclip = _, theta = undefined, reset()) : preclip;
8716           };
8717
8718           projection.postclip = function(_) {
8719             return arguments.length ? (postclip = _, x0 = y0 = x1 = y1 = null, reset()) : postclip;
8720           };
8721
8722           projection.clipAngle = function(_) {
8723             return arguments.length ? (preclip = +_ ? clipCircle(theta = _ * radians) : (theta = null, clipAntimeridian), reset()) : theta * degrees;
8724           };
8725
8726           projection.clipExtent = function(_) {
8727             return arguments.length ? (postclip = _ == null ? (x0 = y0 = x1 = y1 = null, identity) : clipRectangle(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reset()) : x0 == null ? null : [[x0, y0], [x1, y1]];
8728           };
8729
8730           projection.scale = function(_) {
8731             return arguments.length ? (k = +_, recenter()) : k;
8732           };
8733
8734           projection.translate = function(_) {
8735             return arguments.length ? (x = +_[0], y = +_[1], recenter()) : [x, y];
8736           };
8737
8738           projection.center = function(_) {
8739             return arguments.length ? (lambda = _[0] % 360 * radians, phi = _[1] % 360 * radians, recenter()) : [lambda * degrees, phi * degrees];
8740           };
8741
8742           projection.rotate = function(_) {
8743             return arguments.length ? (deltaLambda = _[0] % 360 * radians, deltaPhi = _[1] % 360 * radians, deltaGamma = _.length > 2 ? _[2] % 360 * radians : 0, recenter()) : [deltaLambda * degrees, deltaPhi * degrees, deltaGamma * degrees];
8744           };
8745
8746           projection.angle = function(_) {
8747             return arguments.length ? (alpha = _ % 360 * radians, recenter()) : alpha * degrees;
8748           };
8749
8750           projection.reflectX = function(_) {
8751             return arguments.length ? (sx = _ ? -1 : 1, recenter()) : sx < 0;
8752           };
8753
8754           projection.reflectY = function(_) {
8755             return arguments.length ? (sy = _ ? -1 : 1, recenter()) : sy < 0;
8756           };
8757
8758           projection.precision = function(_) {
8759             return arguments.length ? (projectResample = resample(projectTransform, delta2 = _ * _), reset()) : sqrt(delta2);
8760           };
8761
8762           projection.fitExtent = function(extent, object) {
8763             return fitExtent(projection, extent, object);
8764           };
8765
8766           projection.fitSize = function(size, object) {
8767             return fitSize(projection, size, object);
8768           };
8769
8770           projection.fitWidth = function(width, object) {
8771             return fitWidth(projection, width, object);
8772           };
8773
8774           projection.fitHeight = function(height, object) {
8775             return fitHeight(projection, height, object);
8776           };
8777
8778           function recenter() {
8779             var center = scaleTranslateRotate(k, 0, 0, sx, sy, alpha).apply(null, project(lambda, phi)),
8780                 transform = (alpha ? scaleTranslateRotate : scaleTranslate)(k, x - center[0], y - center[1], sx, sy, alpha);
8781             rotate = rotateRadians(deltaLambda, deltaPhi, deltaGamma);
8782             projectTransform = compose(project, transform);
8783             projectRotateTransform = compose(rotate, projectTransform);
8784             projectResample = resample(projectTransform, delta2);
8785             return reset();
8786           }
8787
8788           function reset() {
8789             cache = cacheStream = null;
8790             return projection;
8791           }
8792
8793           return function() {
8794             project = projectAt.apply(this, arguments);
8795             projection.invert = project.invert && invert;
8796             return recenter();
8797           };
8798         }
8799
8800         function mercatorRaw(lambda, phi) {
8801           return [lambda, log(tan((halfPi + phi) / 2))];
8802         }
8803
8804         mercatorRaw.invert = function(x, y) {
8805           return [x, 2 * atan(exp(y)) - halfPi];
8806         };
8807
8808         function mercator() {
8809           return mercatorProjection(mercatorRaw)
8810               .scale(961 / tau);
8811         }
8812
8813         function mercatorProjection(project) {
8814           var m = projection(project),
8815               center = m.center,
8816               scale = m.scale,
8817               translate = m.translate,
8818               clipExtent = m.clipExtent,
8819               x0 = null, y0, x1, y1; // clip extent
8820
8821           m.scale = function(_) {
8822             return arguments.length ? (scale(_), reclip()) : scale();
8823           };
8824
8825           m.translate = function(_) {
8826             return arguments.length ? (translate(_), reclip()) : translate();
8827           };
8828
8829           m.center = function(_) {
8830             return arguments.length ? (center(_), reclip()) : center();
8831           };
8832
8833           m.clipExtent = function(_) {
8834             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]];
8835           };
8836
8837           function reclip() {
8838             var k = pi * scale(),
8839                 t = m(rotation(m.rotate()).invert([0, 0]));
8840             return clipExtent(x0 == null
8841                 ? [[t[0] - k, t[1] - k], [t[0] + k, t[1] + k]] : project === mercatorRaw
8842                 ? [[Math.max(t[0] - k, x0), y0], [Math.min(t[0] + k, x1), y1]]
8843                 : [[x0, Math.max(t[1] - k, y0)], [x1, Math.min(t[1] + k, y1)]]);
8844           }
8845
8846           return reclip();
8847         }
8848
8849         function d3_geoIdentity() {
8850           var k = 1, tx = 0, ty = 0, sx = 1, sy = 1, // scale, translate and reflect
8851               alpha = 0, ca, sa, // angle
8852               x0 = null, y0, x1, y1, // clip extent
8853               kx = 1, ky = 1,
8854               transform = transformer({
8855                 point: function(x, y) {
8856                   var p = projection([x, y]);
8857                   this.stream.point(p[0], p[1]);
8858                 }
8859               }),
8860               postclip = identity,
8861               cache,
8862               cacheStream;
8863
8864           function reset() {
8865             kx = k * sx;
8866             ky = k * sy;
8867             cache = cacheStream = null;
8868             return projection;
8869           }
8870
8871           function projection (p) {
8872             var x = p[0] * kx, y = p[1] * ky;
8873             if (alpha) {
8874               var t = y * ca - x * sa;
8875               x = x * ca + y * sa;
8876               y = t;
8877             }    
8878             return [x + tx, y + ty];
8879           }
8880           projection.invert = function(p) {
8881             var x = p[0] - tx, y = p[1] - ty;
8882             if (alpha) {
8883               var t = y * ca + x * sa;
8884               x = x * ca - y * sa;
8885               y = t;
8886             }
8887             return [x / kx, y / ky];
8888           };
8889           projection.stream = function(stream) {
8890             return cache && cacheStream === stream ? cache : cache = transform(postclip(cacheStream = stream));
8891           };
8892           projection.postclip = function(_) {
8893             return arguments.length ? (postclip = _, x0 = y0 = x1 = y1 = null, reset()) : postclip;
8894           };
8895           projection.clipExtent = function(_) {
8896             return arguments.length ? (postclip = _ == null ? (x0 = y0 = x1 = y1 = null, identity) : clipRectangle(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reset()) : x0 == null ? null : [[x0, y0], [x1, y1]];
8897           };
8898           projection.scale = function(_) {
8899             return arguments.length ? (k = +_, reset()) : k;
8900           };
8901           projection.translate = function(_) {
8902             return arguments.length ? (tx = +_[0], ty = +_[1], reset()) : [tx, ty];
8903           };
8904           projection.angle = function(_) {
8905             return arguments.length ? (alpha = _ % 360 * radians, sa = sin(alpha), ca = cos(alpha), reset()) : alpha * degrees;
8906           };
8907           projection.reflectX = function(_) {
8908             return arguments.length ? (sx = _ ? -1 : 1, reset()) : sx < 0;
8909           };
8910           projection.reflectY = function(_) {
8911             return arguments.length ? (sy = _ ? -1 : 1, reset()) : sy < 0;
8912           };
8913           projection.fitExtent = function(extent, object) {
8914             return fitExtent(projection, extent, object);
8915           };
8916           projection.fitSize = function(size, object) {
8917             return fitSize(projection, size, object);
8918           };
8919           projection.fitWidth = function(width, object) {
8920             return fitWidth(projection, width, object);
8921           };
8922           projection.fitHeight = function(height, object) {
8923             return fitHeight(projection, height, object);
8924           };
8925
8926           return projection;
8927         }
8928
8929         // constants
8930         var TAU = 2 * Math.PI;
8931         var EQUATORIAL_RADIUS = 6356752.314245179;
8932         var POLAR_RADIUS = 6378137.0;
8933
8934
8935         function geoLatToMeters(dLat) {
8936             return dLat * (TAU * POLAR_RADIUS / 360);
8937         }
8938
8939
8940         function geoLonToMeters(dLon, atLat) {
8941             return Math.abs(atLat) >= 90 ? 0 :
8942                 dLon * (TAU * EQUATORIAL_RADIUS / 360) * Math.abs(Math.cos(atLat * (Math.PI / 180)));
8943         }
8944
8945
8946         function geoMetersToLat(m) {
8947             return m / (TAU * POLAR_RADIUS / 360);
8948         }
8949
8950
8951         function geoMetersToLon(m, atLat) {
8952             return Math.abs(atLat) >= 90 ? 0 :
8953                 m / (TAU * EQUATORIAL_RADIUS / 360) / Math.abs(Math.cos(atLat * (Math.PI / 180)));
8954         }
8955
8956
8957         function geoMetersToOffset(meters, tileSize) {
8958             tileSize = tileSize || 256;
8959             return [
8960                 meters[0] * tileSize / (TAU * EQUATORIAL_RADIUS),
8961                 -meters[1] * tileSize / (TAU * POLAR_RADIUS)
8962             ];
8963         }
8964
8965
8966         function geoOffsetToMeters(offset, tileSize) {
8967             tileSize = tileSize || 256;
8968             return [
8969                 offset[0] * TAU * EQUATORIAL_RADIUS / tileSize,
8970                 -offset[1] * TAU * POLAR_RADIUS / tileSize
8971             ];
8972         }
8973
8974
8975         // Equirectangular approximation of spherical distances on Earth
8976         function geoSphericalDistance(a, b) {
8977             var x = geoLonToMeters(a[0] - b[0], (a[1] + b[1]) / 2);
8978             var y = geoLatToMeters(a[1] - b[1]);
8979             return Math.sqrt((x * x) + (y * y));
8980         }
8981
8982
8983         // scale to zoom
8984         function geoScaleToZoom(k, tileSize) {
8985             tileSize = tileSize || 256;
8986             var log2ts = Math.log(tileSize) * Math.LOG2E;
8987             return Math.log(k * TAU) / Math.LN2 - log2ts;
8988         }
8989
8990
8991         // zoom to scale
8992         function geoZoomToScale(z, tileSize) {
8993             tileSize = tileSize || 256;
8994             return tileSize * Math.pow(2, z) / TAU;
8995         }
8996
8997
8998         // returns info about the node from `nodes` closest to the given `point`
8999         function geoSphericalClosestNode(nodes, point) {
9000             var minDistance = Infinity, distance;
9001             var indexOfMin;
9002
9003             for (var i in nodes) {
9004                 distance = geoSphericalDistance(nodes[i].loc, point);
9005                 if (distance < minDistance) {
9006                     minDistance = distance;
9007                     indexOfMin = i;
9008                 }
9009             }
9010
9011             if (indexOfMin !== undefined) {
9012                 return { index: indexOfMin, distance: minDistance, node: nodes[indexOfMin] };
9013             } else {
9014                 return null;
9015             }
9016         }
9017
9018         function geoExtent(min, max) {
9019             if (!(this instanceof geoExtent)) {
9020                 return new geoExtent(min, max);
9021             } else if (min instanceof geoExtent) {
9022                 return min;
9023             } else if (min && min.length === 2 && min[0].length === 2 && min[1].length === 2) {
9024                 this[0] = min[0];
9025                 this[1] = min[1];
9026             } else {
9027                 this[0] = min        || [ Infinity,  Infinity];
9028                 this[1] = max || min || [-Infinity, -Infinity];
9029             }
9030         }
9031
9032         geoExtent.prototype = new Array(2);
9033
9034         Object.assign(geoExtent.prototype, {
9035
9036             equals: function (obj) {
9037                 return this[0][0] === obj[0][0] &&
9038                     this[0][1] === obj[0][1] &&
9039                     this[1][0] === obj[1][0] &&
9040                     this[1][1] === obj[1][1];
9041             },
9042
9043
9044             extend: function(obj) {
9045                 if (!(obj instanceof geoExtent)) obj = new geoExtent(obj);
9046                 return geoExtent(
9047                     [Math.min(obj[0][0], this[0][0]), Math.min(obj[0][1], this[0][1])],
9048                     [Math.max(obj[1][0], this[1][0]), Math.max(obj[1][1], this[1][1])]
9049                 );
9050             },
9051
9052
9053             _extend: function(extent) {
9054                 this[0][0] = Math.min(extent[0][0], this[0][0]);
9055                 this[0][1] = Math.min(extent[0][1], this[0][1]);
9056                 this[1][0] = Math.max(extent[1][0], this[1][0]);
9057                 this[1][1] = Math.max(extent[1][1], this[1][1]);
9058             },
9059
9060
9061             area: function() {
9062                 return Math.abs((this[1][0] - this[0][0]) * (this[1][1] - this[0][1]));
9063             },
9064
9065
9066             center: function() {
9067                 return [(this[0][0] + this[1][0]) / 2, (this[0][1] + this[1][1]) / 2];
9068             },
9069
9070
9071             rectangle: function() {
9072                 return [this[0][0], this[0][1], this[1][0], this[1][1]];
9073             },
9074
9075
9076             bbox: function() {
9077                 return { minX: this[0][0], minY: this[0][1], maxX: this[1][0], maxY: this[1][1] };
9078             },
9079
9080
9081             polygon: function() {
9082                 return [
9083                     [this[0][0], this[0][1]],
9084                     [this[0][0], this[1][1]],
9085                     [this[1][0], this[1][1]],
9086                     [this[1][0], this[0][1]],
9087                     [this[0][0], this[0][1]]
9088                 ];
9089             },
9090
9091
9092             contains: function(obj) {
9093                 if (!(obj instanceof geoExtent)) obj = new geoExtent(obj);
9094                 return obj[0][0] >= this[0][0] &&
9095                        obj[0][1] >= this[0][1] &&
9096                        obj[1][0] <= this[1][0] &&
9097                        obj[1][1] <= this[1][1];
9098             },
9099
9100
9101             intersects: function(obj) {
9102                 if (!(obj instanceof geoExtent)) obj = new geoExtent(obj);
9103                 return obj[0][0] <= this[1][0] &&
9104                        obj[0][1] <= this[1][1] &&
9105                        obj[1][0] >= this[0][0] &&
9106                        obj[1][1] >= this[0][1];
9107             },
9108
9109
9110             intersection: function(obj) {
9111                 if (!this.intersects(obj)) return new geoExtent();
9112                 return new geoExtent(
9113                     [Math.max(obj[0][0], this[0][0]), Math.max(obj[0][1], this[0][1])],
9114                     [Math.min(obj[1][0], this[1][0]), Math.min(obj[1][1], this[1][1])]
9115                 );
9116             },
9117
9118
9119             percentContainedIn: function(obj) {
9120                 if (!(obj instanceof geoExtent)) obj = new geoExtent(obj);
9121                 var a1 = this.intersection(obj).area();
9122                 var a2 = this.area();
9123
9124                 if (a1 === Infinity || a2 === Infinity) {
9125                     return 0;
9126                 } else if (a1 === 0 || a2 === 0) {
9127                     if (obj.contains(this)) {
9128                         return 1;
9129                     }
9130                     return 0;
9131                 } else {
9132                     return a1 / a2;
9133                 }
9134             },
9135
9136
9137             padByMeters: function(meters) {
9138                 var dLat = geoMetersToLat(meters);
9139                 var dLon = geoMetersToLon(meters, this.center()[1]);
9140                 return geoExtent(
9141                     [this[0][0] - dLon, this[0][1] - dLat],
9142                     [this[1][0] + dLon, this[1][1] + dLat]
9143                 );
9144             },
9145
9146
9147             toParam: function() {
9148                 return this.rectangle().join(',');
9149             }
9150
9151         });
9152
9153         function d3_polygonArea(polygon) {
9154           var i = -1,
9155               n = polygon.length,
9156               a,
9157               b = polygon[n - 1],
9158               area = 0;
9159
9160           while (++i < n) {
9161             a = b;
9162             b = polygon[i];
9163             area += a[1] * b[0] - a[0] * b[1];
9164           }
9165
9166           return area / 2;
9167         }
9168
9169         function d3_polygonCentroid(polygon) {
9170           var i = -1,
9171               n = polygon.length,
9172               x = 0,
9173               y = 0,
9174               a,
9175               b = polygon[n - 1],
9176               c,
9177               k = 0;
9178
9179           while (++i < n) {
9180             a = b;
9181             b = polygon[i];
9182             k += c = a[0] * b[1] - b[0] * a[1];
9183             x += (a[0] + b[0]) * c;
9184             y += (a[1] + b[1]) * c;
9185           }
9186
9187           return k *= 3, [x / k, y / k];
9188         }
9189
9190         // Returns the 2D cross product of AB and AC vectors, i.e., the z-component of
9191         // the 3D cross product in a quadrant I Cartesian coordinate system (+x is
9192         // right, +y is up). Returns a positive value if ABC is counter-clockwise,
9193         // negative if clockwise, and zero if the points are collinear.
9194         function cross(a, b, c) {
9195           return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);
9196         }
9197
9198         function lexicographicOrder(a, b) {
9199           return a[0] - b[0] || a[1] - b[1];
9200         }
9201
9202         // Computes the upper convex hull per the monotone chain algorithm.
9203         // Assumes points.length >= 3, is sorted by x, unique in y.
9204         // Returns an array of indices into points in left-to-right order.
9205         function computeUpperHullIndexes(points) {
9206           var n = points.length,
9207               indexes = [0, 1],
9208               size = 2;
9209
9210           for (var i = 2; i < n; ++i) {
9211             while (size > 1 && cross(points[indexes[size - 2]], points[indexes[size - 1]], points[i]) <= 0) --size;
9212             indexes[size++] = i;
9213           }
9214
9215           return indexes.slice(0, size); // remove popped points
9216         }
9217
9218         function d3_polygonHull(points) {
9219           if ((n = points.length) < 3) return null;
9220
9221           var i,
9222               n,
9223               sortedPoints = new Array(n),
9224               flippedPoints = new Array(n);
9225
9226           for (i = 0; i < n; ++i) sortedPoints[i] = [+points[i][0], +points[i][1], i];
9227           sortedPoints.sort(lexicographicOrder);
9228           for (i = 0; i < n; ++i) flippedPoints[i] = [sortedPoints[i][0], -sortedPoints[i][1]];
9229
9230           var upperIndexes = computeUpperHullIndexes(sortedPoints),
9231               lowerIndexes = computeUpperHullIndexes(flippedPoints);
9232
9233           // Construct the hull polygon, removing possible duplicate endpoints.
9234           var skipLeft = lowerIndexes[0] === upperIndexes[0],
9235               skipRight = lowerIndexes[lowerIndexes.length - 1] === upperIndexes[upperIndexes.length - 1],
9236               hull = [];
9237
9238           // Add upper hull in right-to-l order.
9239           // Then add lower hull in left-to-right order.
9240           for (i = upperIndexes.length - 1; i >= 0; --i) hull.push(points[sortedPoints[upperIndexes[i]][2]]);
9241           for (i = +skipLeft; i < lowerIndexes.length - skipRight; ++i) hull.push(points[sortedPoints[lowerIndexes[i]][2]]);
9242
9243           return hull;
9244         }
9245
9246         // vector equals
9247         function geoVecEqual(a, b, epsilon) {
9248             if (epsilon) {
9249                 return (Math.abs(a[0] - b[0]) <= epsilon) && (Math.abs(a[1] - b[1]) <= epsilon);
9250             } else {
9251                 return (a[0] === b[0]) && (a[1] === b[1]);
9252             }
9253         }
9254
9255         // vector addition
9256         function geoVecAdd(a, b) {
9257             return [ a[0] + b[0], a[1] + b[1] ];
9258         }
9259
9260         // vector subtraction
9261         function geoVecSubtract(a, b) {
9262             return [ a[0] - b[0], a[1] - b[1] ];
9263         }
9264
9265         // vector scaling
9266         function geoVecScale(a, mag) {
9267             return [ a[0] * mag, a[1] * mag ];
9268         }
9269
9270         // vector rounding (was: geoRoundCoordinates)
9271         function geoVecFloor(a) {
9272             return [ Math.floor(a[0]), Math.floor(a[1]) ];
9273         }
9274
9275         // linear interpolation
9276         function geoVecInterp(a, b, t) {
9277             return [
9278                 a[0] + (b[0] - a[0]) * t,
9279                 a[1] + (b[1] - a[1]) * t
9280             ];
9281         }
9282
9283         // http://jsperf.com/id-dist-optimization
9284         function geoVecLength(a, b) {
9285             return Math.sqrt(geoVecLengthSquare(a,b));
9286         }
9287
9288         // length of vector raised to the power two
9289         function geoVecLengthSquare(a, b) {
9290             b = b || [0, 0];
9291             var x = a[0] - b[0];
9292             var y = a[1] - b[1];
9293             return (x * x) + (y * y);
9294         }
9295
9296         // get a unit vector
9297         function geoVecNormalize(a) {
9298             var length = Math.sqrt((a[0] * a[0]) + (a[1] * a[1]));
9299             if (length !== 0) {
9300                 return geoVecScale(a, 1 / length);
9301             }
9302             return [0, 0];
9303         }
9304
9305         // Return the counterclockwise angle in the range (-pi, pi)
9306         // between the positive X axis and the line intersecting a and b.
9307         function geoVecAngle(a, b) {
9308             return Math.atan2(b[1] - a[1], b[0] - a[0]);
9309         }
9310
9311         // dot product
9312         function geoVecDot(a, b, origin) {
9313             origin = origin || [0, 0];
9314             var p = geoVecSubtract(a, origin);
9315             var q = geoVecSubtract(b, origin);
9316             return (p[0]) * (q[0]) + (p[1]) * (q[1]);
9317         }
9318
9319         // normalized dot product
9320         function geoVecNormalizedDot(a, b, origin) {
9321             origin = origin || [0, 0];
9322             var p = geoVecNormalize(geoVecSubtract(a, origin));
9323             var q = geoVecNormalize(geoVecSubtract(b, origin));
9324             return geoVecDot(p, q);
9325         }
9326
9327         // 2D cross product of OA and OB vectors, returns magnitude of Z vector
9328         // Returns a positive value, if OAB makes a counter-clockwise turn,
9329         // negative for clockwise turn, and zero if the points are collinear.
9330         function geoVecCross(a, b, origin) {
9331             origin = origin || [0, 0];
9332             var p = geoVecSubtract(a, origin);
9333             var q = geoVecSubtract(b, origin);
9334             return (p[0]) * (q[1]) - (p[1]) * (q[0]);
9335         }
9336
9337
9338         // find closest orthogonal projection of point onto points array
9339         function geoVecProject(a, points) {
9340             var min = Infinity;
9341             var idx;
9342             var target;
9343
9344             for (var i = 0; i < points.length - 1; i++) {
9345                 var o = points[i];
9346                 var s = geoVecSubtract(points[i + 1], o);
9347                 var v = geoVecSubtract(a, o);
9348                 var proj = geoVecDot(v, s) / geoVecDot(s, s);
9349                 var p;
9350
9351                 if (proj < 0) {
9352                     p = o;
9353                 } else if (proj > 1) {
9354                     p = points[i + 1];
9355                 } else {
9356                     p = [o[0] + proj * s[0], o[1] + proj * s[1]];
9357                 }
9358
9359                 var dist = geoVecLength(p, a);
9360                 if (dist < min) {
9361                     min = dist;
9362                     idx = i + 1;
9363                     target = p;
9364                 }
9365             }
9366
9367             if (idx !== undefined) {
9368                 return { index: idx, distance: min, target: target };
9369             } else {
9370                 return null;
9371             }
9372         }
9373
9374         // Return the counterclockwise angle in the range (-pi, pi)
9375         // between the positive X axis and the line intersecting a and b.
9376         function geoAngle(a, b, projection) {
9377             return geoVecAngle(projection(a.loc), projection(b.loc));
9378         }
9379
9380
9381         function geoEdgeEqual(a, b) {
9382             return (a[0] === b[0] && a[1] === b[1]) ||
9383                 (a[0] === b[1] && a[1] === b[0]);
9384         }
9385
9386
9387         // Rotate all points counterclockwise around a pivot point by given angle
9388         function geoRotate(points, angle, around) {
9389             return points.map(function(point) {
9390                 var radial = geoVecSubtract(point, around);
9391                 return [
9392                     radial[0] * Math.cos(angle) - radial[1] * Math.sin(angle) + around[0],
9393                     radial[0] * Math.sin(angle) + radial[1] * Math.cos(angle) + around[1]
9394                 ];
9395             });
9396         }
9397
9398
9399         // Choose the edge with the minimal distance from `point` to its orthogonal
9400         // projection onto that edge, if such a projection exists, or the distance to
9401         // the closest vertex on that edge. Returns an object with the `index` of the
9402         // chosen edge, the chosen `loc` on that edge, and the `distance` to to it.
9403         function geoChooseEdge(nodes, point, projection, activeID) {
9404             var dist = geoVecLength;
9405             var points = nodes.map(function(n) { return projection(n.loc); });
9406             var ids = nodes.map(function(n) { return n.id; });
9407             var min = Infinity;
9408             var idx;
9409             var loc;
9410
9411             for (var i = 0; i < points.length - 1; i++) {
9412                 if (ids[i] === activeID || ids[i + 1] === activeID) continue;
9413
9414                 var o = points[i];
9415                 var s = geoVecSubtract(points[i + 1], o);
9416                 var v = geoVecSubtract(point, o);
9417                 var proj = geoVecDot(v, s) / geoVecDot(s, s);
9418                 var p;
9419
9420                 if (proj < 0) {
9421                     p = o;
9422                 } else if (proj > 1) {
9423                     p = points[i + 1];
9424                 } else {
9425                     p = [o[0] + proj * s[0], o[1] + proj * s[1]];
9426                 }
9427
9428                 var d = dist(p, point);
9429                 if (d < min) {
9430                     min = d;
9431                     idx = i + 1;
9432                     loc = projection.invert(p);
9433                 }
9434             }
9435
9436             if (idx !== undefined) {
9437                 return { index: idx, distance: min, loc: loc };
9438             } else {
9439                 return null;
9440             }
9441         }
9442
9443
9444         // Test active (dragged or drawing) segments against inactive segments
9445         // This is used to test e.g. multipolygon rings that cross
9446         // `activeNodes` is the ring containing the activeID being dragged.
9447         // `inactiveNodes` is the other ring to test against
9448         function geoHasLineIntersections(activeNodes, inactiveNodes, activeID) {
9449             var actives = [];
9450             var inactives = [];
9451             var j, k, n1, n2, segment;
9452
9453             // gather active segments (only segments in activeNodes that contain the activeID)
9454             for (j = 0; j < activeNodes.length - 1; j++) {
9455                 n1 = activeNodes[j];
9456                 n2 = activeNodes[j+1];
9457                 segment = [n1.loc, n2.loc];
9458                 if (n1.id === activeID || n2.id === activeID) {
9459                     actives.push(segment);
9460                 }
9461             }
9462
9463             // gather inactive segments
9464             for (j = 0; j < inactiveNodes.length - 1; j++) {
9465                 n1 = inactiveNodes[j];
9466                 n2 = inactiveNodes[j+1];
9467                 segment = [n1.loc, n2.loc];
9468                 inactives.push(segment);
9469             }
9470
9471             // test
9472             for (j = 0; j < actives.length; j++) {
9473                 for (k = 0; k < inactives.length; k++) {
9474                     var p = actives[j];
9475                     var q = inactives[k];
9476                     var hit = geoLineIntersection(p, q);
9477                     if (hit) {
9478                         return true;
9479                     }
9480                 }
9481             }
9482
9483             return false;
9484         }
9485
9486
9487         // Test active (dragged or drawing) segments against inactive segments
9488         // This is used to test whether a way intersects with itself.
9489         function geoHasSelfIntersections(nodes, activeID) {
9490             var actives = [];
9491             var inactives = [];
9492             var j, k;
9493
9494             // group active and passive segments along the nodes
9495             for (j = 0; j < nodes.length - 1; j++) {
9496                 var n1 = nodes[j];
9497                 var n2 = nodes[j+1];
9498                 var segment = [n1.loc, n2.loc];
9499                 if (n1.id === activeID || n2.id === activeID) {
9500                     actives.push(segment);
9501                 } else {
9502                     inactives.push(segment);
9503                 }
9504             }
9505
9506             // test
9507             for (j = 0; j < actives.length; j++) {
9508                 for (k = 0; k < inactives.length; k++) {
9509                     var p = actives[j];
9510                     var q = inactives[k];
9511                     // skip if segments share an endpoint
9512                     if (geoVecEqual(p[1], q[0]) || geoVecEqual(p[0], q[1]) ||
9513                         geoVecEqual(p[0], q[0]) || geoVecEqual(p[1], q[1]) ) {
9514                         continue;
9515                     }
9516
9517                     var hit = geoLineIntersection(p, q);
9518                     if (hit) {
9519                         var epsilon = 1e-8;
9520                         // skip if the hit is at the segment's endpoint
9521                         if (geoVecEqual(p[1], hit, epsilon) || geoVecEqual(p[0], hit, epsilon) ||
9522                             geoVecEqual(q[1], hit, epsilon) || geoVecEqual(q[0], hit, epsilon) ) {
9523                             continue;
9524                         } else {
9525                             return true;
9526                         }
9527                     }
9528                 }
9529             }
9530
9531             return false;
9532         }
9533
9534
9535         // Return the intersection point of 2 line segments.
9536         // From https://github.com/pgkelley4/line-segments-intersect
9537         // This uses the vector cross product approach described below:
9538         //  http://stackoverflow.com/a/565282/786339
9539         function geoLineIntersection(a, b) {
9540             var p = [a[0][0], a[0][1]];
9541             var p2 = [a[1][0], a[1][1]];
9542             var q = [b[0][0], b[0][1]];
9543             var q2 = [b[1][0], b[1][1]];
9544             var r = geoVecSubtract(p2, p);
9545             var s = geoVecSubtract(q2, q);
9546             var uNumerator = geoVecCross(geoVecSubtract(q, p), r);
9547             var denominator = geoVecCross(r, s);
9548
9549             if (uNumerator && denominator) {
9550                 var u = uNumerator / denominator;
9551                 var t = geoVecCross(geoVecSubtract(q, p), s) / denominator;
9552
9553                 if ((t >= 0) && (t <= 1) && (u >= 0) && (u <= 1)) {
9554                     return geoVecInterp(p, p2, t);
9555                 }
9556             }
9557
9558             return null;
9559         }
9560
9561
9562         function geoPathIntersections(path1, path2) {
9563             var intersections = [];
9564             for (var i = 0; i < path1.length - 1; i++) {
9565                 for (var j = 0; j < path2.length - 1; j++) {
9566                     var a = [ path1[i], path1[i+1] ];
9567                     var b = [ path2[j], path2[j+1] ];
9568                     var hit = geoLineIntersection(a, b);
9569                     if (hit) {
9570                         intersections.push(hit);
9571                     }
9572                 }
9573             }
9574             return intersections;
9575         }
9576
9577         function geoPathHasIntersections(path1, path2) {
9578             for (var i = 0; i < path1.length - 1; i++) {
9579                 for (var j = 0; j < path2.length - 1; j++) {
9580                     var a = [ path1[i], path1[i+1] ];
9581                     var b = [ path2[j], path2[j+1] ];
9582                     var hit = geoLineIntersection(a, b);
9583                     if (hit) {
9584                         return true;
9585                     }
9586                 }
9587             }
9588             return false;
9589         }
9590
9591
9592         // Return whether point is contained in polygon.
9593         //
9594         // `point` should be a 2-item array of coordinates.
9595         // `polygon` should be an array of 2-item arrays of coordinates.
9596         //
9597         // From https://github.com/substack/point-in-polygon.
9598         // ray-casting algorithm based on
9599         // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
9600         //
9601         function geoPointInPolygon(point, polygon) {
9602             var x = point[0];
9603             var y = point[1];
9604             var inside = false;
9605
9606             for (var i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
9607                 var xi = polygon[i][0];
9608                 var yi = polygon[i][1];
9609                 var xj = polygon[j][0];
9610                 var yj = polygon[j][1];
9611
9612                 var intersect = ((yi > y) !== (yj > y)) &&
9613                     (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
9614                 if (intersect) inside = !inside;
9615             }
9616
9617             return inside;
9618         }
9619
9620
9621         function geoPolygonContainsPolygon(outer, inner) {
9622             return inner.every(function(point) {
9623                 return geoPointInPolygon(point, outer);
9624             });
9625         }
9626
9627
9628         function geoPolygonIntersectsPolygon(outer, inner, checkSegments) {
9629             function testPoints(outer, inner) {
9630                 return inner.some(function(point) {
9631                     return geoPointInPolygon(point, outer);
9632                 });
9633             }
9634
9635            return testPoints(outer, inner) || (!!checkSegments && geoPathHasIntersections(outer, inner));
9636         }
9637
9638
9639         // http://gis.stackexchange.com/questions/22895/finding-minimum-area-rectangle-for-given-points
9640         // http://gis.stackexchange.com/questions/3739/generalisation-strategies-for-building-outlines/3756#3756
9641         function geoGetSmallestSurroundingRectangle(points) {
9642             var hull = d3_polygonHull(points);
9643             var centroid = d3_polygonCentroid(hull);
9644             var minArea = Infinity;
9645             var ssrExtent = [];
9646             var ssrAngle = 0;
9647             var c1 = hull[0];
9648
9649             for (var i = 0; i <= hull.length - 1; i++) {
9650                 var c2 = (i === hull.length - 1) ? hull[0] : hull[i + 1];
9651                 var angle = Math.atan2(c2[1] - c1[1], c2[0] - c1[0]);
9652                 var poly = geoRotate(hull, -angle, centroid);
9653                 var extent = poly.reduce(function(extent, point) {
9654                     return extent.extend(geoExtent(point));
9655                 }, geoExtent());
9656
9657                 var area = extent.area();
9658                 if (area < minArea) {
9659                     minArea = area;
9660                     ssrExtent = extent;
9661                     ssrAngle = angle;
9662                 }
9663                 c1 = c2;
9664             }
9665
9666             return {
9667                 poly: geoRotate(ssrExtent.polygon(), ssrAngle, centroid),
9668                 angle: ssrAngle
9669             };
9670         }
9671
9672
9673         function geoPathLength(path) {
9674             var length = 0;
9675             for (var i = 0; i < path.length - 1; i++) {
9676                 length += geoVecLength(path[i], path[i + 1]);
9677             }
9678             return length;
9679         }
9680
9681
9682         // If the given point is at the edge of the padded viewport,
9683         // return a vector that will nudge the viewport in that direction
9684         function geoViewportEdge(point, dimensions) {
9685             var pad = [80, 20, 50, 20];   // top, right, bottom, left
9686             var x = 0;
9687             var y = 0;
9688
9689             if (point[0] > dimensions[0] - pad[1])
9690                 x = -10;
9691             if (point[0] < pad[3])
9692                 x = 10;
9693             if (point[1] > dimensions[1] - pad[2])
9694                 y = -10;
9695             if (point[1] < pad[0])
9696                 y = 10;
9697
9698             if (x || y) {
9699                 return [x, y];
9700             } else {
9701                 return null;
9702             }
9703         }
9704
9705         var noop$3 = {value: function() {}};
9706
9707         function dispatch() {
9708           for (var i = 0, n = arguments.length, _ = {}, t; i < n; ++i) {
9709             if (!(t = arguments[i] + "") || (t in _) || /[\s.]/.test(t)) throw new Error("illegal type: " + t);
9710             _[t] = [];
9711           }
9712           return new Dispatch(_);
9713         }
9714
9715         function Dispatch(_) {
9716           this._ = _;
9717         }
9718
9719         function parseTypenames(typenames, types) {
9720           return typenames.trim().split(/^|\s+/).map(function(t) {
9721             var name = "", i = t.indexOf(".");
9722             if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);
9723             if (t && !types.hasOwnProperty(t)) throw new Error("unknown type: " + t);
9724             return {type: t, name: name};
9725           });
9726         }
9727
9728         Dispatch.prototype = dispatch.prototype = {
9729           constructor: Dispatch,
9730           on: function(typename, callback) {
9731             var _ = this._,
9732                 T = parseTypenames(typename + "", _),
9733                 t,
9734                 i = -1,
9735                 n = T.length;
9736
9737             // If no callback was specified, return the callback of the given type and name.
9738             if (arguments.length < 2) {
9739               while (++i < n) if ((t = (typename = T[i]).type) && (t = get$1(_[t], typename.name))) return t;
9740               return;
9741             }
9742
9743             // If a type was specified, set the callback for the given type and name.
9744             // Otherwise, if a null callback was specified, remove callbacks of the given name.
9745             if (callback != null && typeof callback !== "function") throw new Error("invalid callback: " + callback);
9746             while (++i < n) {
9747               if (t = (typename = T[i]).type) _[t] = set(_[t], typename.name, callback);
9748               else if (callback == null) for (t in _) _[t] = set(_[t], typename.name, null);
9749             }
9750
9751             return this;
9752           },
9753           copy: function() {
9754             var copy = {}, _ = this._;
9755             for (var t in _) copy[t] = _[t].slice();
9756             return new Dispatch(copy);
9757           },
9758           call: function(type, that) {
9759             if ((n = arguments.length - 2) > 0) for (var args = new Array(n), i = 0, n, t; i < n; ++i) args[i] = arguments[i + 2];
9760             if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type);
9761             for (t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args);
9762           },
9763           apply: function(type, that, args) {
9764             if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type);
9765             for (var t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args);
9766           }
9767         };
9768
9769         function get$1(type, name) {
9770           for (var i = 0, n = type.length, c; i < n; ++i) {
9771             if ((c = type[i]).name === name) {
9772               return c.value;
9773             }
9774           }
9775         }
9776
9777         function set(type, name, callback) {
9778           for (var i = 0, n = type.length; i < n; ++i) {
9779             if (type[i].name === name) {
9780               type[i] = noop$3, type = type.slice(0, i).concat(type.slice(i + 1));
9781               break;
9782             }
9783           }
9784           if (callback != null) type.push({name: name, value: callback});
9785           return type;
9786         }
9787
9788         var xhtml = "http://www.w3.org/1999/xhtml";
9789
9790         var namespaces = {
9791           svg: "http://www.w3.org/2000/svg",
9792           xhtml: xhtml,
9793           xlink: "http://www.w3.org/1999/xlink",
9794           xml: "http://www.w3.org/XML/1998/namespace",
9795           xmlns: "http://www.w3.org/2000/xmlns/"
9796         };
9797
9798         function namespace(name) {
9799           var prefix = name += "", i = prefix.indexOf(":");
9800           if (i >= 0 && (prefix = name.slice(0, i)) !== "xmlns") name = name.slice(i + 1);
9801           return namespaces.hasOwnProperty(prefix) ? {space: namespaces[prefix], local: name} : name;
9802         }
9803
9804         function creatorInherit(name) {
9805           return function() {
9806             var document = this.ownerDocument,
9807                 uri = this.namespaceURI;
9808             return uri === xhtml && document.documentElement.namespaceURI === xhtml
9809                 ? document.createElement(name)
9810                 : document.createElementNS(uri, name);
9811           };
9812         }
9813
9814         function creatorFixed(fullname) {
9815           return function() {
9816             return this.ownerDocument.createElementNS(fullname.space, fullname.local);
9817           };
9818         }
9819
9820         function creator(name) {
9821           var fullname = namespace(name);
9822           return (fullname.local
9823               ? creatorFixed
9824               : creatorInherit)(fullname);
9825         }
9826
9827         function none() {}
9828
9829         function selector(selector) {
9830           return selector == null ? none : function() {
9831             return this.querySelector(selector);
9832           };
9833         }
9834
9835         function selection_select(select) {
9836           if (typeof select !== "function") select = selector(select);
9837
9838           for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
9839             for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {
9840               if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {
9841                 if ("__data__" in node) subnode.__data__ = node.__data__;
9842                 subgroup[i] = subnode;
9843               }
9844             }
9845           }
9846
9847           return new Selection(subgroups, this._parents);
9848         }
9849
9850         function empty() {
9851           return [];
9852         }
9853
9854         function selectorAll(selector) {
9855           return selector == null ? empty : function() {
9856             return this.querySelectorAll(selector);
9857           };
9858         }
9859
9860         function selection_selectAll(select) {
9861           if (typeof select !== "function") select = selectorAll(select);
9862
9863           for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {
9864             for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
9865               if (node = group[i]) {
9866                 subgroups.push(select.call(node, node.__data__, i, group));
9867                 parents.push(node);
9868               }
9869             }
9870           }
9871
9872           return new Selection(subgroups, parents);
9873         }
9874
9875         function matcher(selector) {
9876           return function() {
9877             return this.matches(selector);
9878           };
9879         }
9880
9881         function selection_filter(match) {
9882           if (typeof match !== "function") match = matcher(match);
9883
9884           for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
9885             for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {
9886               if ((node = group[i]) && match.call(node, node.__data__, i, group)) {
9887                 subgroup.push(node);
9888               }
9889             }
9890           }
9891
9892           return new Selection(subgroups, this._parents);
9893         }
9894
9895         function sparse(update) {
9896           return new Array(update.length);
9897         }
9898
9899         function selection_enter() {
9900           return new Selection(this._enter || this._groups.map(sparse), this._parents);
9901         }
9902
9903         function EnterNode(parent, datum) {
9904           this.ownerDocument = parent.ownerDocument;
9905           this.namespaceURI = parent.namespaceURI;
9906           this._next = null;
9907           this._parent = parent;
9908           this.__data__ = datum;
9909         }
9910
9911         EnterNode.prototype = {
9912           constructor: EnterNode,
9913           appendChild: function(child) { return this._parent.insertBefore(child, this._next); },
9914           insertBefore: function(child, next) { return this._parent.insertBefore(child, next); },
9915           querySelector: function(selector) { return this._parent.querySelector(selector); },
9916           querySelectorAll: function(selector) { return this._parent.querySelectorAll(selector); }
9917         };
9918
9919         function constant(x) {
9920           return function() {
9921             return x;
9922           };
9923         }
9924
9925         var keyPrefix = "$"; // Protect against keys like “__proto__”.
9926
9927         function bindIndex(parent, group, enter, update, exit, data) {
9928           var i = 0,
9929               node,
9930               groupLength = group.length,
9931               dataLength = data.length;
9932
9933           // Put any non-null nodes that fit into update.
9934           // Put any null nodes into enter.
9935           // Put any remaining data into enter.
9936           for (; i < dataLength; ++i) {
9937             if (node = group[i]) {
9938               node.__data__ = data[i];
9939               update[i] = node;
9940             } else {
9941               enter[i] = new EnterNode(parent, data[i]);
9942             }
9943           }
9944
9945           // Put any non-null nodes that don’t fit into exit.
9946           for (; i < groupLength; ++i) {
9947             if (node = group[i]) {
9948               exit[i] = node;
9949             }
9950           }
9951         }
9952
9953         function bindKey(parent, group, enter, update, exit, data, key) {
9954           var i,
9955               node,
9956               nodeByKeyValue = {},
9957               groupLength = group.length,
9958               dataLength = data.length,
9959               keyValues = new Array(groupLength),
9960               keyValue;
9961
9962           // Compute the key for each node.
9963           // If multiple nodes have the same key, the duplicates are added to exit.
9964           for (i = 0; i < groupLength; ++i) {
9965             if (node = group[i]) {
9966               keyValues[i] = keyValue = keyPrefix + key.call(node, node.__data__, i, group);
9967               if (keyValue in nodeByKeyValue) {
9968                 exit[i] = node;
9969               } else {
9970                 nodeByKeyValue[keyValue] = node;
9971               }
9972             }
9973           }
9974
9975           // Compute the key for each datum.
9976           // If there a node associated with this key, join and add it to update.
9977           // If there is not (or the key is a duplicate), add it to enter.
9978           for (i = 0; i < dataLength; ++i) {
9979             keyValue = keyPrefix + key.call(parent, data[i], i, data);
9980             if (node = nodeByKeyValue[keyValue]) {
9981               update[i] = node;
9982               node.__data__ = data[i];
9983               nodeByKeyValue[keyValue] = null;
9984             } else {
9985               enter[i] = new EnterNode(parent, data[i]);
9986             }
9987           }
9988
9989           // Add any remaining nodes that were not bound to data to exit.
9990           for (i = 0; i < groupLength; ++i) {
9991             if ((node = group[i]) && (nodeByKeyValue[keyValues[i]] === node)) {
9992               exit[i] = node;
9993             }
9994           }
9995         }
9996
9997         function selection_data(value, key) {
9998           if (!value) {
9999             data = new Array(this.size()), j = -1;
10000             this.each(function(d) { data[++j] = d; });
10001             return data;
10002           }
10003
10004           var bind = key ? bindKey : bindIndex,
10005               parents = this._parents,
10006               groups = this._groups;
10007
10008           if (typeof value !== "function") value = constant(value);
10009
10010           for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) {
10011             var parent = parents[j],
10012                 group = groups[j],
10013                 groupLength = group.length,
10014                 data = value.call(parent, parent && parent.__data__, j, parents),
10015                 dataLength = data.length,
10016                 enterGroup = enter[j] = new Array(dataLength),
10017                 updateGroup = update[j] = new Array(dataLength),
10018                 exitGroup = exit[j] = new Array(groupLength);
10019
10020             bind(parent, group, enterGroup, updateGroup, exitGroup, data, key);
10021
10022             // Now connect the enter nodes to their following update node, such that
10023             // appendChild can insert the materialized enter node before this node,
10024             // rather than at the end of the parent node.
10025             for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) {
10026               if (previous = enterGroup[i0]) {
10027                 if (i0 >= i1) i1 = i0 + 1;
10028                 while (!(next = updateGroup[i1]) && ++i1 < dataLength);
10029                 previous._next = next || null;
10030               }
10031             }
10032           }
10033
10034           update = new Selection(update, parents);
10035           update._enter = enter;
10036           update._exit = exit;
10037           return update;
10038         }
10039
10040         function selection_exit() {
10041           return new Selection(this._exit || this._groups.map(sparse), this._parents);
10042         }
10043
10044         function selection_join(onenter, onupdate, onexit) {
10045           var enter = this.enter(), update = this, exit = this.exit();
10046           enter = typeof onenter === "function" ? onenter(enter) : enter.append(onenter + "");
10047           if (onupdate != null) update = onupdate(update);
10048           if (onexit == null) exit.remove(); else onexit(exit);
10049           return enter && update ? enter.merge(update).order() : update;
10050         }
10051
10052         function selection_merge(selection) {
10053
10054           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) {
10055             for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {
10056               if (node = group0[i] || group1[i]) {
10057                 merge[i] = node;
10058               }
10059             }
10060           }
10061
10062           for (; j < m0; ++j) {
10063             merges[j] = groups0[j];
10064           }
10065
10066           return new Selection(merges, this._parents);
10067         }
10068
10069         function selection_order() {
10070
10071           for (var groups = this._groups, j = -1, m = groups.length; ++j < m;) {
10072             for (var group = groups[j], i = group.length - 1, next = group[i], node; --i >= 0;) {
10073               if (node = group[i]) {
10074                 if (next && node.compareDocumentPosition(next) ^ 4) next.parentNode.insertBefore(node, next);
10075                 next = node;
10076               }
10077             }
10078           }
10079
10080           return this;
10081         }
10082
10083         function selection_sort(compare) {
10084           if (!compare) compare = ascending;
10085
10086           function compareNode(a, b) {
10087             return a && b ? compare(a.__data__, b.__data__) : !a - !b;
10088           }
10089
10090           for (var groups = this._groups, m = groups.length, sortgroups = new Array(m), j = 0; j < m; ++j) {
10091             for (var group = groups[j], n = group.length, sortgroup = sortgroups[j] = new Array(n), node, i = 0; i < n; ++i) {
10092               if (node = group[i]) {
10093                 sortgroup[i] = node;
10094               }
10095             }
10096             sortgroup.sort(compareNode);
10097           }
10098
10099           return new Selection(sortgroups, this._parents).order();
10100         }
10101
10102         function ascending(a, b) {
10103           return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
10104         }
10105
10106         function selection_call() {
10107           var callback = arguments[0];
10108           arguments[0] = this;
10109           callback.apply(null, arguments);
10110           return this;
10111         }
10112
10113         function selection_nodes() {
10114           var nodes = new Array(this.size()), i = -1;
10115           this.each(function() { nodes[++i] = this; });
10116           return nodes;
10117         }
10118
10119         function selection_node() {
10120
10121           for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {
10122             for (var group = groups[j], i = 0, n = group.length; i < n; ++i) {
10123               var node = group[i];
10124               if (node) return node;
10125             }
10126           }
10127
10128           return null;
10129         }
10130
10131         function selection_size() {
10132           var size = 0;
10133           this.each(function() { ++size; });
10134           return size;
10135         }
10136
10137         function selection_empty() {
10138           return !this.node();
10139         }
10140
10141         function selection_each(callback) {
10142
10143           for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {
10144             for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) {
10145               if (node = group[i]) callback.call(node, node.__data__, i, group);
10146             }
10147           }
10148
10149           return this;
10150         }
10151
10152         function attrRemove(name) {
10153           return function() {
10154             this.removeAttribute(name);
10155           };
10156         }
10157
10158         function attrRemoveNS(fullname) {
10159           return function() {
10160             this.removeAttributeNS(fullname.space, fullname.local);
10161           };
10162         }
10163
10164         function attrConstant(name, value) {
10165           return function() {
10166             this.setAttribute(name, value);
10167           };
10168         }
10169
10170         function attrConstantNS(fullname, value) {
10171           return function() {
10172             this.setAttributeNS(fullname.space, fullname.local, value);
10173           };
10174         }
10175
10176         function attrFunction(name, value) {
10177           return function() {
10178             var v = value.apply(this, arguments);
10179             if (v == null) this.removeAttribute(name);
10180             else this.setAttribute(name, v);
10181           };
10182         }
10183
10184         function attrFunctionNS(fullname, value) {
10185           return function() {
10186             var v = value.apply(this, arguments);
10187             if (v == null) this.removeAttributeNS(fullname.space, fullname.local);
10188             else this.setAttributeNS(fullname.space, fullname.local, v);
10189           };
10190         }
10191
10192         function selection_attr(name, value) {
10193           var fullname = namespace(name);
10194
10195           if (arguments.length < 2) {
10196             var node = this.node();
10197             return fullname.local
10198                 ? node.getAttributeNS(fullname.space, fullname.local)
10199                 : node.getAttribute(fullname);
10200           }
10201
10202           return this.each((value == null
10203               ? (fullname.local ? attrRemoveNS : attrRemove) : (typeof value === "function"
10204               ? (fullname.local ? attrFunctionNS : attrFunction)
10205               : (fullname.local ? attrConstantNS : attrConstant)))(fullname, value));
10206         }
10207
10208         function defaultView(node) {
10209           return (node.ownerDocument && node.ownerDocument.defaultView) // node is a Node
10210               || (node.document && node) // node is a Window
10211               || node.defaultView; // node is a Document
10212         }
10213
10214         function styleRemove(name) {
10215           return function() {
10216             this.style.removeProperty(name);
10217           };
10218         }
10219
10220         function styleConstant(name, value, priority) {
10221           return function() {
10222             this.style.setProperty(name, value, priority);
10223           };
10224         }
10225
10226         function styleFunction(name, value, priority) {
10227           return function() {
10228             var v = value.apply(this, arguments);
10229             if (v == null) this.style.removeProperty(name);
10230             else this.style.setProperty(name, v, priority);
10231           };
10232         }
10233
10234         function selection_style(name, value, priority) {
10235           return arguments.length > 1
10236               ? this.each((value == null
10237                     ? styleRemove : typeof value === "function"
10238                     ? styleFunction
10239                     : styleConstant)(name, value, priority == null ? "" : priority))
10240               : styleValue(this.node(), name);
10241         }
10242
10243         function styleValue(node, name) {
10244           return node.style.getPropertyValue(name)
10245               || defaultView(node).getComputedStyle(node, null).getPropertyValue(name);
10246         }
10247
10248         function propertyRemove(name) {
10249           return function() {
10250             delete this[name];
10251           };
10252         }
10253
10254         function propertyConstant(name, value) {
10255           return function() {
10256             this[name] = value;
10257           };
10258         }
10259
10260         function propertyFunction(name, value) {
10261           return function() {
10262             var v = value.apply(this, arguments);
10263             if (v == null) delete this[name];
10264             else this[name] = v;
10265           };
10266         }
10267
10268         function selection_property(name, value) {
10269           return arguments.length > 1
10270               ? this.each((value == null
10271                   ? propertyRemove : typeof value === "function"
10272                   ? propertyFunction
10273                   : propertyConstant)(name, value))
10274               : this.node()[name];
10275         }
10276
10277         function classArray(string) {
10278           return string.trim().split(/^|\s+/);
10279         }
10280
10281         function classList(node) {
10282           return node.classList || new ClassList(node);
10283         }
10284
10285         function ClassList(node) {
10286           this._node = node;
10287           this._names = classArray(node.getAttribute("class") || "");
10288         }
10289
10290         ClassList.prototype = {
10291           add: function(name) {
10292             var i = this._names.indexOf(name);
10293             if (i < 0) {
10294               this._names.push(name);
10295               this._node.setAttribute("class", this._names.join(" "));
10296             }
10297           },
10298           remove: function(name) {
10299             var i = this._names.indexOf(name);
10300             if (i >= 0) {
10301               this._names.splice(i, 1);
10302               this._node.setAttribute("class", this._names.join(" "));
10303             }
10304           },
10305           contains: function(name) {
10306             return this._names.indexOf(name) >= 0;
10307           }
10308         };
10309
10310         function classedAdd(node, names) {
10311           var list = classList(node), i = -1, n = names.length;
10312           while (++i < n) list.add(names[i]);
10313         }
10314
10315         function classedRemove(node, names) {
10316           var list = classList(node), i = -1, n = names.length;
10317           while (++i < n) list.remove(names[i]);
10318         }
10319
10320         function classedTrue(names) {
10321           return function() {
10322             classedAdd(this, names);
10323           };
10324         }
10325
10326         function classedFalse(names) {
10327           return function() {
10328             classedRemove(this, names);
10329           };
10330         }
10331
10332         function classedFunction(names, value) {
10333           return function() {
10334             (value.apply(this, arguments) ? classedAdd : classedRemove)(this, names);
10335           };
10336         }
10337
10338         function selection_classed(name, value) {
10339           var names = classArray(name + "");
10340
10341           if (arguments.length < 2) {
10342             var list = classList(this.node()), i = -1, n = names.length;
10343             while (++i < n) if (!list.contains(names[i])) return false;
10344             return true;
10345           }
10346
10347           return this.each((typeof value === "function"
10348               ? classedFunction : value
10349               ? classedTrue
10350               : classedFalse)(names, value));
10351         }
10352
10353         function textRemove() {
10354           this.textContent = "";
10355         }
10356
10357         function textConstant(value) {
10358           return function() {
10359             this.textContent = value;
10360           };
10361         }
10362
10363         function textFunction(value) {
10364           return function() {
10365             var v = value.apply(this, arguments);
10366             this.textContent = v == null ? "" : v;
10367           };
10368         }
10369
10370         function selection_text(value) {
10371           return arguments.length
10372               ? this.each(value == null
10373                   ? textRemove : (typeof value === "function"
10374                   ? textFunction
10375                   : textConstant)(value))
10376               : this.node().textContent;
10377         }
10378
10379         function htmlRemove() {
10380           this.innerHTML = "";
10381         }
10382
10383         function htmlConstant(value) {
10384           return function() {
10385             this.innerHTML = value;
10386           };
10387         }
10388
10389         function htmlFunction(value) {
10390           return function() {
10391             var v = value.apply(this, arguments);
10392             this.innerHTML = v == null ? "" : v;
10393           };
10394         }
10395
10396         function selection_html(value) {
10397           return arguments.length
10398               ? this.each(value == null
10399                   ? htmlRemove : (typeof value === "function"
10400                   ? htmlFunction
10401                   : htmlConstant)(value))
10402               : this.node().innerHTML;
10403         }
10404
10405         function raise() {
10406           if (this.nextSibling) this.parentNode.appendChild(this);
10407         }
10408
10409         function selection_raise() {
10410           return this.each(raise);
10411         }
10412
10413         function lower() {
10414           if (this.previousSibling) this.parentNode.insertBefore(this, this.parentNode.firstChild);
10415         }
10416
10417         function selection_lower() {
10418           return this.each(lower);
10419         }
10420
10421         function selection_append(name) {
10422           var create = typeof name === "function" ? name : creator(name);
10423           return this.select(function() {
10424             return this.appendChild(create.apply(this, arguments));
10425           });
10426         }
10427
10428         function constantNull() {
10429           return null;
10430         }
10431
10432         function selection_insert(name, before) {
10433           var create = typeof name === "function" ? name : creator(name),
10434               select = before == null ? constantNull : typeof before === "function" ? before : selector(before);
10435           return this.select(function() {
10436             return this.insertBefore(create.apply(this, arguments), select.apply(this, arguments) || null);
10437           });
10438         }
10439
10440         function remove() {
10441           var parent = this.parentNode;
10442           if (parent) parent.removeChild(this);
10443         }
10444
10445         function selection_remove() {
10446           return this.each(remove);
10447         }
10448
10449         function selection_cloneShallow() {
10450           var clone = this.cloneNode(false), parent = this.parentNode;
10451           return parent ? parent.insertBefore(clone, this.nextSibling) : clone;
10452         }
10453
10454         function selection_cloneDeep() {
10455           var clone = this.cloneNode(true), parent = this.parentNode;
10456           return parent ? parent.insertBefore(clone, this.nextSibling) : clone;
10457         }
10458
10459         function selection_clone(deep) {
10460           return this.select(deep ? selection_cloneDeep : selection_cloneShallow);
10461         }
10462
10463         function selection_datum(value) {
10464           return arguments.length
10465               ? this.property("__data__", value)
10466               : this.node().__data__;
10467         }
10468
10469         var filterEvents = {};
10470
10471         var event = null;
10472
10473         if (typeof document !== "undefined") {
10474           var element = document.documentElement;
10475           if (!("onmouseenter" in element)) {
10476             filterEvents = {mouseenter: "mouseover", mouseleave: "mouseout"};
10477           }
10478         }
10479
10480         function filterContextListener(listener, index, group) {
10481           listener = contextListener(listener, index, group);
10482           return function(event) {
10483             var related = event.relatedTarget;
10484             if (!related || (related !== this && !(related.compareDocumentPosition(this) & 8))) {
10485               listener.call(this, event);
10486             }
10487           };
10488         }
10489
10490         function contextListener(listener, index, group) {
10491           return function(event1) {
10492             var event0 = event; // Events can be reentrant (e.g., focus).
10493             event = event1;
10494             try {
10495               listener.call(this, this.__data__, index, group);
10496             } finally {
10497               event = event0;
10498             }
10499           };
10500         }
10501
10502         function parseTypenames$1(typenames) {
10503           return typenames.trim().split(/^|\s+/).map(function(t) {
10504             var name = "", i = t.indexOf(".");
10505             if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);
10506             return {type: t, name: name};
10507           });
10508         }
10509
10510         function onRemove(typename) {
10511           return function() {
10512             var on = this.__on;
10513             if (!on) return;
10514             for (var j = 0, i = -1, m = on.length, o; j < m; ++j) {
10515               if (o = on[j], (!typename.type || o.type === typename.type) && o.name === typename.name) {
10516                 this.removeEventListener(o.type, o.listener, o.capture);
10517               } else {
10518                 on[++i] = o;
10519               }
10520             }
10521             if (++i) on.length = i;
10522             else delete this.__on;
10523           };
10524         }
10525
10526         function onAdd(typename, value, capture) {
10527           var wrap = filterEvents.hasOwnProperty(typename.type) ? filterContextListener : contextListener;
10528           return function(d, i, group) {
10529             var on = this.__on, o, listener = wrap(value, i, group);
10530             if (on) for (var j = 0, m = on.length; j < m; ++j) {
10531               if ((o = on[j]).type === typename.type && o.name === typename.name) {
10532                 this.removeEventListener(o.type, o.listener, o.capture);
10533                 this.addEventListener(o.type, o.listener = listener, o.capture = capture);
10534                 o.value = value;
10535                 return;
10536               }
10537             }
10538             this.addEventListener(typename.type, listener, capture);
10539             o = {type: typename.type, name: typename.name, value: value, listener: listener, capture: capture};
10540             if (!on) this.__on = [o];
10541             else on.push(o);
10542           };
10543         }
10544
10545         function selection_on(typename, value, capture) {
10546           var typenames = parseTypenames$1(typename + ""), i, n = typenames.length, t;
10547
10548           if (arguments.length < 2) {
10549             var on = this.node().__on;
10550             if (on) for (var j = 0, m = on.length, o; j < m; ++j) {
10551               for (i = 0, o = on[j]; i < n; ++i) {
10552                 if ((t = typenames[i]).type === o.type && t.name === o.name) {
10553                   return o.value;
10554                 }
10555               }
10556             }
10557             return;
10558           }
10559
10560           on = value ? onAdd : onRemove;
10561           if (capture == null) capture = false;
10562           for (i = 0; i < n; ++i) this.each(on(typenames[i], value, capture));
10563           return this;
10564         }
10565
10566         function customEvent(event1, listener, that, args) {
10567           var event0 = event;
10568           event1.sourceEvent = event;
10569           event = event1;
10570           try {
10571             return listener.apply(that, args);
10572           } finally {
10573             event = event0;
10574           }
10575         }
10576
10577         function dispatchEvent(node, type, params) {
10578           var window = defaultView(node),
10579               event = window.CustomEvent;
10580
10581           if (typeof event === "function") {
10582             event = new event(type, params);
10583           } else {
10584             event = window.document.createEvent("Event");
10585             if (params) event.initEvent(type, params.bubbles, params.cancelable), event.detail = params.detail;
10586             else event.initEvent(type, false, false);
10587           }
10588
10589           node.dispatchEvent(event);
10590         }
10591
10592         function dispatchConstant(type, params) {
10593           return function() {
10594             return dispatchEvent(this, type, params);
10595           };
10596         }
10597
10598         function dispatchFunction(type, params) {
10599           return function() {
10600             return dispatchEvent(this, type, params.apply(this, arguments));
10601           };
10602         }
10603
10604         function selection_dispatch(type, params) {
10605           return this.each((typeof params === "function"
10606               ? dispatchFunction
10607               : dispatchConstant)(type, params));
10608         }
10609
10610         var root$1 = [null];
10611
10612         function Selection(groups, parents) {
10613           this._groups = groups;
10614           this._parents = parents;
10615         }
10616
10617         function selection() {
10618           return new Selection([[document.documentElement]], root$1);
10619         }
10620
10621         Selection.prototype = selection.prototype = {
10622           constructor: Selection,
10623           select: selection_select,
10624           selectAll: selection_selectAll,
10625           filter: selection_filter,
10626           data: selection_data,
10627           enter: selection_enter,
10628           exit: selection_exit,
10629           join: selection_join,
10630           merge: selection_merge,
10631           order: selection_order,
10632           sort: selection_sort,
10633           call: selection_call,
10634           nodes: selection_nodes,
10635           node: selection_node,
10636           size: selection_size,
10637           empty: selection_empty,
10638           each: selection_each,
10639           attr: selection_attr,
10640           style: selection_style,
10641           property: selection_property,
10642           classed: selection_classed,
10643           text: selection_text,
10644           html: selection_html,
10645           raise: selection_raise,
10646           lower: selection_lower,
10647           append: selection_append,
10648           insert: selection_insert,
10649           remove: selection_remove,
10650           clone: selection_clone,
10651           datum: selection_datum,
10652           on: selection_on,
10653           dispatch: selection_dispatch
10654         };
10655
10656         function select(selector) {
10657           return typeof selector === "string"
10658               ? new Selection([[document.querySelector(selector)]], [document.documentElement])
10659               : new Selection([[selector]], root$1);
10660         }
10661
10662         function sourceEvent() {
10663           var current = event, source;
10664           while (source = current.sourceEvent) current = source;
10665           return current;
10666         }
10667
10668         function point(node, event) {
10669           var svg = node.ownerSVGElement || node;
10670
10671           if (svg.createSVGPoint) {
10672             var point = svg.createSVGPoint();
10673             point.x = event.clientX, point.y = event.clientY;
10674             point = point.matrixTransform(node.getScreenCTM().inverse());
10675             return [point.x, point.y];
10676           }
10677
10678           var rect = node.getBoundingClientRect();
10679           return [event.clientX - rect.left - node.clientLeft, event.clientY - rect.top - node.clientTop];
10680         }
10681
10682         function mouse(node) {
10683           var event = sourceEvent();
10684           if (event.changedTouches) event = event.changedTouches[0];
10685           return point(node, event);
10686         }
10687
10688         function selectAll(selector) {
10689           return typeof selector === "string"
10690               ? new Selection([document.querySelectorAll(selector)], [document.documentElement])
10691               : new Selection([selector == null ? [] : selector], root$1);
10692         }
10693
10694         function touch(node, touches, identifier) {
10695           if (arguments.length < 3) identifier = touches, touches = sourceEvent().changedTouches;
10696
10697           for (var i = 0, n = touches ? touches.length : 0, touch; i < n; ++i) {
10698             if ((touch = touches[i]).identifier === identifier) {
10699               return point(node, touch);
10700             }
10701           }
10702
10703           return null;
10704         }
10705
10706         function nopropagation() {
10707           event.stopImmediatePropagation();
10708         }
10709
10710         function noevent() {
10711           event.preventDefault();
10712           event.stopImmediatePropagation();
10713         }
10714
10715         function dragDisable(view) {
10716           var root = view.document.documentElement,
10717               selection = select(view).on("dragstart.drag", noevent, true);
10718           if ("onselectstart" in root) {
10719             selection.on("selectstart.drag", noevent, true);
10720           } else {
10721             root.__noselect = root.style.MozUserSelect;
10722             root.style.MozUserSelect = "none";
10723           }
10724         }
10725
10726         function yesdrag(view, noclick) {
10727           var root = view.document.documentElement,
10728               selection = select(view).on("dragstart.drag", null);
10729           if (noclick) {
10730             selection.on("click.drag", noevent, true);
10731             setTimeout(function() { selection.on("click.drag", null); }, 0);
10732           }
10733           if ("onselectstart" in root) {
10734             selection.on("selectstart.drag", null);
10735           } else {
10736             root.style.MozUserSelect = root.__noselect;
10737             delete root.__noselect;
10738           }
10739         }
10740
10741         function constant$1(x) {
10742           return function() {
10743             return x;
10744           };
10745         }
10746
10747         function DragEvent(target, type, subject, id, active, x, y, dx, dy, dispatch) {
10748           this.target = target;
10749           this.type = type;
10750           this.subject = subject;
10751           this.identifier = id;
10752           this.active = active;
10753           this.x = x;
10754           this.y = y;
10755           this.dx = dx;
10756           this.dy = dy;
10757           this._ = dispatch;
10758         }
10759
10760         DragEvent.prototype.on = function() {
10761           var value = this._.on.apply(this._, arguments);
10762           return value === this._ ? this : value;
10763         };
10764
10765         // Ignore right-click, since that should open the context menu.
10766         function defaultFilter() {
10767           return !event.ctrlKey && !event.button;
10768         }
10769
10770         function defaultContainer() {
10771           return this.parentNode;
10772         }
10773
10774         function defaultSubject(d) {
10775           return d == null ? {x: event.x, y: event.y} : d;
10776         }
10777
10778         function defaultTouchable() {
10779           return navigator.maxTouchPoints || ("ontouchstart" in this);
10780         }
10781
10782         function d3_drag() {
10783           var filter = defaultFilter,
10784               container = defaultContainer,
10785               subject = defaultSubject,
10786               touchable = defaultTouchable,
10787               gestures = {},
10788               listeners = dispatch("start", "drag", "end"),
10789               active = 0,
10790               mousedownx,
10791               mousedowny,
10792               mousemoving,
10793               touchending,
10794               clickDistance2 = 0;
10795
10796           function drag(selection) {
10797             selection
10798                 .on("mousedown.drag", mousedowned)
10799               .filter(touchable)
10800                 .on("touchstart.drag", touchstarted)
10801                 .on("touchmove.drag", touchmoved)
10802                 .on("touchend.drag touchcancel.drag", touchended)
10803                 .style("touch-action", "none")
10804                 .style("-webkit-tap-highlight-color", "rgba(0,0,0,0)");
10805           }
10806
10807           function mousedowned() {
10808             if (touchending || !filter.apply(this, arguments)) return;
10809             var gesture = beforestart("mouse", container.apply(this, arguments), mouse, this, arguments);
10810             if (!gesture) return;
10811             select(event.view).on("mousemove.drag", mousemoved, true).on("mouseup.drag", mouseupped, true);
10812             dragDisable(event.view);
10813             nopropagation();
10814             mousemoving = false;
10815             mousedownx = event.clientX;
10816             mousedowny = event.clientY;
10817             gesture("start");
10818           }
10819
10820           function mousemoved() {
10821             noevent();
10822             if (!mousemoving) {
10823               var dx = event.clientX - mousedownx, dy = event.clientY - mousedowny;
10824               mousemoving = dx * dx + dy * dy > clickDistance2;
10825             }
10826             gestures.mouse("drag");
10827           }
10828
10829           function mouseupped() {
10830             select(event.view).on("mousemove.drag mouseup.drag", null);
10831             yesdrag(event.view, mousemoving);
10832             noevent();
10833             gestures.mouse("end");
10834           }
10835
10836           function touchstarted() {
10837             if (!filter.apply(this, arguments)) return;
10838             var touches = event.changedTouches,
10839                 c = container.apply(this, arguments),
10840                 n = touches.length, i, gesture;
10841
10842             for (i = 0; i < n; ++i) {
10843               if (gesture = beforestart(touches[i].identifier, c, touch, this, arguments)) {
10844                 nopropagation();
10845                 gesture("start");
10846               }
10847             }
10848           }
10849
10850           function touchmoved() {
10851             var touches = event.changedTouches,
10852                 n = touches.length, i, gesture;
10853
10854             for (i = 0; i < n; ++i) {
10855               if (gesture = gestures[touches[i].identifier]) {
10856                 noevent();
10857                 gesture("drag");
10858               }
10859             }
10860           }
10861
10862           function touchended() {
10863             var touches = event.changedTouches,
10864                 n = touches.length, i, gesture;
10865
10866             if (touchending) clearTimeout(touchending);
10867             touchending = setTimeout(function() { touchending = null; }, 500); // Ghost clicks are delayed!
10868             for (i = 0; i < n; ++i) {
10869               if (gesture = gestures[touches[i].identifier]) {
10870                 nopropagation();
10871                 gesture("end");
10872               }
10873             }
10874           }
10875
10876           function beforestart(id, container, point, that, args) {
10877             var p = point(container, id), s, dx, dy,
10878                 sublisteners = listeners.copy();
10879
10880             if (!customEvent(new DragEvent(drag, "beforestart", s, id, active, p[0], p[1], 0, 0, sublisteners), function() {
10881               if ((event.subject = s = subject.apply(that, args)) == null) return false;
10882               dx = s.x - p[0] || 0;
10883               dy = s.y - p[1] || 0;
10884               return true;
10885             })) return;
10886
10887             return function gesture(type) {
10888               var p0 = p, n;
10889               switch (type) {
10890                 case "start": gestures[id] = gesture, n = active++; break;
10891                 case "end": delete gestures[id], --active; // nobreak
10892                 case "drag": p = point(container, id), n = active; break;
10893               }
10894               customEvent(new DragEvent(drag, type, s, id, n, p[0] + dx, p[1] + dy, p[0] - p0[0], p[1] - p0[1], sublisteners), sublisteners.apply, sublisteners, [type, that, args]);
10895             };
10896           }
10897
10898           drag.filter = function(_) {
10899             return arguments.length ? (filter = typeof _ === "function" ? _ : constant$1(!!_), drag) : filter;
10900           };
10901
10902           drag.container = function(_) {
10903             return arguments.length ? (container = typeof _ === "function" ? _ : constant$1(_), drag) : container;
10904           };
10905
10906           drag.subject = function(_) {
10907             return arguments.length ? (subject = typeof _ === "function" ? _ : constant$1(_), drag) : subject;
10908           };
10909
10910           drag.touchable = function(_) {
10911             return arguments.length ? (touchable = typeof _ === "function" ? _ : constant$1(!!_), drag) : touchable;
10912           };
10913
10914           drag.on = function() {
10915             var value = listeners.on.apply(listeners, arguments);
10916             return value === listeners ? drag : value;
10917           };
10918
10919           drag.clickDistance = function(_) {
10920             return arguments.length ? (clickDistance2 = (_ = +_) * _, drag) : Math.sqrt(clickDistance2);
10921           };
10922
10923           return drag;
10924         }
10925
10926         function define$1(constructor, factory, prototype) {
10927           constructor.prototype = factory.prototype = prototype;
10928           prototype.constructor = constructor;
10929         }
10930
10931         function extend(parent, definition) {
10932           var prototype = Object.create(parent.prototype);
10933           for (var key in definition) prototype[key] = definition[key];
10934           return prototype;
10935         }
10936
10937         function Color() {}
10938
10939         var darker = 0.7;
10940         var brighter = 1 / darker;
10941
10942         var reI = "\\s*([+-]?\\d+)\\s*",
10943             reN = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*",
10944             reP = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*",
10945             reHex = /^#([0-9a-f]{3,8})$/,
10946             reRgbInteger = new RegExp("^rgb\\(" + [reI, reI, reI] + "\\)$"),
10947             reRgbPercent = new RegExp("^rgb\\(" + [reP, reP, reP] + "\\)$"),
10948             reRgbaInteger = new RegExp("^rgba\\(" + [reI, reI, reI, reN] + "\\)$"),
10949             reRgbaPercent = new RegExp("^rgba\\(" + [reP, reP, reP, reN] + "\\)$"),
10950             reHslPercent = new RegExp("^hsl\\(" + [reN, reP, reP] + "\\)$"),
10951             reHslaPercent = new RegExp("^hsla\\(" + [reN, reP, reP, reN] + "\\)$");
10952
10953         var named = {
10954           aliceblue: 0xf0f8ff,
10955           antiquewhite: 0xfaebd7,
10956           aqua: 0x00ffff,
10957           aquamarine: 0x7fffd4,
10958           azure: 0xf0ffff,
10959           beige: 0xf5f5dc,
10960           bisque: 0xffe4c4,
10961           black: 0x000000,
10962           blanchedalmond: 0xffebcd,
10963           blue: 0x0000ff,
10964           blueviolet: 0x8a2be2,
10965           brown: 0xa52a2a,
10966           burlywood: 0xdeb887,
10967           cadetblue: 0x5f9ea0,
10968           chartreuse: 0x7fff00,
10969           chocolate: 0xd2691e,
10970           coral: 0xff7f50,
10971           cornflowerblue: 0x6495ed,
10972           cornsilk: 0xfff8dc,
10973           crimson: 0xdc143c,
10974           cyan: 0x00ffff,
10975           darkblue: 0x00008b,
10976           darkcyan: 0x008b8b,
10977           darkgoldenrod: 0xb8860b,
10978           darkgray: 0xa9a9a9,
10979           darkgreen: 0x006400,
10980           darkgrey: 0xa9a9a9,
10981           darkkhaki: 0xbdb76b,
10982           darkmagenta: 0x8b008b,
10983           darkolivegreen: 0x556b2f,
10984           darkorange: 0xff8c00,
10985           darkorchid: 0x9932cc,
10986           darkred: 0x8b0000,
10987           darksalmon: 0xe9967a,
10988           darkseagreen: 0x8fbc8f,
10989           darkslateblue: 0x483d8b,
10990           darkslategray: 0x2f4f4f,
10991           darkslategrey: 0x2f4f4f,
10992           darkturquoise: 0x00ced1,
10993           darkviolet: 0x9400d3,
10994           deeppink: 0xff1493,
10995           deepskyblue: 0x00bfff,
10996           dimgray: 0x696969,
10997           dimgrey: 0x696969,
10998           dodgerblue: 0x1e90ff,
10999           firebrick: 0xb22222,
11000           floralwhite: 0xfffaf0,
11001           forestgreen: 0x228b22,
11002           fuchsia: 0xff00ff,
11003           gainsboro: 0xdcdcdc,
11004           ghostwhite: 0xf8f8ff,
11005           gold: 0xffd700,
11006           goldenrod: 0xdaa520,
11007           gray: 0x808080,
11008           green: 0x008000,
11009           greenyellow: 0xadff2f,
11010           grey: 0x808080,
11011           honeydew: 0xf0fff0,
11012           hotpink: 0xff69b4,
11013           indianred: 0xcd5c5c,
11014           indigo: 0x4b0082,
11015           ivory: 0xfffff0,
11016           khaki: 0xf0e68c,
11017           lavender: 0xe6e6fa,
11018           lavenderblush: 0xfff0f5,
11019           lawngreen: 0x7cfc00,
11020           lemonchiffon: 0xfffacd,
11021           lightblue: 0xadd8e6,
11022           lightcoral: 0xf08080,
11023           lightcyan: 0xe0ffff,
11024           lightgoldenrodyellow: 0xfafad2,
11025           lightgray: 0xd3d3d3,
11026           lightgreen: 0x90ee90,
11027           lightgrey: 0xd3d3d3,
11028           lightpink: 0xffb6c1,
11029           lightsalmon: 0xffa07a,
11030           lightseagreen: 0x20b2aa,
11031           lightskyblue: 0x87cefa,
11032           lightslategray: 0x778899,
11033           lightslategrey: 0x778899,
11034           lightsteelblue: 0xb0c4de,
11035           lightyellow: 0xffffe0,
11036           lime: 0x00ff00,
11037           limegreen: 0x32cd32,
11038           linen: 0xfaf0e6,
11039           magenta: 0xff00ff,
11040           maroon: 0x800000,
11041           mediumaquamarine: 0x66cdaa,
11042           mediumblue: 0x0000cd,
11043           mediumorchid: 0xba55d3,
11044           mediumpurple: 0x9370db,
11045           mediumseagreen: 0x3cb371,
11046           mediumslateblue: 0x7b68ee,
11047           mediumspringgreen: 0x00fa9a,
11048           mediumturquoise: 0x48d1cc,
11049           mediumvioletred: 0xc71585,
11050           midnightblue: 0x191970,
11051           mintcream: 0xf5fffa,
11052           mistyrose: 0xffe4e1,
11053           moccasin: 0xffe4b5,
11054           navajowhite: 0xffdead,
11055           navy: 0x000080,
11056           oldlace: 0xfdf5e6,
11057           olive: 0x808000,
11058           olivedrab: 0x6b8e23,
11059           orange: 0xffa500,
11060           orangered: 0xff4500,
11061           orchid: 0xda70d6,
11062           palegoldenrod: 0xeee8aa,
11063           palegreen: 0x98fb98,
11064           paleturquoise: 0xafeeee,
11065           palevioletred: 0xdb7093,
11066           papayawhip: 0xffefd5,
11067           peachpuff: 0xffdab9,
11068           peru: 0xcd853f,
11069           pink: 0xffc0cb,
11070           plum: 0xdda0dd,
11071           powderblue: 0xb0e0e6,
11072           purple: 0x800080,
11073           rebeccapurple: 0x663399,
11074           red: 0xff0000,
11075           rosybrown: 0xbc8f8f,
11076           royalblue: 0x4169e1,
11077           saddlebrown: 0x8b4513,
11078           salmon: 0xfa8072,
11079           sandybrown: 0xf4a460,
11080           seagreen: 0x2e8b57,
11081           seashell: 0xfff5ee,
11082           sienna: 0xa0522d,
11083           silver: 0xc0c0c0,
11084           skyblue: 0x87ceeb,
11085           slateblue: 0x6a5acd,
11086           slategray: 0x708090,
11087           slategrey: 0x708090,
11088           snow: 0xfffafa,
11089           springgreen: 0x00ff7f,
11090           steelblue: 0x4682b4,
11091           tan: 0xd2b48c,
11092           teal: 0x008080,
11093           thistle: 0xd8bfd8,
11094           tomato: 0xff6347,
11095           turquoise: 0x40e0d0,
11096           violet: 0xee82ee,
11097           wheat: 0xf5deb3,
11098           white: 0xffffff,
11099           whitesmoke: 0xf5f5f5,
11100           yellow: 0xffff00,
11101           yellowgreen: 0x9acd32
11102         };
11103
11104         define$1(Color, color, {
11105           copy: function(channels) {
11106             return Object.assign(new this.constructor, this, channels);
11107           },
11108           displayable: function() {
11109             return this.rgb().displayable();
11110           },
11111           hex: color_formatHex, // Deprecated! Use color.formatHex.
11112           formatHex: color_formatHex,
11113           formatHsl: color_formatHsl,
11114           formatRgb: color_formatRgb,
11115           toString: color_formatRgb
11116         });
11117
11118         function color_formatHex() {
11119           return this.rgb().formatHex();
11120         }
11121
11122         function color_formatHsl() {
11123           return hslConvert(this).formatHsl();
11124         }
11125
11126         function color_formatRgb() {
11127           return this.rgb().formatRgb();
11128         }
11129
11130         function color(format) {
11131           var m, l;
11132           format = (format + "").trim().toLowerCase();
11133           return (m = reHex.exec(format)) ? (l = m[1].length, m = parseInt(m[1], 16), l === 6 ? rgbn(m) // #ff0000
11134               : l === 3 ? new Rgb((m >> 8 & 0xf) | (m >> 4 & 0xf0), (m >> 4 & 0xf) | (m & 0xf0), ((m & 0xf) << 4) | (m & 0xf), 1) // #f00
11135               : l === 8 ? rgba(m >> 24 & 0xff, m >> 16 & 0xff, m >> 8 & 0xff, (m & 0xff) / 0xff) // #ff000000
11136               : 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
11137               : null) // invalid hex
11138               : (m = reRgbInteger.exec(format)) ? new Rgb(m[1], m[2], m[3], 1) // rgb(255, 0, 0)
11139               : (m = reRgbPercent.exec(format)) ? new Rgb(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, 1) // rgb(100%, 0%, 0%)
11140               : (m = reRgbaInteger.exec(format)) ? rgba(m[1], m[2], m[3], m[4]) // rgba(255, 0, 0, 1)
11141               : (m = reRgbaPercent.exec(format)) ? rgba(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, m[4]) // rgb(100%, 0%, 0%, 1)
11142               : (m = reHslPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, 1) // hsl(120, 50%, 50%)
11143               : (m = reHslaPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, m[4]) // hsla(120, 50%, 50%, 1)
11144               : named.hasOwnProperty(format) ? rgbn(named[format]) // eslint-disable-line no-prototype-builtins
11145               : format === "transparent" ? new Rgb(NaN, NaN, NaN, 0)
11146               : null;
11147         }
11148
11149         function rgbn(n) {
11150           return new Rgb(n >> 16 & 0xff, n >> 8 & 0xff, n & 0xff, 1);
11151         }
11152
11153         function rgba(r, g, b, a) {
11154           if (a <= 0) r = g = b = NaN;
11155           return new Rgb(r, g, b, a);
11156         }
11157
11158         function rgbConvert(o) {
11159           if (!(o instanceof Color)) o = color(o);
11160           if (!o) return new Rgb;
11161           o = o.rgb();
11162           return new Rgb(o.r, o.g, o.b, o.opacity);
11163         }
11164
11165         function rgb(r, g, b, opacity) {
11166           return arguments.length === 1 ? rgbConvert(r) : new Rgb(r, g, b, opacity == null ? 1 : opacity);
11167         }
11168
11169         function Rgb(r, g, b, opacity) {
11170           this.r = +r;
11171           this.g = +g;
11172           this.b = +b;
11173           this.opacity = +opacity;
11174         }
11175
11176         define$1(Rgb, rgb, extend(Color, {
11177           brighter: function(k) {
11178             k = k == null ? brighter : Math.pow(brighter, k);
11179             return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
11180           },
11181           darker: function(k) {
11182             k = k == null ? darker : Math.pow(darker, k);
11183             return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
11184           },
11185           rgb: function() {
11186             return this;
11187           },
11188           displayable: function() {
11189             return (-0.5 <= this.r && this.r < 255.5)
11190                 && (-0.5 <= this.g && this.g < 255.5)
11191                 && (-0.5 <= this.b && this.b < 255.5)
11192                 && (0 <= this.opacity && this.opacity <= 1);
11193           },
11194           hex: rgb_formatHex, // Deprecated! Use color.formatHex.
11195           formatHex: rgb_formatHex,
11196           formatRgb: rgb_formatRgb,
11197           toString: rgb_formatRgb
11198         }));
11199
11200         function rgb_formatHex() {
11201           return "#" + hex$1(this.r) + hex$1(this.g) + hex$1(this.b);
11202         }
11203
11204         function rgb_formatRgb() {
11205           var a = this.opacity; a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));
11206           return (a === 1 ? "rgb(" : "rgba(")
11207               + Math.max(0, Math.min(255, Math.round(this.r) || 0)) + ", "
11208               + Math.max(0, Math.min(255, Math.round(this.g) || 0)) + ", "
11209               + Math.max(0, Math.min(255, Math.round(this.b) || 0))
11210               + (a === 1 ? ")" : ", " + a + ")");
11211         }
11212
11213         function hex$1(value) {
11214           value = Math.max(0, Math.min(255, Math.round(value) || 0));
11215           return (value < 16 ? "0" : "") + value.toString(16);
11216         }
11217
11218         function hsla(h, s, l, a) {
11219           if (a <= 0) h = s = l = NaN;
11220           else if (l <= 0 || l >= 1) h = s = NaN;
11221           else if (s <= 0) h = NaN;
11222           return new Hsl(h, s, l, a);
11223         }
11224
11225         function hslConvert(o) {
11226           if (o instanceof Hsl) return new Hsl(o.h, o.s, o.l, o.opacity);
11227           if (!(o instanceof Color)) o = color(o);
11228           if (!o) return new Hsl;
11229           if (o instanceof Hsl) return o;
11230           o = o.rgb();
11231           var r = o.r / 255,
11232               g = o.g / 255,
11233               b = o.b / 255,
11234               min = Math.min(r, g, b),
11235               max = Math.max(r, g, b),
11236               h = NaN,
11237               s = max - min,
11238               l = (max + min) / 2;
11239           if (s) {
11240             if (r === max) h = (g - b) / s + (g < b) * 6;
11241             else if (g === max) h = (b - r) / s + 2;
11242             else h = (r - g) / s + 4;
11243             s /= l < 0.5 ? max + min : 2 - max - min;
11244             h *= 60;
11245           } else {
11246             s = l > 0 && l < 1 ? 0 : h;
11247           }
11248           return new Hsl(h, s, l, o.opacity);
11249         }
11250
11251         function hsl(h, s, l, opacity) {
11252           return arguments.length === 1 ? hslConvert(h) : new Hsl(h, s, l, opacity == null ? 1 : opacity);
11253         }
11254
11255         function Hsl(h, s, l, opacity) {
11256           this.h = +h;
11257           this.s = +s;
11258           this.l = +l;
11259           this.opacity = +opacity;
11260         }
11261
11262         define$1(Hsl, hsl, extend(Color, {
11263           brighter: function(k) {
11264             k = k == null ? brighter : Math.pow(brighter, k);
11265             return new Hsl(this.h, this.s, this.l * k, this.opacity);
11266           },
11267           darker: function(k) {
11268             k = k == null ? darker : Math.pow(darker, k);
11269             return new Hsl(this.h, this.s, this.l * k, this.opacity);
11270           },
11271           rgb: function() {
11272             var h = this.h % 360 + (this.h < 0) * 360,
11273                 s = isNaN(h) || isNaN(this.s) ? 0 : this.s,
11274                 l = this.l,
11275                 m2 = l + (l < 0.5 ? l : 1 - l) * s,
11276                 m1 = 2 * l - m2;
11277             return new Rgb(
11278               hsl2rgb(h >= 240 ? h - 240 : h + 120, m1, m2),
11279               hsl2rgb(h, m1, m2),
11280               hsl2rgb(h < 120 ? h + 240 : h - 120, m1, m2),
11281               this.opacity
11282             );
11283           },
11284           displayable: function() {
11285             return (0 <= this.s && this.s <= 1 || isNaN(this.s))
11286                 && (0 <= this.l && this.l <= 1)
11287                 && (0 <= this.opacity && this.opacity <= 1);
11288           },
11289           formatHsl: function() {
11290             var a = this.opacity; a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));
11291             return (a === 1 ? "hsl(" : "hsla(")
11292                 + (this.h || 0) + ", "
11293                 + (this.s || 0) * 100 + "%, "
11294                 + (this.l || 0) * 100 + "%"
11295                 + (a === 1 ? ")" : ", " + a + ")");
11296           }
11297         }));
11298
11299         /* From FvD 13.37, CSS Color Module Level 3 */
11300         function hsl2rgb(h, m1, m2) {
11301           return (h < 60 ? m1 + (m2 - m1) * h / 60
11302               : h < 180 ? m2
11303               : h < 240 ? m1 + (m2 - m1) * (240 - h) / 60
11304               : m1) * 255;
11305         }
11306
11307         function constant$2(x) {
11308           return function() {
11309             return x;
11310           };
11311         }
11312
11313         function linear(a, d) {
11314           return function(t) {
11315             return a + t * d;
11316           };
11317         }
11318
11319         function exponential(a, b, y) {
11320           return a = Math.pow(a, y), b = Math.pow(b, y) - a, y = 1 / y, function(t) {
11321             return Math.pow(a + t * b, y);
11322           };
11323         }
11324
11325         function gamma(y) {
11326           return (y = +y) === 1 ? nogamma : function(a, b) {
11327             return b - a ? exponential(a, b, y) : constant$2(isNaN(a) ? b : a);
11328           };
11329         }
11330
11331         function nogamma(a, b) {
11332           var d = b - a;
11333           return d ? linear(a, d) : constant$2(isNaN(a) ? b : a);
11334         }
11335
11336         var d3_interpolateRgb = (function rgbGamma(y) {
11337           var color = gamma(y);
11338
11339           function rgb$1(start, end) {
11340             var r = color((start = rgb(start)).r, (end = rgb(end)).r),
11341                 g = color(start.g, end.g),
11342                 b = color(start.b, end.b),
11343                 opacity = nogamma(start.opacity, end.opacity);
11344             return function(t) {
11345               start.r = r(t);
11346               start.g = g(t);
11347               start.b = b(t);
11348               start.opacity = opacity(t);
11349               return start + "";
11350             };
11351           }
11352
11353           rgb$1.gamma = rgbGamma;
11354
11355           return rgb$1;
11356         })(1);
11357
11358         function numberArray(a, b) {
11359           if (!b) b = [];
11360           var n = a ? Math.min(b.length, a.length) : 0,
11361               c = b.slice(),
11362               i;
11363           return function(t) {
11364             for (i = 0; i < n; ++i) c[i] = a[i] * (1 - t) + b[i] * t;
11365             return c;
11366           };
11367         }
11368
11369         function isNumberArray(x) {
11370           return ArrayBuffer.isView(x) && !(x instanceof DataView);
11371         }
11372
11373         function genericArray(a, b) {
11374           var nb = b ? b.length : 0,
11375               na = a ? Math.min(nb, a.length) : 0,
11376               x = new Array(na),
11377               c = new Array(nb),
11378               i;
11379
11380           for (i = 0; i < na; ++i) x[i] = interpolate(a[i], b[i]);
11381           for (; i < nb; ++i) c[i] = b[i];
11382
11383           return function(t) {
11384             for (i = 0; i < na; ++i) c[i] = x[i](t);
11385             return c;
11386           };
11387         }
11388
11389         function date(a, b) {
11390           var d = new Date;
11391           return a = +a, b = +b, function(t) {
11392             return d.setTime(a * (1 - t) + b * t), d;
11393           };
11394         }
11395
11396         function d3_interpolateNumber(a, b) {
11397           return a = +a, b = +b, function(t) {
11398             return a * (1 - t) + b * t;
11399           };
11400         }
11401
11402         function object(a, b) {
11403           var i = {},
11404               c = {},
11405               k;
11406
11407           if (a === null || typeof a !== "object") a = {};
11408           if (b === null || typeof b !== "object") b = {};
11409
11410           for (k in b) {
11411             if (k in a) {
11412               i[k] = interpolate(a[k], b[k]);
11413             } else {
11414               c[k] = b[k];
11415             }
11416           }
11417
11418           return function(t) {
11419             for (k in i) c[k] = i[k](t);
11420             return c;
11421           };
11422         }
11423
11424         var reA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,
11425             reB = new RegExp(reA.source, "g");
11426
11427         function zero(b) {
11428           return function() {
11429             return b;
11430           };
11431         }
11432
11433         function one(b) {
11434           return function(t) {
11435             return b(t) + "";
11436           };
11437         }
11438
11439         function interpolateString(a, b) {
11440           var bi = reA.lastIndex = reB.lastIndex = 0, // scan index for next number in b
11441               am, // current match in a
11442               bm, // current match in b
11443               bs, // string preceding current number in b, if any
11444               i = -1, // index in s
11445               s = [], // string constants and placeholders
11446               q = []; // number interpolators
11447
11448           // Coerce inputs to strings.
11449           a = a + "", b = b + "";
11450
11451           // Interpolate pairs of numbers in a & b.
11452           while ((am = reA.exec(a))
11453               && (bm = reB.exec(b))) {
11454             if ((bs = bm.index) > bi) { // a string precedes the next number in b
11455               bs = b.slice(bi, bs);
11456               if (s[i]) s[i] += bs; // coalesce with previous string
11457               else s[++i] = bs;
11458             }
11459             if ((am = am[0]) === (bm = bm[0])) { // numbers in a & b match
11460               if (s[i]) s[i] += bm; // coalesce with previous string
11461               else s[++i] = bm;
11462             } else { // interpolate non-matching numbers
11463               s[++i] = null;
11464               q.push({i: i, x: d3_interpolateNumber(am, bm)});
11465             }
11466             bi = reB.lastIndex;
11467           }
11468
11469           // Add remains of b.
11470           if (bi < b.length) {
11471             bs = b.slice(bi);
11472             if (s[i]) s[i] += bs; // coalesce with previous string
11473             else s[++i] = bs;
11474           }
11475
11476           // Special optimization for only a single match.
11477           // Otherwise, interpolate each of the numbers and rejoin the string.
11478           return s.length < 2 ? (q[0]
11479               ? one(q[0].x)
11480               : zero(b))
11481               : (b = q.length, function(t) {
11482                   for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t);
11483                   return s.join("");
11484                 });
11485         }
11486
11487         function interpolate(a, b) {
11488           var t = typeof b, c;
11489           return b == null || t === "boolean" ? constant$2(b)
11490               : (t === "number" ? d3_interpolateNumber
11491               : t === "string" ? ((c = color(b)) ? (b = c, d3_interpolateRgb) : interpolateString)
11492               : b instanceof color ? d3_interpolateRgb
11493               : b instanceof Date ? date
11494               : isNumberArray(b) ? numberArray
11495               : Array.isArray(b) ? genericArray
11496               : typeof b.valueOf !== "function" && typeof b.toString !== "function" || isNaN(b) ? object
11497               : d3_interpolateNumber)(a, b);
11498         }
11499
11500         function interpolateRound(a, b) {
11501           return a = +a, b = +b, function(t) {
11502             return Math.round(a * (1 - t) + b * t);
11503           };
11504         }
11505
11506         var degrees$1 = 180 / Math.PI;
11507
11508         var identity$1 = {
11509           translateX: 0,
11510           translateY: 0,
11511           rotate: 0,
11512           skewX: 0,
11513           scaleX: 1,
11514           scaleY: 1
11515         };
11516
11517         function decompose(a, b, c, d, e, f) {
11518           var scaleX, scaleY, skewX;
11519           if (scaleX = Math.sqrt(a * a + b * b)) a /= scaleX, b /= scaleX;
11520           if (skewX = a * c + b * d) c -= a * skewX, d -= b * skewX;
11521           if (scaleY = Math.sqrt(c * c + d * d)) c /= scaleY, d /= scaleY, skewX /= scaleY;
11522           if (a * d < b * c) a = -a, b = -b, skewX = -skewX, scaleX = -scaleX;
11523           return {
11524             translateX: e,
11525             translateY: f,
11526             rotate: Math.atan2(b, a) * degrees$1,
11527             skewX: Math.atan(skewX) * degrees$1,
11528             scaleX: scaleX,
11529             scaleY: scaleY
11530           };
11531         }
11532
11533         var cssNode,
11534             cssRoot,
11535             cssView,
11536             svgNode;
11537
11538         function parseCss(value) {
11539           if (value === "none") return identity$1;
11540           if (!cssNode) cssNode = document.createElement("DIV"), cssRoot = document.documentElement, cssView = document.defaultView;
11541           cssNode.style.transform = value;
11542           value = cssView.getComputedStyle(cssRoot.appendChild(cssNode), null).getPropertyValue("transform");
11543           cssRoot.removeChild(cssNode);
11544           value = value.slice(7, -1).split(",");
11545           return decompose(+value[0], +value[1], +value[2], +value[3], +value[4], +value[5]);
11546         }
11547
11548         function parseSvg(value) {
11549           if (value == null) return identity$1;
11550           if (!svgNode) svgNode = document.createElementNS("http://www.w3.org/2000/svg", "g");
11551           svgNode.setAttribute("transform", value);
11552           if (!(value = svgNode.transform.baseVal.consolidate())) return identity$1;
11553           value = value.matrix;
11554           return decompose(value.a, value.b, value.c, value.d, value.e, value.f);
11555         }
11556
11557         function interpolateTransform(parse, pxComma, pxParen, degParen) {
11558
11559           function pop(s) {
11560             return s.length ? s.pop() + " " : "";
11561           }
11562
11563           function translate(xa, ya, xb, yb, s, q) {
11564             if (xa !== xb || ya !== yb) {
11565               var i = s.push("translate(", null, pxComma, null, pxParen);
11566               q.push({i: i - 4, x: d3_interpolateNumber(xa, xb)}, {i: i - 2, x: d3_interpolateNumber(ya, yb)});
11567             } else if (xb || yb) {
11568               s.push("translate(" + xb + pxComma + yb + pxParen);
11569             }
11570           }
11571
11572           function rotate(a, b, s, q) {
11573             if (a !== b) {
11574               if (a - b > 180) b += 360; else if (b - a > 180) a += 360; // shortest path
11575               q.push({i: s.push(pop(s) + "rotate(", null, degParen) - 2, x: d3_interpolateNumber(a, b)});
11576             } else if (b) {
11577               s.push(pop(s) + "rotate(" + b + degParen);
11578             }
11579           }
11580
11581           function skewX(a, b, s, q) {
11582             if (a !== b) {
11583               q.push({i: s.push(pop(s) + "skewX(", null, degParen) - 2, x: d3_interpolateNumber(a, b)});
11584             } else if (b) {
11585               s.push(pop(s) + "skewX(" + b + degParen);
11586             }
11587           }
11588
11589           function scale(xa, ya, xb, yb, s, q) {
11590             if (xa !== xb || ya !== yb) {
11591               var i = s.push(pop(s) + "scale(", null, ",", null, ")");
11592               q.push({i: i - 4, x: d3_interpolateNumber(xa, xb)}, {i: i - 2, x: d3_interpolateNumber(ya, yb)});
11593             } else if (xb !== 1 || yb !== 1) {
11594               s.push(pop(s) + "scale(" + xb + "," + yb + ")");
11595             }
11596           }
11597
11598           return function(a, b) {
11599             var s = [], // string constants and placeholders
11600                 q = []; // number interpolators
11601             a = parse(a), b = parse(b);
11602             translate(a.translateX, a.translateY, b.translateX, b.translateY, s, q);
11603             rotate(a.rotate, b.rotate, s, q);
11604             skewX(a.skewX, b.skewX, s, q);
11605             scale(a.scaleX, a.scaleY, b.scaleX, b.scaleY, s, q);
11606             a = b = null; // gc
11607             return function(t) {
11608               var i = -1, n = q.length, o;
11609               while (++i < n) s[(o = q[i]).i] = o.x(t);
11610               return s.join("");
11611             };
11612           };
11613         }
11614
11615         var interpolateTransformCss = interpolateTransform(parseCss, "px, ", "px)", "deg)");
11616         var interpolateTransformSvg = interpolateTransform(parseSvg, ", ", ")", ")");
11617
11618         var rho = Math.SQRT2,
11619             rho2 = 2,
11620             rho4 = 4,
11621             epsilon2$1 = 1e-12;
11622
11623         function cosh(x) {
11624           return ((x = Math.exp(x)) + 1 / x) / 2;
11625         }
11626
11627         function sinh(x) {
11628           return ((x = Math.exp(x)) - 1 / x) / 2;
11629         }
11630
11631         function tanh(x) {
11632           return ((x = Math.exp(2 * x)) - 1) / (x + 1);
11633         }
11634
11635         // p0 = [ux0, uy0, w0]
11636         // p1 = [ux1, uy1, w1]
11637         function interpolateZoom(p0, p1) {
11638           var ux0 = p0[0], uy0 = p0[1], w0 = p0[2],
11639               ux1 = p1[0], uy1 = p1[1], w1 = p1[2],
11640               dx = ux1 - ux0,
11641               dy = uy1 - uy0,
11642               d2 = dx * dx + dy * dy,
11643               i,
11644               S;
11645
11646           // Special case for u0 ≅ u1.
11647           if (d2 < epsilon2$1) {
11648             S = Math.log(w1 / w0) / rho;
11649             i = function(t) {
11650               return [
11651                 ux0 + t * dx,
11652                 uy0 + t * dy,
11653                 w0 * Math.exp(rho * t * S)
11654               ];
11655             };
11656           }
11657
11658           // General case.
11659           else {
11660             var d1 = Math.sqrt(d2),
11661                 b0 = (w1 * w1 - w0 * w0 + rho4 * d2) / (2 * w0 * rho2 * d1),
11662                 b1 = (w1 * w1 - w0 * w0 - rho4 * d2) / (2 * w1 * rho2 * d1),
11663                 r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0),
11664                 r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1);
11665             S = (r1 - r0) / rho;
11666             i = function(t) {
11667               var s = t * S,
11668                   coshr0 = cosh(r0),
11669                   u = w0 / (rho2 * d1) * (coshr0 * tanh(rho * s + r0) - sinh(r0));
11670               return [
11671                 ux0 + u * dx,
11672                 uy0 + u * dy,
11673                 w0 * coshr0 / cosh(rho * s + r0)
11674               ];
11675             };
11676           }
11677
11678           i.duration = S * 1000;
11679
11680           return i;
11681         }
11682
11683         function d3_quantize(interpolator, n) {
11684           var samples = new Array(n);
11685           for (var i = 0; i < n; ++i) samples[i] = interpolator(i / (n - 1));
11686           return samples;
11687         }
11688
11689         var frame = 0, // is an animation frame pending?
11690             timeout = 0, // is a timeout pending?
11691             interval = 0, // are any timers active?
11692             pokeDelay = 1000, // how frequently we check for clock skew
11693             taskHead,
11694             taskTail,
11695             clockLast = 0,
11696             clockNow = 0,
11697             clockSkew = 0,
11698             clock = typeof performance === "object" && performance.now ? performance : Date,
11699             setFrame = typeof window === "object" && window.requestAnimationFrame ? window.requestAnimationFrame.bind(window) : function(f) { setTimeout(f, 17); };
11700
11701         function now() {
11702           return clockNow || (setFrame(clearNow), clockNow = clock.now() + clockSkew);
11703         }
11704
11705         function clearNow() {
11706           clockNow = 0;
11707         }
11708
11709         function Timer() {
11710           this._call =
11711           this._time =
11712           this._next = null;
11713         }
11714
11715         Timer.prototype = timer.prototype = {
11716           constructor: Timer,
11717           restart: function(callback, delay, time) {
11718             if (typeof callback !== "function") throw new TypeError("callback is not a function");
11719             time = (time == null ? now() : +time) + (delay == null ? 0 : +delay);
11720             if (!this._next && taskTail !== this) {
11721               if (taskTail) taskTail._next = this;
11722               else taskHead = this;
11723               taskTail = this;
11724             }
11725             this._call = callback;
11726             this._time = time;
11727             sleep();
11728           },
11729           stop: function() {
11730             if (this._call) {
11731               this._call = null;
11732               this._time = Infinity;
11733               sleep();
11734             }
11735           }
11736         };
11737
11738         function timer(callback, delay, time) {
11739           var t = new Timer;
11740           t.restart(callback, delay, time);
11741           return t;
11742         }
11743
11744         function timerFlush() {
11745           now(); // Get the current time, if not already set.
11746           ++frame; // Pretend we’ve set an alarm, if we haven’t already.
11747           var t = taskHead, e;
11748           while (t) {
11749             if ((e = clockNow - t._time) >= 0) t._call.call(null, e);
11750             t = t._next;
11751           }
11752           --frame;
11753         }
11754
11755         function wake() {
11756           clockNow = (clockLast = clock.now()) + clockSkew;
11757           frame = timeout = 0;
11758           try {
11759             timerFlush();
11760           } finally {
11761             frame = 0;
11762             nap();
11763             clockNow = 0;
11764           }
11765         }
11766
11767         function poke() {
11768           var now = clock.now(), delay = now - clockLast;
11769           if (delay > pokeDelay) clockSkew -= delay, clockLast = now;
11770         }
11771
11772         function nap() {
11773           var t0, t1 = taskHead, t2, time = Infinity;
11774           while (t1) {
11775             if (t1._call) {
11776               if (time > t1._time) time = t1._time;
11777               t0 = t1, t1 = t1._next;
11778             } else {
11779               t2 = t1._next, t1._next = null;
11780               t1 = t0 ? t0._next = t2 : taskHead = t2;
11781             }
11782           }
11783           taskTail = t0;
11784           sleep(time);
11785         }
11786
11787         function sleep(time) {
11788           if (frame) return; // Soonest alarm already set, or will be.
11789           if (timeout) timeout = clearTimeout(timeout);
11790           var delay = time - clockNow; // Strictly less than if we recomputed clockNow.
11791           if (delay > 24) {
11792             if (time < Infinity) timeout = setTimeout(wake, time - clock.now() - clockSkew);
11793             if (interval) interval = clearInterval(interval);
11794           } else {
11795             if (!interval) clockLast = clock.now(), interval = setInterval(poke, pokeDelay);
11796             frame = 1, setFrame(wake);
11797           }
11798         }
11799
11800         function d3_timeout(callback, delay, time) {
11801           var t = new Timer;
11802           delay = delay == null ? 0 : +delay;
11803           t.restart(function(elapsed) {
11804             t.stop();
11805             callback(elapsed + delay);
11806           }, delay, time);
11807           return t;
11808         }
11809
11810         var emptyOn = dispatch("start", "end", "cancel", "interrupt");
11811         var emptyTween = [];
11812
11813         var CREATED = 0;
11814         var SCHEDULED = 1;
11815         var STARTING = 2;
11816         var STARTED = 3;
11817         var RUNNING = 4;
11818         var ENDING = 5;
11819         var ENDED = 6;
11820
11821         function schedule(node, name, id, index, group, timing) {
11822           var schedules = node.__transition;
11823           if (!schedules) node.__transition = {};
11824           else if (id in schedules) return;
11825           create$7(node, id, {
11826             name: name,
11827             index: index, // For context during callback.
11828             group: group, // For context during callback.
11829             on: emptyOn,
11830             tween: emptyTween,
11831             time: timing.time,
11832             delay: timing.delay,
11833             duration: timing.duration,
11834             ease: timing.ease,
11835             timer: null,
11836             state: CREATED
11837           });
11838         }
11839
11840         function init(node, id) {
11841           var schedule = get$2(node, id);
11842           if (schedule.state > CREATED) throw new Error("too late; already scheduled");
11843           return schedule;
11844         }
11845
11846         function set$1(node, id) {
11847           var schedule = get$2(node, id);
11848           if (schedule.state > STARTED) throw new Error("too late; already running");
11849           return schedule;
11850         }
11851
11852         function get$2(node, id) {
11853           var schedule = node.__transition;
11854           if (!schedule || !(schedule = schedule[id])) throw new Error("transition not found");
11855           return schedule;
11856         }
11857
11858         function create$7(node, id, self) {
11859           var schedules = node.__transition,
11860               tween;
11861
11862           // Initialize the self timer when the transition is created.
11863           // Note the actual delay is not known until the first callback!
11864           schedules[id] = self;
11865           self.timer = timer(schedule, 0, self.time);
11866
11867           function schedule(elapsed) {
11868             self.state = SCHEDULED;
11869             self.timer.restart(start, self.delay, self.time);
11870
11871             // If the elapsed delay is less than our first sleep, start immediately.
11872             if (self.delay <= elapsed) start(elapsed - self.delay);
11873           }
11874
11875           function start(elapsed) {
11876             var i, j, n, o;
11877
11878             // If the state is not SCHEDULED, then we previously errored on start.
11879             if (self.state !== SCHEDULED) return stop();
11880
11881             for (i in schedules) {
11882               o = schedules[i];
11883               if (o.name !== self.name) continue;
11884
11885               // While this element already has a starting transition during this frame,
11886               // defer starting an interrupting transition until that transition has a
11887               // chance to tick (and possibly end); see d3/d3-transition#54!
11888               if (o.state === STARTED) return d3_timeout(start);
11889
11890               // Interrupt the active transition, if any.
11891               if (o.state === RUNNING) {
11892                 o.state = ENDED;
11893                 o.timer.stop();
11894                 o.on.call("interrupt", node, node.__data__, o.index, o.group);
11895                 delete schedules[i];
11896               }
11897
11898               // Cancel any pre-empted transitions.
11899               else if (+i < id) {
11900                 o.state = ENDED;
11901                 o.timer.stop();
11902                 o.on.call("cancel", node, node.__data__, o.index, o.group);
11903                 delete schedules[i];
11904               }
11905             }
11906
11907             // Defer the first tick to end of the current frame; see d3/d3#1576.
11908             // Note the transition may be canceled after start and before the first tick!
11909             // Note this must be scheduled before the start event; see d3/d3-transition#16!
11910             // Assuming this is successful, subsequent callbacks go straight to tick.
11911             d3_timeout(function() {
11912               if (self.state === STARTED) {
11913                 self.state = RUNNING;
11914                 self.timer.restart(tick, self.delay, self.time);
11915                 tick(elapsed);
11916               }
11917             });
11918
11919             // Dispatch the start event.
11920             // Note this must be done before the tween are initialized.
11921             self.state = STARTING;
11922             self.on.call("start", node, node.__data__, self.index, self.group);
11923             if (self.state !== STARTING) return; // interrupted
11924             self.state = STARTED;
11925
11926             // Initialize the tween, deleting null tween.
11927             tween = new Array(n = self.tween.length);
11928             for (i = 0, j = -1; i < n; ++i) {
11929               if (o = self.tween[i].value.call(node, node.__data__, self.index, self.group)) {
11930                 tween[++j] = o;
11931               }
11932             }
11933             tween.length = j + 1;
11934           }
11935
11936           function tick(elapsed) {
11937             var t = elapsed < self.duration ? self.ease.call(null, elapsed / self.duration) : (self.timer.restart(stop), self.state = ENDING, 1),
11938                 i = -1,
11939                 n = tween.length;
11940
11941             while (++i < n) {
11942               tween[i].call(node, t);
11943             }
11944
11945             // Dispatch the end event.
11946             if (self.state === ENDING) {
11947               self.on.call("end", node, node.__data__, self.index, self.group);
11948               stop();
11949             }
11950           }
11951
11952           function stop() {
11953             self.state = ENDED;
11954             self.timer.stop();
11955             delete schedules[id];
11956             for (var i in schedules) return; // eslint-disable-line no-unused-vars
11957             delete node.__transition;
11958           }
11959         }
11960
11961         function interrupt(node, name) {
11962           var schedules = node.__transition,
11963               schedule,
11964               active,
11965               empty = true,
11966               i;
11967
11968           if (!schedules) return;
11969
11970           name = name == null ? null : name + "";
11971
11972           for (i in schedules) {
11973             if ((schedule = schedules[i]).name !== name) { empty = false; continue; }
11974             active = schedule.state > STARTING && schedule.state < ENDING;
11975             schedule.state = ENDED;
11976             schedule.timer.stop();
11977             schedule.on.call(active ? "interrupt" : "cancel", node, node.__data__, schedule.index, schedule.group);
11978             delete schedules[i];
11979           }
11980
11981           if (empty) delete node.__transition;
11982         }
11983
11984         function selection_interrupt(name) {
11985           return this.each(function() {
11986             interrupt(this, name);
11987           });
11988         }
11989
11990         function tweenRemove(id, name) {
11991           var tween0, tween1;
11992           return function() {
11993             var schedule = set$1(this, id),
11994                 tween = schedule.tween;
11995
11996             // If this node shared tween with the previous node,
11997             // just assign the updated shared tween and we’re done!
11998             // Otherwise, copy-on-write.
11999             if (tween !== tween0) {
12000               tween1 = tween0 = tween;
12001               for (var i = 0, n = tween1.length; i < n; ++i) {
12002                 if (tween1[i].name === name) {
12003                   tween1 = tween1.slice();
12004                   tween1.splice(i, 1);
12005                   break;
12006                 }
12007               }
12008             }
12009
12010             schedule.tween = tween1;
12011           };
12012         }
12013
12014         function tweenFunction(id, name, value) {
12015           var tween0, tween1;
12016           if (typeof value !== "function") throw new Error;
12017           return function() {
12018             var schedule = set$1(this, id),
12019                 tween = schedule.tween;
12020
12021             // If this node shared tween with the previous node,
12022             // just assign the updated shared tween and we’re done!
12023             // Otherwise, copy-on-write.
12024             if (tween !== tween0) {
12025               tween1 = (tween0 = tween).slice();
12026               for (var t = {name: name, value: value}, i = 0, n = tween1.length; i < n; ++i) {
12027                 if (tween1[i].name === name) {
12028                   tween1[i] = t;
12029                   break;
12030                 }
12031               }
12032               if (i === n) tween1.push(t);
12033             }
12034
12035             schedule.tween = tween1;
12036           };
12037         }
12038
12039         function transition_tween(name, value) {
12040           var id = this._id;
12041
12042           name += "";
12043
12044           if (arguments.length < 2) {
12045             var tween = get$2(this.node(), id).tween;
12046             for (var i = 0, n = tween.length, t; i < n; ++i) {
12047               if ((t = tween[i]).name === name) {
12048                 return t.value;
12049               }
12050             }
12051             return null;
12052           }
12053
12054           return this.each((value == null ? tweenRemove : tweenFunction)(id, name, value));
12055         }
12056
12057         function tweenValue(transition, name, value) {
12058           var id = transition._id;
12059
12060           transition.each(function() {
12061             var schedule = set$1(this, id);
12062             (schedule.value || (schedule.value = {}))[name] = value.apply(this, arguments);
12063           });
12064
12065           return function(node) {
12066             return get$2(node, id).value[name];
12067           };
12068         }
12069
12070         function interpolate$1(a, b) {
12071           var c;
12072           return (typeof b === "number" ? d3_interpolateNumber
12073               : b instanceof color ? d3_interpolateRgb
12074               : (c = color(b)) ? (b = c, d3_interpolateRgb)
12075               : interpolateString)(a, b);
12076         }
12077
12078         function attrRemove$1(name) {
12079           return function() {
12080             this.removeAttribute(name);
12081           };
12082         }
12083
12084         function attrRemoveNS$1(fullname) {
12085           return function() {
12086             this.removeAttributeNS(fullname.space, fullname.local);
12087           };
12088         }
12089
12090         function attrConstant$1(name, interpolate, value1) {
12091           var string00,
12092               string1 = value1 + "",
12093               interpolate0;
12094           return function() {
12095             var string0 = this.getAttribute(name);
12096             return string0 === string1 ? null
12097                 : string0 === string00 ? interpolate0
12098                 : interpolate0 = interpolate(string00 = string0, value1);
12099           };
12100         }
12101
12102         function attrConstantNS$1(fullname, interpolate, value1) {
12103           var string00,
12104               string1 = value1 + "",
12105               interpolate0;
12106           return function() {
12107             var string0 = this.getAttributeNS(fullname.space, fullname.local);
12108             return string0 === string1 ? null
12109                 : string0 === string00 ? interpolate0
12110                 : interpolate0 = interpolate(string00 = string0, value1);
12111           };
12112         }
12113
12114         function attrFunction$1(name, interpolate, value) {
12115           var string00,
12116               string10,
12117               interpolate0;
12118           return function() {
12119             var string0, value1 = value(this), string1;
12120             if (value1 == null) return void this.removeAttribute(name);
12121             string0 = this.getAttribute(name);
12122             string1 = value1 + "";
12123             return string0 === string1 ? null
12124                 : string0 === string00 && string1 === string10 ? interpolate0
12125                 : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1));
12126           };
12127         }
12128
12129         function attrFunctionNS$1(fullname, interpolate, value) {
12130           var string00,
12131               string10,
12132               interpolate0;
12133           return function() {
12134             var string0, value1 = value(this), string1;
12135             if (value1 == null) return void this.removeAttributeNS(fullname.space, fullname.local);
12136             string0 = this.getAttributeNS(fullname.space, fullname.local);
12137             string1 = value1 + "";
12138             return string0 === string1 ? null
12139                 : string0 === string00 && string1 === string10 ? interpolate0
12140                 : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1));
12141           };
12142         }
12143
12144         function transition_attr(name, value) {
12145           var fullname = namespace(name), i = fullname === "transform" ? interpolateTransformSvg : interpolate$1;
12146           return this.attrTween(name, typeof value === "function"
12147               ? (fullname.local ? attrFunctionNS$1 : attrFunction$1)(fullname, i, tweenValue(this, "attr." + name, value))
12148               : value == null ? (fullname.local ? attrRemoveNS$1 : attrRemove$1)(fullname)
12149               : (fullname.local ? attrConstantNS$1 : attrConstant$1)(fullname, i, value));
12150         }
12151
12152         function attrInterpolate(name, i) {
12153           return function(t) {
12154             this.setAttribute(name, i.call(this, t));
12155           };
12156         }
12157
12158         function attrInterpolateNS(fullname, i) {
12159           return function(t) {
12160             this.setAttributeNS(fullname.space, fullname.local, i.call(this, t));
12161           };
12162         }
12163
12164         function attrTweenNS(fullname, value) {
12165           var t0, i0;
12166           function tween() {
12167             var i = value.apply(this, arguments);
12168             if (i !== i0) t0 = (i0 = i) && attrInterpolateNS(fullname, i);
12169             return t0;
12170           }
12171           tween._value = value;
12172           return tween;
12173         }
12174
12175         function attrTween(name, value) {
12176           var t0, i0;
12177           function tween() {
12178             var i = value.apply(this, arguments);
12179             if (i !== i0) t0 = (i0 = i) && attrInterpolate(name, i);
12180             return t0;
12181           }
12182           tween._value = value;
12183           return tween;
12184         }
12185
12186         function transition_attrTween(name, value) {
12187           var key = "attr." + name;
12188           if (arguments.length < 2) return (key = this.tween(key)) && key._value;
12189           if (value == null) return this.tween(key, null);
12190           if (typeof value !== "function") throw new Error;
12191           var fullname = namespace(name);
12192           return this.tween(key, (fullname.local ? attrTweenNS : attrTween)(fullname, value));
12193         }
12194
12195         function delayFunction(id, value) {
12196           return function() {
12197             init(this, id).delay = +value.apply(this, arguments);
12198           };
12199         }
12200
12201         function delayConstant(id, value) {
12202           return value = +value, function() {
12203             init(this, id).delay = value;
12204           };
12205         }
12206
12207         function transition_delay(value) {
12208           var id = this._id;
12209
12210           return arguments.length
12211               ? this.each((typeof value === "function"
12212                   ? delayFunction
12213                   : delayConstant)(id, value))
12214               : get$2(this.node(), id).delay;
12215         }
12216
12217         function durationFunction(id, value) {
12218           return function() {
12219             set$1(this, id).duration = +value.apply(this, arguments);
12220           };
12221         }
12222
12223         function durationConstant(id, value) {
12224           return value = +value, function() {
12225             set$1(this, id).duration = value;
12226           };
12227         }
12228
12229         function transition_duration(value) {
12230           var id = this._id;
12231
12232           return arguments.length
12233               ? this.each((typeof value === "function"
12234                   ? durationFunction
12235                   : durationConstant)(id, value))
12236               : get$2(this.node(), id).duration;
12237         }
12238
12239         function easeConstant(id, value) {
12240           if (typeof value !== "function") throw new Error;
12241           return function() {
12242             set$1(this, id).ease = value;
12243           };
12244         }
12245
12246         function transition_ease(value) {
12247           var id = this._id;
12248
12249           return arguments.length
12250               ? this.each(easeConstant(id, value))
12251               : get$2(this.node(), id).ease;
12252         }
12253
12254         function transition_filter(match) {
12255           if (typeof match !== "function") match = matcher(match);
12256
12257           for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
12258             for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {
12259               if ((node = group[i]) && match.call(node, node.__data__, i, group)) {
12260                 subgroup.push(node);
12261               }
12262             }
12263           }
12264
12265           return new Transition(subgroups, this._parents, this._name, this._id);
12266         }
12267
12268         function transition_merge(transition) {
12269           if (transition._id !== this._id) throw new Error;
12270
12271           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) {
12272             for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {
12273               if (node = group0[i] || group1[i]) {
12274                 merge[i] = node;
12275               }
12276             }
12277           }
12278
12279           for (; j < m0; ++j) {
12280             merges[j] = groups0[j];
12281           }
12282
12283           return new Transition(merges, this._parents, this._name, this._id);
12284         }
12285
12286         function start(name) {
12287           return (name + "").trim().split(/^|\s+/).every(function(t) {
12288             var i = t.indexOf(".");
12289             if (i >= 0) t = t.slice(0, i);
12290             return !t || t === "start";
12291           });
12292         }
12293
12294         function onFunction(id, name, listener) {
12295           var on0, on1, sit = start(name) ? init : set$1;
12296           return function() {
12297             var schedule = sit(this, id),
12298                 on = schedule.on;
12299
12300             // If this node shared a dispatch with the previous node,
12301             // just assign the updated shared dispatch and we’re done!
12302             // Otherwise, copy-on-write.
12303             if (on !== on0) (on1 = (on0 = on).copy()).on(name, listener);
12304
12305             schedule.on = on1;
12306           };
12307         }
12308
12309         function transition_on(name, listener) {
12310           var id = this._id;
12311
12312           return arguments.length < 2
12313               ? get$2(this.node(), id).on.on(name)
12314               : this.each(onFunction(id, name, listener));
12315         }
12316
12317         function removeFunction(id) {
12318           return function() {
12319             var parent = this.parentNode;
12320             for (var i in this.__transition) if (+i !== id) return;
12321             if (parent) parent.removeChild(this);
12322           };
12323         }
12324
12325         function transition_remove() {
12326           return this.on("end.remove", removeFunction(this._id));
12327         }
12328
12329         function transition_select(select) {
12330           var name = this._name,
12331               id = this._id;
12332
12333           if (typeof select !== "function") select = selector(select);
12334
12335           for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
12336             for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {
12337               if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {
12338                 if ("__data__" in node) subnode.__data__ = node.__data__;
12339                 subgroup[i] = subnode;
12340                 schedule(subgroup[i], name, id, i, subgroup, get$2(node, id));
12341               }
12342             }
12343           }
12344
12345           return new Transition(subgroups, this._parents, name, id);
12346         }
12347
12348         function transition_selectAll(select) {
12349           var name = this._name,
12350               id = this._id;
12351
12352           if (typeof select !== "function") select = selectorAll(select);
12353
12354           for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {
12355             for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
12356               if (node = group[i]) {
12357                 for (var children = select.call(node, node.__data__, i, group), child, inherit = get$2(node, id), k = 0, l = children.length; k < l; ++k) {
12358                   if (child = children[k]) {
12359                     schedule(child, name, id, k, children, inherit);
12360                   }
12361                 }
12362                 subgroups.push(children);
12363                 parents.push(node);
12364               }
12365             }
12366           }
12367
12368           return new Transition(subgroups, parents, name, id);
12369         }
12370
12371         var Selection$1 = selection.prototype.constructor;
12372
12373         function transition_selection() {
12374           return new Selection$1(this._groups, this._parents);
12375         }
12376
12377         function styleNull(name, interpolate) {
12378           var string00,
12379               string10,
12380               interpolate0;
12381           return function() {
12382             var string0 = styleValue(this, name),
12383                 string1 = (this.style.removeProperty(name), styleValue(this, name));
12384             return string0 === string1 ? null
12385                 : string0 === string00 && string1 === string10 ? interpolate0
12386                 : interpolate0 = interpolate(string00 = string0, string10 = string1);
12387           };
12388         }
12389
12390         function styleRemove$1(name) {
12391           return function() {
12392             this.style.removeProperty(name);
12393           };
12394         }
12395
12396         function styleConstant$1(name, interpolate, value1) {
12397           var string00,
12398               string1 = value1 + "",
12399               interpolate0;
12400           return function() {
12401             var string0 = styleValue(this, name);
12402             return string0 === string1 ? null
12403                 : string0 === string00 ? interpolate0
12404                 : interpolate0 = interpolate(string00 = string0, value1);
12405           };
12406         }
12407
12408         function styleFunction$1(name, interpolate, value) {
12409           var string00,
12410               string10,
12411               interpolate0;
12412           return function() {
12413             var string0 = styleValue(this, name),
12414                 value1 = value(this),
12415                 string1 = value1 + "";
12416             if (value1 == null) string1 = value1 = (this.style.removeProperty(name), styleValue(this, name));
12417             return string0 === string1 ? null
12418                 : string0 === string00 && string1 === string10 ? interpolate0
12419                 : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1));
12420           };
12421         }
12422
12423         function styleMaybeRemove(id, name) {
12424           var on0, on1, listener0, key = "style." + name, event = "end." + key, remove;
12425           return function() {
12426             var schedule = set$1(this, id),
12427                 on = schedule.on,
12428                 listener = schedule.value[key] == null ? remove || (remove = styleRemove$1(name)) : undefined;
12429
12430             // If this node shared a dispatch with the previous node,
12431             // just assign the updated shared dispatch and we’re done!
12432             // Otherwise, copy-on-write.
12433             if (on !== on0 || listener0 !== listener) (on1 = (on0 = on).copy()).on(event, listener0 = listener);
12434
12435             schedule.on = on1;
12436           };
12437         }
12438
12439         function transition_style(name, value, priority) {
12440           var i = (name += "") === "transform" ? interpolateTransformCss : interpolate$1;
12441           return value == null ? this
12442               .styleTween(name, styleNull(name, i))
12443               .on("end.style." + name, styleRemove$1(name))
12444             : typeof value === "function" ? this
12445               .styleTween(name, styleFunction$1(name, i, tweenValue(this, "style." + name, value)))
12446               .each(styleMaybeRemove(this._id, name))
12447             : this
12448               .styleTween(name, styleConstant$1(name, i, value), priority)
12449               .on("end.style." + name, null);
12450         }
12451
12452         function styleInterpolate(name, i, priority) {
12453           return function(t) {
12454             this.style.setProperty(name, i.call(this, t), priority);
12455           };
12456         }
12457
12458         function styleTween(name, value, priority) {
12459           var t, i0;
12460           function tween() {
12461             var i = value.apply(this, arguments);
12462             if (i !== i0) t = (i0 = i) && styleInterpolate(name, i, priority);
12463             return t;
12464           }
12465           tween._value = value;
12466           return tween;
12467         }
12468
12469         function transition_styleTween(name, value, priority) {
12470           var key = "style." + (name += "");
12471           if (arguments.length < 2) return (key = this.tween(key)) && key._value;
12472           if (value == null) return this.tween(key, null);
12473           if (typeof value !== "function") throw new Error;
12474           return this.tween(key, styleTween(name, value, priority == null ? "" : priority));
12475         }
12476
12477         function textConstant$1(value) {
12478           return function() {
12479             this.textContent = value;
12480           };
12481         }
12482
12483         function textFunction$1(value) {
12484           return function() {
12485             var value1 = value(this);
12486             this.textContent = value1 == null ? "" : value1;
12487           };
12488         }
12489
12490         function transition_text(value) {
12491           return this.tween("text", typeof value === "function"
12492               ? textFunction$1(tweenValue(this, "text", value))
12493               : textConstant$1(value == null ? "" : value + ""));
12494         }
12495
12496         function textInterpolate(i) {
12497           return function(t) {
12498             this.textContent = i.call(this, t);
12499           };
12500         }
12501
12502         function textTween(value) {
12503           var t0, i0;
12504           function tween() {
12505             var i = value.apply(this, arguments);
12506             if (i !== i0) t0 = (i0 = i) && textInterpolate(i);
12507             return t0;
12508           }
12509           tween._value = value;
12510           return tween;
12511         }
12512
12513         function transition_textTween(value) {
12514           var key = "text";
12515           if (arguments.length < 1) return (key = this.tween(key)) && key._value;
12516           if (value == null) return this.tween(key, null);
12517           if (typeof value !== "function") throw new Error;
12518           return this.tween(key, textTween(value));
12519         }
12520
12521         function transition_transition() {
12522           var name = this._name,
12523               id0 = this._id,
12524               id1 = newId();
12525
12526           for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) {
12527             for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
12528               if (node = group[i]) {
12529                 var inherit = get$2(node, id0);
12530                 schedule(node, name, id1, i, group, {
12531                   time: inherit.time + inherit.delay + inherit.duration,
12532                   delay: 0,
12533                   duration: inherit.duration,
12534                   ease: inherit.ease
12535                 });
12536               }
12537             }
12538           }
12539
12540           return new Transition(groups, this._parents, name, id1);
12541         }
12542
12543         function transition_end() {
12544           var on0, on1, that = this, id = that._id, size = that.size();
12545           return new Promise(function(resolve, reject) {
12546             var cancel = {value: reject},
12547                 end = {value: function() { if (--size === 0) resolve(); }};
12548
12549             that.each(function() {
12550               var schedule = set$1(this, id),
12551                   on = schedule.on;
12552
12553               // If this node shared a dispatch with the previous node,
12554               // just assign the updated shared dispatch and we’re done!
12555               // Otherwise, copy-on-write.
12556               if (on !== on0) {
12557                 on1 = (on0 = on).copy();
12558                 on1._.cancel.push(cancel);
12559                 on1._.interrupt.push(cancel);
12560                 on1._.end.push(end);
12561               }
12562
12563               schedule.on = on1;
12564             });
12565           });
12566         }
12567
12568         var id$3 = 0;
12569
12570         function Transition(groups, parents, name, id) {
12571           this._groups = groups;
12572           this._parents = parents;
12573           this._name = name;
12574           this._id = id;
12575         }
12576
12577         function transition(name) {
12578           return selection().transition(name);
12579         }
12580
12581         function newId() {
12582           return ++id$3;
12583         }
12584
12585         var selection_prototype = selection.prototype;
12586
12587         Transition.prototype = transition.prototype = {
12588           constructor: Transition,
12589           select: transition_select,
12590           selectAll: transition_selectAll,
12591           filter: transition_filter,
12592           merge: transition_merge,
12593           selection: transition_selection,
12594           transition: transition_transition,
12595           call: selection_prototype.call,
12596           nodes: selection_prototype.nodes,
12597           node: selection_prototype.node,
12598           size: selection_prototype.size,
12599           empty: selection_prototype.empty,
12600           each: selection_prototype.each,
12601           on: transition_on,
12602           attr: transition_attr,
12603           attrTween: transition_attrTween,
12604           style: transition_style,
12605           styleTween: transition_styleTween,
12606           text: transition_text,
12607           textTween: transition_textTween,
12608           remove: transition_remove,
12609           tween: transition_tween,
12610           delay: transition_delay,
12611           duration: transition_duration,
12612           ease: transition_ease,
12613           end: transition_end
12614         };
12615
12616         function linear$1(t) {
12617           return +t;
12618         }
12619
12620         function cubicInOut(t) {
12621           return ((t *= 2) <= 1 ? t * t * t : (t -= 2) * t * t + 2) / 2;
12622         }
12623
12624         var defaultTiming = {
12625           time: null, // Set on use.
12626           delay: 0,
12627           duration: 250,
12628           ease: cubicInOut
12629         };
12630
12631         function inherit(node, id) {
12632           var timing;
12633           while (!(timing = node.__transition) || !(timing = timing[id])) {
12634             if (!(node = node.parentNode)) {
12635               return defaultTiming.time = now(), defaultTiming;
12636             }
12637           }
12638           return timing;
12639         }
12640
12641         function selection_transition(name) {
12642           var id,
12643               timing;
12644
12645           if (name instanceof Transition) {
12646             id = name._id, name = name._name;
12647           } else {
12648             id = newId(), (timing = defaultTiming).time = now(), name = name == null ? null : name + "";
12649           }
12650
12651           for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) {
12652             for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
12653               if (node = group[i]) {
12654                 schedule(node, name, id, i, group, timing || inherit(node, id));
12655               }
12656             }
12657           }
12658
12659           return new Transition(groups, this._parents, name, id);
12660         }
12661
12662         selection.prototype.interrupt = selection_interrupt;
12663         selection.prototype.transition = selection_transition;
12664
12665         function constant$3(x) {
12666           return function() {
12667             return x;
12668           };
12669         }
12670
12671         function ZoomEvent(target, type, transform) {
12672           this.target = target;
12673           this.type = type;
12674           this.transform = transform;
12675         }
12676
12677         function Transform(k, x, y) {
12678           this.k = k;
12679           this.x = x;
12680           this.y = y;
12681         }
12682
12683         Transform.prototype = {
12684           constructor: Transform,
12685           scale: function(k) {
12686             return k === 1 ? this : new Transform(this.k * k, this.x, this.y);
12687           },
12688           translate: function(x, y) {
12689             return x === 0 & y === 0 ? this : new Transform(this.k, this.x + this.k * x, this.y + this.k * y);
12690           },
12691           apply: function(point) {
12692             return [point[0] * this.k + this.x, point[1] * this.k + this.y];
12693           },
12694           applyX: function(x) {
12695             return x * this.k + this.x;
12696           },
12697           applyY: function(y) {
12698             return y * this.k + this.y;
12699           },
12700           invert: function(location) {
12701             return [(location[0] - this.x) / this.k, (location[1] - this.y) / this.k];
12702           },
12703           invertX: function(x) {
12704             return (x - this.x) / this.k;
12705           },
12706           invertY: function(y) {
12707             return (y - this.y) / this.k;
12708           },
12709           rescaleX: function(x) {
12710             return x.copy().domain(x.range().map(this.invertX, this).map(x.invert, x));
12711           },
12712           rescaleY: function(y) {
12713             return y.copy().domain(y.range().map(this.invertY, this).map(y.invert, y));
12714           },
12715           toString: function() {
12716             return "translate(" + this.x + "," + this.y + ") scale(" + this.k + ")";
12717           }
12718         };
12719
12720         var identity$2 = new Transform(1, 0, 0);
12721
12722         function nopropagation$1() {
12723           event.stopImmediatePropagation();
12724         }
12725
12726         function noevent$1() {
12727           event.preventDefault();
12728           event.stopImmediatePropagation();
12729         }
12730
12731         // Ignore right-click, since that should open the context menu.
12732         function defaultFilter$1() {
12733           return !event.ctrlKey && !event.button;
12734         }
12735
12736         function defaultExtent() {
12737           var e = this;
12738           if (e instanceof SVGElement) {
12739             e = e.ownerSVGElement || e;
12740             if (e.hasAttribute("viewBox")) {
12741               e = e.viewBox.baseVal;
12742               return [[e.x, e.y], [e.x + e.width, e.y + e.height]];
12743             }
12744             return [[0, 0], [e.width.baseVal.value, e.height.baseVal.value]];
12745           }
12746           return [[0, 0], [e.clientWidth, e.clientHeight]];
12747         }
12748
12749         function defaultTransform() {
12750           return this.__zoom || identity$2;
12751         }
12752
12753         function defaultWheelDelta() {
12754           return -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002);
12755         }
12756
12757         function defaultTouchable$1() {
12758           return navigator.maxTouchPoints || ("ontouchstart" in this);
12759         }
12760
12761         function defaultConstrain(transform, extent, translateExtent) {
12762           var dx0 = transform.invertX(extent[0][0]) - translateExtent[0][0],
12763               dx1 = transform.invertX(extent[1][0]) - translateExtent[1][0],
12764               dy0 = transform.invertY(extent[0][1]) - translateExtent[0][1],
12765               dy1 = transform.invertY(extent[1][1]) - translateExtent[1][1];
12766           return transform.translate(
12767             dx1 > dx0 ? (dx0 + dx1) / 2 : Math.min(0, dx0) || Math.max(0, dx1),
12768             dy1 > dy0 ? (dy0 + dy1) / 2 : Math.min(0, dy0) || Math.max(0, dy1)
12769           );
12770         }
12771
12772         function d3_zoom() {
12773           var filter = defaultFilter$1,
12774               extent = defaultExtent,
12775               constrain = defaultConstrain,
12776               wheelDelta = defaultWheelDelta,
12777               touchable = defaultTouchable$1,
12778               scaleExtent = [0, Infinity],
12779               translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]],
12780               duration = 250,
12781               interpolate = interpolateZoom,
12782               listeners = dispatch("start", "zoom", "end"),
12783               touchstarting,
12784               touchending,
12785               touchDelay = 500,
12786               wheelDelay = 150,
12787               clickDistance2 = 0;
12788
12789           function zoom(selection) {
12790             selection
12791                 .property("__zoom", defaultTransform)
12792                 .on("wheel.zoom", wheeled)
12793                 .on("mousedown.zoom", mousedowned)
12794                 .on("dblclick.zoom", dblclicked)
12795               .filter(touchable)
12796                 .on("touchstart.zoom", touchstarted)
12797                 .on("touchmove.zoom", touchmoved)
12798                 .on("touchend.zoom touchcancel.zoom", touchended)
12799                 .style("touch-action", "none")
12800                 .style("-webkit-tap-highlight-color", "rgba(0,0,0,0)");
12801           }
12802
12803           zoom.transform = function(collection, transform, point) {
12804             var selection = collection.selection ? collection.selection() : collection;
12805             selection.property("__zoom", defaultTransform);
12806             if (collection !== selection) {
12807               schedule(collection, transform, point);
12808             } else {
12809               selection.interrupt().each(function() {
12810                 gesture(this, arguments)
12811                     .start()
12812                     .zoom(null, typeof transform === "function" ? transform.apply(this, arguments) : transform)
12813                     .end();
12814               });
12815             }
12816           };
12817
12818           zoom.scaleBy = function(selection, k, p) {
12819             zoom.scaleTo(selection, function() {
12820               var k0 = this.__zoom.k,
12821                   k1 = typeof k === "function" ? k.apply(this, arguments) : k;
12822               return k0 * k1;
12823             }, p);
12824           };
12825
12826           zoom.scaleTo = function(selection, k, p) {
12827             zoom.transform(selection, function() {
12828               var e = extent.apply(this, arguments),
12829                   t0 = this.__zoom,
12830                   p0 = p == null ? centroid(e) : typeof p === "function" ? p.apply(this, arguments) : p,
12831                   p1 = t0.invert(p0),
12832                   k1 = typeof k === "function" ? k.apply(this, arguments) : k;
12833               return constrain(translate(scale(t0, k1), p0, p1), e, translateExtent);
12834             }, p);
12835           };
12836
12837           zoom.translateBy = function(selection, x, y) {
12838             zoom.transform(selection, function() {
12839               return constrain(this.__zoom.translate(
12840                 typeof x === "function" ? x.apply(this, arguments) : x,
12841                 typeof y === "function" ? y.apply(this, arguments) : y
12842               ), extent.apply(this, arguments), translateExtent);
12843             });
12844           };
12845
12846           zoom.translateTo = function(selection, x, y, p) {
12847             zoom.transform(selection, function() {
12848               var e = extent.apply(this, arguments),
12849                   t = this.__zoom,
12850                   p0 = p == null ? centroid(e) : typeof p === "function" ? p.apply(this, arguments) : p;
12851               return constrain(identity$2.translate(p0[0], p0[1]).scale(t.k).translate(
12852                 typeof x === "function" ? -x.apply(this, arguments) : -x,
12853                 typeof y === "function" ? -y.apply(this, arguments) : -y
12854               ), e, translateExtent);
12855             }, p);
12856           };
12857
12858           function scale(transform, k) {
12859             k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], k));
12860             return k === transform.k ? transform : new Transform(k, transform.x, transform.y);
12861           }
12862
12863           function translate(transform, p0, p1) {
12864             var x = p0[0] - p1[0] * transform.k, y = p0[1] - p1[1] * transform.k;
12865             return x === transform.x && y === transform.y ? transform : new Transform(transform.k, x, y);
12866           }
12867
12868           function centroid(extent) {
12869             return [(+extent[0][0] + +extent[1][0]) / 2, (+extent[0][1] + +extent[1][1]) / 2];
12870           }
12871
12872           function schedule(transition, transform, point) {
12873             transition
12874                 .on("start.zoom", function() { gesture(this, arguments).start(); })
12875                 .on("interrupt.zoom end.zoom", function() { gesture(this, arguments).end(); })
12876                 .tween("zoom", function() {
12877                   var that = this,
12878                       args = arguments,
12879                       g = gesture(that, args),
12880                       e = extent.apply(that, args),
12881                       p = point == null ? centroid(e) : typeof point === "function" ? point.apply(that, args) : point,
12882                       w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]),
12883                       a = that.__zoom,
12884                       b = typeof transform === "function" ? transform.apply(that, args) : transform,
12885                       i = interpolate(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k));
12886                   return function(t) {
12887                     if (t === 1) t = b; // Avoid rounding error on end.
12888                     else { var l = i(t), k = w / l[2]; t = new Transform(k, p[0] - l[0] * k, p[1] - l[1] * k); }
12889                     g.zoom(null, t);
12890                   };
12891                 });
12892           }
12893
12894           function gesture(that, args, clean) {
12895             return (!clean && that.__zooming) || new Gesture(that, args);
12896           }
12897
12898           function Gesture(that, args) {
12899             this.that = that;
12900             this.args = args;
12901             this.active = 0;
12902             this.extent = extent.apply(that, args);
12903             this.taps = 0;
12904           }
12905
12906           Gesture.prototype = {
12907             start: function() {
12908               if (++this.active === 1) {
12909                 this.that.__zooming = this;
12910                 this.emit("start");
12911               }
12912               return this;
12913             },
12914             zoom: function(key, transform) {
12915               if (this.mouse && key !== "mouse") this.mouse[1] = transform.invert(this.mouse[0]);
12916               if (this.touch0 && key !== "touch") this.touch0[1] = transform.invert(this.touch0[0]);
12917               if (this.touch1 && key !== "touch") this.touch1[1] = transform.invert(this.touch1[0]);
12918               this.that.__zoom = transform;
12919               this.emit("zoom");
12920               return this;
12921             },
12922             end: function() {
12923               if (--this.active === 0) {
12924                 delete this.that.__zooming;
12925                 this.emit("end");
12926               }
12927               return this;
12928             },
12929             emit: function(type) {
12930               customEvent(new ZoomEvent(zoom, type, this.that.__zoom), listeners.apply, listeners, [type, this.that, this.args]);
12931             }
12932           };
12933
12934           function wheeled() {
12935             if (!filter.apply(this, arguments)) return;
12936             var g = gesture(this, arguments),
12937                 t = this.__zoom,
12938                 k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], t.k * Math.pow(2, wheelDelta.apply(this, arguments)))),
12939                 p = mouse(this);
12940
12941             // If the mouse is in the same location as before, reuse it.
12942             // If there were recent wheel events, reset the wheel idle timeout.
12943             if (g.wheel) {
12944               if (g.mouse[0][0] !== p[0] || g.mouse[0][1] !== p[1]) {
12945                 g.mouse[1] = t.invert(g.mouse[0] = p);
12946               }
12947               clearTimeout(g.wheel);
12948             }
12949
12950             // If this wheel event won’t trigger a transform change, ignore it.
12951             else if (t.k === k) return;
12952
12953             // Otherwise, capture the mouse point and location at the start.
12954             else {
12955               g.mouse = [p, t.invert(p)];
12956               interrupt(this);
12957               g.start();
12958             }
12959
12960             noevent$1();
12961             g.wheel = setTimeout(wheelidled, wheelDelay);
12962             g.zoom("mouse", constrain(translate(scale(t, k), g.mouse[0], g.mouse[1]), g.extent, translateExtent));
12963
12964             function wheelidled() {
12965               g.wheel = null;
12966               g.end();
12967             }
12968           }
12969
12970           function mousedowned() {
12971             if (touchending || !filter.apply(this, arguments)) return;
12972             var g = gesture(this, arguments, true),
12973                 v = select(event.view).on("mousemove.zoom", mousemoved, true).on("mouseup.zoom", mouseupped, true),
12974                 p = mouse(this),
12975                 x0 = event.clientX,
12976                 y0 = event.clientY;
12977
12978             dragDisable(event.view);
12979             nopropagation$1();
12980             g.mouse = [p, this.__zoom.invert(p)];
12981             interrupt(this);
12982             g.start();
12983
12984             function mousemoved() {
12985               noevent$1();
12986               if (!g.moved) {
12987                 var dx = event.clientX - x0, dy = event.clientY - y0;
12988                 g.moved = dx * dx + dy * dy > clickDistance2;
12989               }
12990               g.zoom("mouse", constrain(translate(g.that.__zoom, g.mouse[0] = mouse(g.that), g.mouse[1]), g.extent, translateExtent));
12991             }
12992
12993             function mouseupped() {
12994               v.on("mousemove.zoom mouseup.zoom", null);
12995               yesdrag(event.view, g.moved);
12996               noevent$1();
12997               g.end();
12998             }
12999           }
13000
13001           function dblclicked() {
13002             if (!filter.apply(this, arguments)) return;
13003             var t0 = this.__zoom,
13004                 p0 = mouse(this),
13005                 p1 = t0.invert(p0),
13006                 k1 = t0.k * (event.shiftKey ? 0.5 : 2),
13007                 t1 = constrain(translate(scale(t0, k1), p0, p1), extent.apply(this, arguments), translateExtent);
13008
13009             noevent$1();
13010             if (duration > 0) select(this).transition().duration(duration).call(schedule, t1, p0);
13011             else select(this).call(zoom.transform, t1);
13012           }
13013
13014           function touchstarted() {
13015             if (!filter.apply(this, arguments)) return;
13016             var touches = event.touches,
13017                 n = touches.length,
13018                 g = gesture(this, arguments, event.changedTouches.length === n),
13019                 started, i, t, p;
13020
13021             nopropagation$1();
13022             for (i = 0; i < n; ++i) {
13023               t = touches[i], p = touch(this, touches, t.identifier);
13024               p = [p, this.__zoom.invert(p), t.identifier];
13025               if (!g.touch0) g.touch0 = p, started = true, g.taps = 1 + !!touchstarting;
13026               else if (!g.touch1 && g.touch0[2] !== p[2]) g.touch1 = p, g.taps = 0;
13027             }
13028
13029             if (touchstarting) touchstarting = clearTimeout(touchstarting);
13030
13031             if (started) {
13032               if (g.taps < 2) touchstarting = setTimeout(function() { touchstarting = null; }, touchDelay);
13033               interrupt(this);
13034               g.start();
13035             }
13036           }
13037
13038           function touchmoved() {
13039             if (!this.__zooming) return;
13040             var g = gesture(this, arguments),
13041                 touches = event.changedTouches,
13042                 n = touches.length, i, t, p, l;
13043
13044             noevent$1();
13045             if (touchstarting) touchstarting = clearTimeout(touchstarting);
13046             g.taps = 0;
13047             for (i = 0; i < n; ++i) {
13048               t = touches[i], p = touch(this, touches, t.identifier);
13049               if (g.touch0 && g.touch0[2] === t.identifier) g.touch0[0] = p;
13050               else if (g.touch1 && g.touch1[2] === t.identifier) g.touch1[0] = p;
13051             }
13052             t = g.that.__zoom;
13053             if (g.touch1) {
13054               var p0 = g.touch0[0], l0 = g.touch0[1],
13055                   p1 = g.touch1[0], l1 = g.touch1[1],
13056                   dp = (dp = p1[0] - p0[0]) * dp + (dp = p1[1] - p0[1]) * dp,
13057                   dl = (dl = l1[0] - l0[0]) * dl + (dl = l1[1] - l0[1]) * dl;
13058               t = scale(t, Math.sqrt(dp / dl));
13059               p = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2];
13060               l = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2];
13061             }
13062             else if (g.touch0) p = g.touch0[0], l = g.touch0[1];
13063             else return;
13064             g.zoom("touch", constrain(translate(t, p, l), g.extent, translateExtent));
13065           }
13066
13067           function touchended() {
13068             if (!this.__zooming) return;
13069             var g = gesture(this, arguments),
13070                 touches = event.changedTouches,
13071                 n = touches.length, i, t;
13072
13073             nopropagation$1();
13074             if (touchending) clearTimeout(touchending);
13075             touchending = setTimeout(function() { touchending = null; }, touchDelay);
13076             for (i = 0; i < n; ++i) {
13077               t = touches[i];
13078               if (g.touch0 && g.touch0[2] === t.identifier) delete g.touch0;
13079               else if (g.touch1 && g.touch1[2] === t.identifier) delete g.touch1;
13080             }
13081             if (g.touch1 && !g.touch0) g.touch0 = g.touch1, delete g.touch1;
13082             if (g.touch0) g.touch0[1] = this.__zoom.invert(g.touch0[0]);
13083             else {
13084               g.end();
13085               // If this was a dbltap, reroute to the (optional) dblclick.zoom handler.
13086               if (g.taps === 2) {
13087                 var p = select(this).on("dblclick.zoom");
13088                 if (p) p.apply(this, arguments);
13089               }
13090             }
13091           }
13092
13093           zoom.wheelDelta = function(_) {
13094             return arguments.length ? (wheelDelta = typeof _ === "function" ? _ : constant$3(+_), zoom) : wheelDelta;
13095           };
13096
13097           zoom.filter = function(_) {
13098             return arguments.length ? (filter = typeof _ === "function" ? _ : constant$3(!!_), zoom) : filter;
13099           };
13100
13101           zoom.touchable = function(_) {
13102             return arguments.length ? (touchable = typeof _ === "function" ? _ : constant$3(!!_), zoom) : touchable;
13103           };
13104
13105           zoom.extent = function(_) {
13106             return arguments.length ? (extent = typeof _ === "function" ? _ : constant$3([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom) : extent;
13107           };
13108
13109           zoom.scaleExtent = function(_) {
13110             return arguments.length ? (scaleExtent[0] = +_[0], scaleExtent[1] = +_[1], zoom) : [scaleExtent[0], scaleExtent[1]];
13111           };
13112
13113           zoom.translateExtent = function(_) {
13114             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]]];
13115           };
13116
13117           zoom.constrain = function(_) {
13118             return arguments.length ? (constrain = _, zoom) : constrain;
13119           };
13120
13121           zoom.duration = function(_) {
13122             return arguments.length ? (duration = +_, zoom) : duration;
13123           };
13124
13125           zoom.interpolate = function(_) {
13126             return arguments.length ? (interpolate = _, zoom) : interpolate;
13127           };
13128
13129           zoom.on = function() {
13130             var value = listeners.on.apply(listeners, arguments);
13131             return value === listeners ? zoom : value;
13132           };
13133
13134           zoom.clickDistance = function(_) {
13135             return arguments.length ? (clickDistance2 = (_ = +_) * _, zoom) : Math.sqrt(clickDistance2);
13136           };
13137
13138           return zoom;
13139         }
13140
13141         /*
13142             Bypasses features of D3's default projection stream pipeline that are unnecessary:
13143             * Antimeridian clipping
13144             * Spherical rotation
13145             * Resampling
13146         */
13147         function geoRawMercator() {
13148             var project = mercatorRaw;
13149             var k = 512 / Math.PI; // scale
13150             var x = 0;
13151             var y = 0; // translate
13152             var clipExtent = [[0, 0], [0, 0]];
13153
13154
13155             function projection(point) {
13156                 point = project(point[0] * Math.PI / 180, point[1] * Math.PI / 180);
13157                 return [point[0] * k + x, y - point[1] * k];
13158             }
13159
13160
13161             projection.invert = function(point) {
13162                 point = project.invert((point[0] - x) / k, (y - point[1]) / k);
13163                 return point && [point[0] * 180 / Math.PI, point[1] * 180 / Math.PI];
13164             };
13165
13166
13167             projection.scale = function(_) {
13168                 if (!arguments.length) return k;
13169                 k = +_;
13170                 return projection;
13171             };
13172
13173
13174             projection.translate = function(_) {
13175                 if (!arguments.length) return [x, y];
13176                 x = +_[0];
13177                 y = +_[1];
13178                 return projection;
13179             };
13180
13181
13182             projection.clipExtent = function(_) {
13183                 if (!arguments.length) return clipExtent;
13184                 clipExtent = _;
13185                 return projection;
13186             };
13187
13188
13189             projection.transform = function(obj) {
13190                 if (!arguments.length) return identity$2.translate(x, y).scale(k);
13191                 x = +obj.x;
13192                 y = +obj.y;
13193                 k = +obj.k;
13194                 return projection;
13195             };
13196
13197
13198             projection.stream = d3_geoTransform({
13199                 point: function(x, y) {
13200                     var vec = projection([x, y]);
13201                     this.stream.point(vec[0], vec[1]);
13202                 }
13203             }).stream;
13204
13205
13206             return projection;
13207         }
13208
13209         function geoOrthoNormalizedDotProduct(a, b, origin) {
13210             if (geoVecEqual(origin, a) || geoVecEqual(origin, b)) {
13211                 return 1;  // coincident points, treat as straight and try to remove
13212             }
13213             return geoVecNormalizedDot(a, b, origin);
13214         }
13215
13216
13217         function geoOrthoFilterDotProduct(dotp, epsilon, lowerThreshold, upperThreshold, allowStraightAngles) {
13218             var val = Math.abs(dotp);
13219             if (val < epsilon) {
13220                 return 0;      // already orthogonal
13221             } else if (allowStraightAngles && Math.abs(val-1) < epsilon) {
13222                 return 0;      // straight angle, which is okay in this case
13223             } else if (val < lowerThreshold || val > upperThreshold) {
13224                 return dotp;   // can be adjusted
13225             } else {
13226                 return null;   // ignore vertex
13227             }
13228         }
13229
13230
13231         function geoOrthoCalcScore(points, isClosed, epsilon, threshold) {
13232             var score = 0;
13233             var first = isClosed ? 0 : 1;
13234             var last = isClosed ? points.length : points.length - 1;
13235             var coords = points.map(function(p) { return p.coord; });
13236
13237             var lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180);
13238             var upperThreshold = Math.cos(threshold * Math.PI / 180);
13239
13240             for (var i = first; i < last; i++) {
13241                 var a = coords[(i - 1 + coords.length) % coords.length];
13242                 var origin = coords[i];
13243                 var b = coords[(i + 1) % coords.length];
13244
13245                 var dotp = geoOrthoFilterDotProduct(geoOrthoNormalizedDotProduct(a, b, origin), epsilon, lowerThreshold, upperThreshold);
13246                 if (dotp === null) continue;    // ignore vertex
13247                 score = score + 2.0 * Math.min(Math.abs(dotp - 1.0), Math.min(Math.abs(dotp), Math.abs(dotp + 1)));
13248             }
13249
13250             return score;
13251         }
13252
13253         // returns the maximum angle less than `lessThan` between the actual corner and a 0° or 90° corner
13254         function geoOrthoMaxOffsetAngle(coords, isClosed, lessThan) {
13255             var max = -Infinity;
13256
13257             var first = isClosed ? 0 : 1;
13258             var last = isClosed ? coords.length : coords.length - 1;
13259
13260             for (var i = first; i < last; i++) {
13261                 var a = coords[(i - 1 + coords.length) % coords.length];
13262                 var origin = coords[i];
13263                 var b = coords[(i + 1) % coords.length];
13264                 var normalizedDotP = geoOrthoNormalizedDotProduct(a, b, origin);
13265
13266                 var angle = Math.acos(Math.abs(normalizedDotP)) * 180 / Math.PI;
13267
13268                 if (angle > 45) angle = 90 - angle;
13269
13270                 if (angle >= lessThan) continue;
13271
13272                 if (angle > max) max = angle;
13273             }
13274
13275             if (max === -Infinity) return null;
13276
13277             return max;
13278         }
13279
13280
13281         // similar to geoOrthoCalcScore, but returns quickly if there is something to do
13282         function geoOrthoCanOrthogonalize(coords, isClosed, epsilon, threshold, allowStraightAngles) {
13283             var score = null;
13284             var first = isClosed ? 0 : 1;
13285             var last = isClosed ? coords.length : coords.length - 1;
13286
13287             var lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180);
13288             var upperThreshold = Math.cos(threshold * Math.PI / 180);
13289
13290             for (var i = first; i < last; i++) {
13291                 var a = coords[(i - 1 + coords.length) % coords.length];
13292                 var origin = coords[i];
13293                 var b = coords[(i + 1) % coords.length];
13294
13295                 var dotp = geoOrthoFilterDotProduct(geoOrthoNormalizedDotProduct(a, b, origin), epsilon, lowerThreshold, upperThreshold, allowStraightAngles);
13296                 if (dotp === null) continue;        // ignore vertex
13297                 if (Math.abs(dotp) > 0) return 1;   // something to do
13298                 score = 0;                          // already square
13299             }
13300
13301             return score;
13302         }
13303
13304         // Returns true if a and b have the same elements at the same indices.
13305         function utilArrayIdentical(a, b) {
13306             // an array is always identical to itself
13307             if (a === b) return true;
13308
13309             var i = a.length;
13310             if (i !== b.length) return false;
13311             while (i--) {
13312                 if (a[i] !== b[i]) return false;
13313             }
13314             return true;
13315         }
13316
13317         // http://2ality.com/2015/01/es6-set-operations.html
13318
13319         // Difference (a \ b): create a set that contains those elements of set a that are not in set b.
13320         // This operation is also sometimes called minus (-).
13321         // var a = [1,2,3];
13322         // var b = [4,3,2];
13323         // utilArrayDifference(a, b)
13324         //   [1]
13325         // utilArrayDifference(b, a)
13326         //   [4]
13327         function utilArrayDifference(a, b) {
13328             var other = new Set(b);
13329             return Array.from(new Set(a))
13330                 .filter(function(v) { return !other.has(v); });
13331         }
13332
13333         // Intersection (a ∩ b): create a set that contains those elements of set a that are also in set b.
13334         // var a = [1,2,3];
13335         // var b = [4,3,2];
13336         // utilArrayIntersection(a, b)
13337         //   [2,3]
13338         function utilArrayIntersection(a, b) {
13339             var other = new Set(b);
13340             return Array.from(new Set(a))
13341                 .filter(function(v) { return other.has(v); });
13342         }
13343
13344         // Union (a ∪ b): create a set that contains the elements of both set a and set b.
13345         // var a = [1,2,3];
13346         // var b = [4,3,2];
13347         // utilArrayUnion(a, b)
13348         //   [1,2,3,4]
13349         function utilArrayUnion(a, b) {
13350             var result = new Set(a);
13351             b.forEach(function(v) { result.add(v); });
13352             return Array.from(result);
13353         }
13354
13355         // Returns an Array with all the duplicates removed
13356         // var a = [1,1,2,3,3];
13357         // utilArrayUniq(a)
13358         //   [1,2,3]
13359         function utilArrayUniq(a) {
13360             return Array.from(new Set(a));
13361         }
13362
13363
13364         // Splits array into chunks of given chunk size
13365         // var a = [1,2,3,4,5,6,7];
13366         // utilArrayChunk(a, 3);
13367         //   [[1,2,3],[4,5,6],[7]];
13368         function utilArrayChunk(a, chunkSize) {
13369             if (!chunkSize || chunkSize < 0) return [a.slice()];
13370
13371             var result = new Array(Math.ceil(a.length / chunkSize));
13372             return Array.from(result, function(item, i) {
13373                 return a.slice(i * chunkSize, i * chunkSize + chunkSize);
13374             });
13375         }
13376
13377
13378         // Flattens two level array into a single level
13379         // var a = [[1,2,3],[4,5,6],[7]];
13380         // utilArrayFlatten(a);
13381         //   [1,2,3,4,5,6,7];
13382         function utilArrayFlatten(a) {
13383             return a.reduce(function(acc, val) {
13384                 return acc.concat(val);
13385             }, []);
13386         }
13387
13388
13389         // Groups the items of the Array according to the given key
13390         // `key` can be passed as a property or as a key function
13391         //
13392         // var pets = [
13393         //     { type: 'Dog', name: 'Spot' },
13394         //     { type: 'Cat', name: 'Tiger' },
13395         //     { type: 'Dog', name: 'Rover' },
13396         //     { type: 'Cat', name: 'Leo' }
13397         // ];
13398         //
13399         // utilArrayGroupBy(pets, 'type')
13400         //   {
13401         //     'Dog': [{type: 'Dog', name: 'Spot'}, {type: 'Dog', name: 'Rover'}],
13402         //     'Cat': [{type: 'Cat', name: 'Tiger'}, {type: 'Cat', name: 'Leo'}]
13403         //   }
13404         //
13405         // utilArrayGroupBy(pets, function(item) { return item.name.length; })
13406         //   {
13407         //     3: [{type: 'Cat', name: 'Leo'}],
13408         //     4: [{type: 'Dog', name: 'Spot'}],
13409         //     5: [{type: 'Cat', name: 'Tiger'}, {type: 'Dog', name: 'Rover'}]
13410         //   }
13411         function utilArrayGroupBy(a, key) {
13412             return a.reduce(function(acc, item) {
13413                 var group = (typeof key === 'function') ? key(item) : item[key];
13414                 (acc[group] = acc[group] || []).push(item);
13415                 return acc;
13416             }, {});
13417         }
13418
13419
13420         // Returns an Array with all the duplicates removed
13421         // where uniqueness determined by the given key
13422         // `key` can be passed as a property or as a key function
13423         //
13424         // var pets = [
13425         //     { type: 'Dog', name: 'Spot' },
13426         //     { type: 'Cat', name: 'Tiger' },
13427         //     { type: 'Dog', name: 'Rover' },
13428         //     { type: 'Cat', name: 'Leo' }
13429         // ];
13430         //
13431         // utilArrayUniqBy(pets, 'type')
13432         //   [
13433         //     { type: 'Dog', name: 'Spot' },
13434         //     { type: 'Cat', name: 'Tiger' }
13435         //   ]
13436         //
13437         // utilArrayUniqBy(pets, function(item) { return item.name.length; })
13438         //   [
13439         //     { type: 'Dog', name: 'Spot' },
13440         //     { type: 'Cat', name: 'Tiger' },
13441         //     { type: 'Cat', name: 'Leo' }
13442         //   }
13443         function utilArrayUniqBy(a, key) {
13444             var seen = new Set();
13445             return a.reduce(function(acc, item) {
13446                 var val = (typeof key === 'function') ? key(item) : item[key];
13447                 if (val && !seen.has(val)) {
13448                     seen.add(val);
13449                     acc.push(item);
13450                 }
13451                 return acc;
13452             }, []);
13453         }
13454
13455         var remove$1 = removeDiacritics;
13456
13457         var replacementList = [
13458           {
13459             base: ' ',
13460             chars: "\u00A0",
13461           }, {
13462             base: '0',
13463             chars: "\u07C0",
13464           }, {
13465             base: 'A',
13466             chars: "\u24B6\uFF21\u00C0\u00C1\u00C2\u1EA6\u1EA4\u1EAA\u1EA8\u00C3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\u00C4\u01DE\u1EA2\u00C5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F",
13467           }, {
13468             base: 'AA',
13469             chars: "\uA732",
13470           }, {
13471             base: 'AE',
13472             chars: "\u00C6\u01FC\u01E2",
13473           }, {
13474             base: 'AO',
13475             chars: "\uA734",
13476           }, {
13477             base: 'AU',
13478             chars: "\uA736",
13479           }, {
13480             base: 'AV',
13481             chars: "\uA738\uA73A",
13482           }, {
13483             base: 'AY',
13484             chars: "\uA73C",
13485           }, {
13486             base: 'B',
13487             chars: "\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0181",
13488           }, {
13489             base: 'C',
13490             chars: "\u24b8\uff23\uA73E\u1E08\u0106\u0043\u0108\u010A\u010C\u00C7\u0187\u023B",
13491           }, {
13492             base: 'D',
13493             chars: "\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018A\u0189\u1D05\uA779",
13494           }, {
13495             base: 'Dh',
13496             chars: "\u00D0",
13497           }, {
13498             base: 'DZ',
13499             chars: "\u01F1\u01C4",
13500           }, {
13501             base: 'Dz',
13502             chars: "\u01F2\u01C5",
13503           }, {
13504             base: 'E',
13505             chars: "\u025B\u24BA\uFF25\u00C8\u00C9\u00CA\u1EC0\u1EBE\u1EC4\u1EC2\u1EBC\u0112\u1E14\u1E16\u0114\u0116\u00CB\u1EBA\u011A\u0204\u0206\u1EB8\u1EC6\u0228\u1E1C\u0118\u1E18\u1E1A\u0190\u018E\u1D07",
13506           }, {
13507             base: 'F',
13508             chars: "\uA77C\u24BB\uFF26\u1E1E\u0191\uA77B",
13509           }, {
13510             base: 'G',
13511             chars: "\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E\u0262",
13512           }, {
13513             base: 'H',
13514             chars: "\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D",
13515           }, {
13516             base: 'I',
13517             chars: "\u24BE\uFF29\xCC\xCD\xCE\u0128\u012A\u012C\u0130\xCF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197",
13518           }, {
13519             base: 'J',
13520             chars: "\u24BF\uFF2A\u0134\u0248\u0237",
13521           }, {
13522             base: 'K',
13523             chars: "\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2",
13524           }, {
13525             base: 'L',
13526             chars: "\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780",
13527           }, {
13528             base: 'LJ',
13529             chars: "\u01C7",
13530           }, {
13531             base: 'Lj',
13532             chars: "\u01C8",
13533           }, {
13534             base: 'M',
13535             chars: "\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C\u03FB",
13536           }, {
13537             base: 'N',
13538             chars: "\uA7A4\u0220\u24C3\uFF2E\u01F8\u0143\xD1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u019D\uA790\u1D0E",
13539           }, {
13540             base: 'NJ',
13541             chars: "\u01CA",
13542           }, {
13543             base: 'Nj',
13544             chars: "\u01CB",
13545           }, {
13546             base: 'O',
13547             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",
13548           }, {
13549             base: 'OE',
13550             chars: "\u0152",
13551           }, {
13552             base: 'OI',
13553             chars: "\u01A2",
13554           }, {
13555             base: 'OO',
13556             chars: "\uA74E",
13557           }, {
13558             base: 'OU',
13559             chars: "\u0222",
13560           }, {
13561             base: 'P',
13562             chars: "\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754",
13563           }, {
13564             base: 'Q',
13565             chars: "\u24C6\uFF31\uA756\uA758\u024A",
13566           }, {
13567             base: 'R',
13568             chars: "\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782",
13569           }, {
13570             base: 'S',
13571             chars: "\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784",
13572           }, {
13573             base: 'T',
13574             chars: "\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786",
13575           }, {
13576             base: 'Th',
13577             chars: "\u00DE",
13578           }, {
13579             base: 'TZ',
13580             chars: "\uA728",
13581           }, {
13582             base: 'U',
13583             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",
13584           }, {
13585             base: 'V',
13586             chars: "\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245",
13587           }, {
13588             base: 'VY',
13589             chars: "\uA760",
13590           }, {
13591             base: 'W',
13592             chars: "\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72",
13593           }, {
13594             base: 'X',
13595             chars: "\u24CD\uFF38\u1E8A\u1E8C",
13596           }, {
13597             base: 'Y',
13598             chars: "\u24CE\uFF39\u1EF2\xDD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE",
13599           }, {
13600             base: 'Z',
13601             chars: "\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762",
13602           }, {
13603             base: 'a',
13604             chars: "\u24D0\uFF41\u1E9A\u00E0\u00E1\u00E2\u1EA7\u1EA5\u1EAB\u1EA9\u00E3\u0101\u0103\u1EB1\u1EAF\u1EB5\u1EB3\u0227\u01E1\u00E4\u01DF\u1EA3\u00E5\u01FB\u01CE\u0201\u0203\u1EA1\u1EAD\u1EB7\u1E01\u0105\u2C65\u0250\u0251",
13605           }, {
13606             base: 'aa',
13607             chars: "\uA733",
13608           }, {
13609             base: 'ae',
13610             chars: "\u00E6\u01FD\u01E3",
13611           }, {
13612             base: 'ao',
13613             chars: "\uA735",
13614           }, {
13615             base: 'au',
13616             chars: "\uA737",
13617           }, {
13618             base: 'av',
13619             chars: "\uA739\uA73B",
13620           }, {
13621             base: 'ay',
13622             chars: "\uA73D",
13623           }, {
13624             base: 'b',
13625             chars: "\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253\u0182",
13626           }, {
13627             base: 'c',
13628             chars: "\uFF43\u24D2\u0107\u0109\u010B\u010D\u00E7\u1E09\u0188\u023C\uA73F\u2184",
13629           }, {
13630             base: 'd',
13631             chars: "\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\u018B\u13E7\u0501\uA7AA",
13632           }, {
13633             base: 'dh',
13634             chars: "\u00F0",
13635           }, {
13636             base: 'dz',
13637             chars: "\u01F3\u01C6",
13638           }, {
13639             base: 'e',
13640             chars: "\u24D4\uFF45\u00E8\u00E9\u00EA\u1EC1\u1EBF\u1EC5\u1EC3\u1EBD\u0113\u1E15\u1E17\u0115\u0117\u00EB\u1EBB\u011B\u0205\u0207\u1EB9\u1EC7\u0229\u1E1D\u0119\u1E19\u1E1B\u0247\u01DD",
13641           }, {
13642             base: 'f',
13643             chars: "\u24D5\uFF46\u1E1F\u0192",
13644           }, {
13645             base: 'ff',
13646             chars: "\uFB00",
13647           }, {
13648             base: 'fi',
13649             chars: "\uFB01",
13650           }, {
13651             base: 'fl',
13652             chars: "\uFB02",
13653           }, {
13654             base: 'ffi',
13655             chars: "\uFB03",
13656           }, {
13657             base: 'ffl',
13658             chars: "\uFB04",
13659           }, {
13660             base: 'g',
13661             chars: "\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\uA77F\u1D79",
13662           }, {
13663             base: 'h',
13664             chars: "\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265",
13665           }, {
13666             base: 'hv',
13667             chars: "\u0195",
13668           }, {
13669             base: 'i',
13670             chars: "\u24D8\uFF49\xEC\xED\xEE\u0129\u012B\u012D\xEF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131",
13671           }, {
13672             base: 'j',
13673             chars: "\u24D9\uFF4A\u0135\u01F0\u0249",
13674           }, {
13675             base: 'k',
13676             chars: "\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3",
13677           }, {
13678             base: 'l',
13679             chars: "\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747\u026D",
13680           }, {
13681             base: 'lj',
13682             chars: "\u01C9",
13683           }, {
13684             base: 'm',
13685             chars: "\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F",
13686           }, {
13687             base: 'n',
13688             chars: "\u24DD\uFF4E\u01F9\u0144\xF1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5\u043B\u0509",
13689           }, {
13690             base: 'nj',
13691             chars: "\u01CC",
13692           }, {
13693             base: 'o',
13694             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",
13695           }, {
13696             base: 'oe',
13697             chars: "\u0153",
13698           }, {
13699             base: 'oi',
13700             chars: "\u01A3",
13701           }, {
13702             base: 'oo',
13703             chars: "\uA74F",
13704           }, {
13705             base: 'ou',
13706             chars: "\u0223",
13707           }, {
13708             base: 'p',
13709             chars: "\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755\u03C1",
13710           }, {
13711             base: 'q',
13712             chars: "\u24E0\uFF51\u024B\uA757\uA759",
13713           }, {
13714             base: 'r',
13715             chars: "\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783",
13716           }, {
13717             base: 's',
13718             chars: "\u24E2\uFF53\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B\u0282",
13719           }, {
13720             base: 'ss',
13721             chars: "\xDF",
13722           }, {
13723             base: 't',
13724             chars: "\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787",
13725           }, {
13726             base: 'th',
13727             chars: "\u00FE",
13728           }, {
13729             base: 'tz',
13730             chars: "\uA729",
13731           }, {
13732             base: 'u',
13733             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",
13734           }, {
13735             base: 'v',
13736             chars: "\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C",
13737           }, {
13738             base: 'vy',
13739             chars: "\uA761",
13740           }, {
13741             base: 'w',
13742             chars: "\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73",
13743           }, {
13744             base: 'x',
13745             chars: "\u24E7\uFF58\u1E8B\u1E8D",
13746           }, {
13747             base: 'y',
13748             chars: "\u24E8\uFF59\u1EF3\xFD\u0177\u1EF9\u0233\u1E8F\xFF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF",
13749           }, {
13750             base: 'z',
13751             chars: "\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763",
13752           }
13753         ];
13754
13755         var diacriticsMap = {};
13756         for (var i$1 = 0; i$1 < replacementList.length; i$1 += 1) {
13757           var chars = replacementList[i$1].chars;
13758           for (var j = 0; j < chars.length; j += 1) {
13759             diacriticsMap[chars[j]] = replacementList[i$1].base;
13760           }
13761         }
13762
13763         function removeDiacritics(str) {
13764           return str.replace(/[^\u0000-\u007e]/g, function(c) {
13765             return diacriticsMap[c] || c;
13766           });
13767         }
13768
13769         var replacementList_1 = replacementList;
13770         var diacriticsMap_1 = diacriticsMap;
13771
13772         var diacritics = {
13773                 remove: remove$1,
13774                 replacementList: replacementList_1,
13775                 diacriticsMap: diacriticsMap_1
13776         };
13777
13778         var isArabic_1 = createCommonjsModule(function (module, exports) {
13779         Object.defineProperty(exports, "__esModule", { value: true });
13780         const arabicBlocks = [
13781             [0x0600, 0x06FF],
13782             [0x0750, 0x077F],
13783             [0x08A0, 0x08FF],
13784             [0xFB50, 0xFDFF],
13785             [0xFE70, 0xFEFF],
13786             [0x10E60, 0x10E7F],
13787             [0x1EC70, 0x1ECBF],
13788             [0x1EE00, 0x1EEFF] // Mathematical Alphabetic symbols https://www.unicode.org/charts/PDF/U1EE00.pdf
13789         ];
13790         function isArabic(char) {
13791             if (char.length > 1) {
13792                 // allow the newer chars?
13793                 throw new Error('isArabic works on only one-character strings');
13794             }
13795             let code = char.charCodeAt(0);
13796             for (let i = 0; i < arabicBlocks.length; i++) {
13797                 let block = arabicBlocks[i];
13798                 if (code >= block[0] && code <= block[1]) {
13799                     return true;
13800                 }
13801             }
13802             return false;
13803         }
13804         exports.isArabic = isArabic;
13805         function isMath(char) {
13806             if (char.length > 2) {
13807                 // allow the newer chars?
13808                 throw new Error('isMath works on only one-character strings');
13809             }
13810             let code = char.charCodeAt(0);
13811             return ((code >= 0x660 && code <= 0x66C) || (code >= 0x6F0 && code <= 0x6F9));
13812         }
13813         exports.isMath = isMath;
13814         });
13815
13816         var unicodeArabic = createCommonjsModule(function (module, exports) {
13817         Object.defineProperty(exports, "__esModule", { value: true });
13818         const arabicReference = {
13819             "alef": {
13820                 "normal": [
13821                     "\u0627"
13822                 ],
13823                 "madda_above": {
13824                     "normal": [
13825                         "\u0627\u0653",
13826                         "\u0622"
13827                     ],
13828                     "isolated": "\uFE81",
13829                     "final": "\uFE82"
13830                 },
13831                 "hamza_above": {
13832                     "normal": [
13833                         "\u0627\u0654",
13834                         "\u0623"
13835                     ],
13836                     "isolated": "\uFE83",
13837                     "final": "\uFE84"
13838                 },
13839                 "hamza_below": {
13840                     "normal": [
13841                         "\u0627\u0655",
13842                         "\u0625"
13843                     ],
13844                     "isolated": "\uFE87",
13845                     "final": "\uFE88"
13846                 },
13847                 "wasla": {
13848                     "normal": "\u0671",
13849                     "isolated": "\uFB50",
13850                     "final": "\uFB51"
13851                 },
13852                 "wavy_hamza_above": [
13853                     "\u0672"
13854                 ],
13855                 "wavy_hamza_below": [
13856                     "\u0627\u065F",
13857                     "\u0673"
13858                 ],
13859                 "high_hamza": [
13860                     "\u0675",
13861                     "\u0627\u0674"
13862                 ],
13863                 "indic_two_above": [
13864                     "\u0773"
13865                 ],
13866                 "indic_three_above": [
13867                     "\u0774"
13868                 ],
13869                 "fathatan": {
13870                     "normal": [
13871                         "\u0627\u064B"
13872                     ],
13873                     "final": "\uFD3C",
13874                     "isolated": "\uFD3D"
13875                 },
13876                 "isolated": "\uFE8D",
13877                 "final": "\uFE8E"
13878             },
13879             "beh": {
13880                 "normal": [
13881                     "\u0628"
13882                 ],
13883                 "dotless": [
13884                     "\u066E"
13885                 ],
13886                 "three_dots_horizontally_below": [
13887                     "\u0750"
13888                 ],
13889                 "dot_below_three_dots_above": [
13890                     "\u0751"
13891                 ],
13892                 "three_dots_pointing_upwards_below": [
13893                     "\u0752"
13894                 ],
13895                 "three_dots_pointing_upwards_below_two_dots_above": [
13896                     "\u0753"
13897                 ],
13898                 "two_dots_below_dot_above": [
13899                     "\u0754"
13900                 ],
13901                 "inverted_small_v_below": [
13902                     "\u0755"
13903                 ],
13904                 "small_v": [
13905                     "\u0756"
13906                 ],
13907                 "small_v_below": [
13908                     "\u08A0"
13909                 ],
13910                 "hamza_above": [
13911                     "\u08A1"
13912                 ],
13913                 "small_meem_above": [
13914                     "\u08B6"
13915                 ],
13916                 "isolated": "\uFE8F",
13917                 "final": "\uFE90",
13918                 "initial": "\uFE91",
13919                 "medial": "\uFE92"
13920             },
13921             "teh marbuta": {
13922                 "normal": [
13923                     "\u0629"
13924                 ],
13925                 "isolated": "\uFE93",
13926                 "final": "\uFE94"
13927             },
13928             "teh": {
13929                 "normal": [
13930                     "\u062A"
13931                 ],
13932                 "ring": [
13933                     "\u067C"
13934                 ],
13935                 "three_dots_above_downwards": [
13936                     "\u067D"
13937                 ],
13938                 "small_teh_above": [
13939                     "\u08B8"
13940                 ],
13941                 "isolated": "\uFE95",
13942                 "final": "\uFE96",
13943                 "initial": "\uFE97",
13944                 "medial": "\uFE98"
13945             },
13946             "theh": {
13947                 "normal": [
13948                     "\u062B"
13949                 ],
13950                 "isolated": "\uFE99",
13951                 "final": "\uFE9A",
13952                 "initial": "\uFE9B",
13953                 "medial": "\uFE9C"
13954             },
13955             "jeem": {
13956                 "normal": [
13957                     "\u062C"
13958                 ],
13959                 "two_dots_above": [
13960                     "\u08A2"
13961                 ],
13962                 "isolated": "\uFE9D",
13963                 "final": "\uFE9E",
13964                 "initial": "\uFE9F",
13965                 "medial": "\uFEA0"
13966             },
13967             "hah": {
13968                 "normal": [
13969                     "\u062D"
13970                 ],
13971                 "hamza_above": [
13972                     "\u0681"
13973                 ],
13974                 "two_dots_vertical_above": [
13975                     "\u0682"
13976                 ],
13977                 "three_dots_above": [
13978                     "\u0685"
13979                 ],
13980                 "two_dots_above": [
13981                     "\u0757"
13982                 ],
13983                 "three_dots_pointing_upwards_below": [
13984                     "\u0758"
13985                 ],
13986                 "small_tah_below": [
13987                     "\u076E"
13988                 ],
13989                 "small_tah_two_dots": [
13990                     "\u076F"
13991                 ],
13992                 "small_tah_above": [
13993                     "\u0772"
13994                 ],
13995                 "indic_four_below": [
13996                     "\u077C"
13997                 ],
13998                 "isolated": "\uFEA1",
13999                 "final": "\uFEA2",
14000                 "initial": "\uFEA3",
14001                 "medial": "\uFEA4"
14002             },
14003             "khah": {
14004                 "normal": [
14005                     "\u062E"
14006                 ],
14007                 "isolated": "\uFEA5",
14008                 "final": "\uFEA6",
14009                 "initial": "\uFEA7",
14010                 "medial": "\uFEA8"
14011             },
14012             "dal": {
14013                 "normal": [
14014                     "\u062F"
14015                 ],
14016                 "ring": [
14017                     "\u0689"
14018                 ],
14019                 "dot_below": [
14020                     "\u068A"
14021                 ],
14022                 "dot_below_small_tah": [
14023                     "\u068B"
14024                 ],
14025                 "three_dots_above_downwards": [
14026                     "\u068F"
14027                 ],
14028                 "four_dots_above": [
14029                     "\u0690"
14030                 ],
14031                 "inverted_v": [
14032                     "\u06EE"
14033                 ],
14034                 "two_dots_vertically_below_small_tah": [
14035                     "\u0759"
14036                 ],
14037                 "inverted_small_v_below": [
14038                     "\u075A"
14039                 ],
14040                 "three_dots_below": [
14041                     "\u08AE"
14042                 ],
14043                 "isolated": "\uFEA9",
14044                 "final": "\uFEAA"
14045             },
14046             "thal": {
14047                 "normal": [
14048                     "\u0630"
14049                 ],
14050                 "isolated": "\uFEAB",
14051                 "final": "\uFEAC"
14052             },
14053             "reh": {
14054                 "normal": [
14055                     "\u0631"
14056                 ],
14057                 "small_v": [
14058                     "\u0692"
14059                 ],
14060                 "ring": [
14061                     "\u0693"
14062                 ],
14063                 "dot_below": [
14064                     "\u0694"
14065                 ],
14066                 "small_v_below": [
14067                     "\u0695"
14068                 ],
14069                 "dot_below_dot_above": [
14070                     "\u0696"
14071                 ],
14072                 "two_dots_above": [
14073                     "\u0697"
14074                 ],
14075                 "four_dots_above": [
14076                     "\u0699"
14077                 ],
14078                 "inverted_v": [
14079                     "\u06EF"
14080                 ],
14081                 "stroke": [
14082                     "\u075B"
14083                 ],
14084                 "two_dots_vertically_above": [
14085                     "\u076B"
14086                 ],
14087                 "hamza_above": [
14088                     "\u076C"
14089                 ],
14090                 "small_tah_two_dots": [
14091                     "\u0771"
14092                 ],
14093                 "loop": [
14094                     "\u08AA"
14095                 ],
14096                 "small_noon_above": [
14097                     "\u08B9"
14098                 ],
14099                 "isolated": "\uFEAD",
14100                 "final": "\uFEAE"
14101             },
14102             "zain": {
14103                 "normal": [
14104                     "\u0632"
14105                 ],
14106                 "inverted_v_above": [
14107                     "\u08B2"
14108                 ],
14109                 "isolated": "\uFEAF",
14110                 "final": "\uFEB0"
14111             },
14112             "seen": {
14113                 "normal": [
14114                     "\u0633"
14115                 ],
14116                 "dot_below_dot_above": [
14117                     "\u069A"
14118                 ],
14119                 "three_dots_below": [
14120                     "\u069B"
14121                 ],
14122                 "three_dots_below_three_dots_above": [
14123                     "\u069C"
14124                 ],
14125                 "four_dots_above": [
14126                     "\u075C"
14127                 ],
14128                 "two_dots_vertically_above": [
14129                     "\u076D"
14130                 ],
14131                 "small_tah_two_dots": [
14132                     "\u0770"
14133                 ],
14134                 "indic_four_above": [
14135                     "\u077D"
14136                 ],
14137                 "inverted_v": [
14138                     "\u077E"
14139                 ],
14140                 "isolated": "\uFEB1",
14141                 "final": "\uFEB2",
14142                 "initial": "\uFEB3",
14143                 "medial": "\uFEB4"
14144             },
14145             "sheen": {
14146                 "normal": [
14147                     "\u0634"
14148                 ],
14149                 "dot_below": [
14150                     "\u06FA"
14151                 ],
14152                 "isolated": "\uFEB5",
14153                 "final": "\uFEB6",
14154                 "initial": "\uFEB7",
14155                 "medial": "\uFEB8"
14156             },
14157             "sad": {
14158                 "normal": [
14159                     "\u0635"
14160                 ],
14161                 "two_dots_below": [
14162                     "\u069D"
14163                 ],
14164                 "three_dots_above": [
14165                     "\u069E"
14166                 ],
14167                 "three_dots_below": [
14168                     "\u08AF"
14169                 ],
14170                 "isolated": "\uFEB9",
14171                 "final": "\uFEBA",
14172                 "initial": "\uFEBB",
14173                 "medial": "\uFEBC"
14174             },
14175             "dad": {
14176                 "normal": [
14177                     "\u0636"
14178                 ],
14179                 "dot_below": [
14180                     "\u06FB"
14181                 ],
14182                 "isolated": "\uFEBD",
14183                 "final": "\uFEBE",
14184                 "initial": "\uFEBF",
14185                 "medial": "\uFEC0"
14186             },
14187             "tah": {
14188                 "normal": [
14189                     "\u0637"
14190                 ],
14191                 "three_dots_above": [
14192                     "\u069F"
14193                 ],
14194                 "two_dots_above": [
14195                     "\u08A3"
14196                 ],
14197                 "isolated": "\uFEC1",
14198                 "final": "\uFEC2",
14199                 "initial": "\uFEC3",
14200                 "medial": "\uFEC4"
14201             },
14202             "zah": {
14203                 "normal": [
14204                     "\u0638"
14205                 ],
14206                 "isolated": "\uFEC5",
14207                 "final": "\uFEC6",
14208                 "initial": "\uFEC7",
14209                 "medial": "\uFEC8"
14210             },
14211             "ain": {
14212                 "normal": [
14213                     "\u0639"
14214                 ],
14215                 "three_dots_above": [
14216                     "\u06A0"
14217                 ],
14218                 "two_dots_above": [
14219                     "\u075D"
14220                 ],
14221                 "three_dots_pointing_downwards_above": [
14222                     "\u075E"
14223                 ],
14224                 "two_dots_vertically_above": [
14225                     "\u075F"
14226                 ],
14227                 "three_dots_below": [
14228                     "\u08B3"
14229                 ],
14230                 "isolated": "\uFEC9",
14231                 "final": "\uFECA",
14232                 "initial": "\uFECB",
14233                 "medial": "\uFECC"
14234             },
14235             "ghain": {
14236                 "normal": [
14237                     "\u063A"
14238                 ],
14239                 "dot_below": [
14240                     "\u06FC"
14241                 ],
14242                 "isolated": "\uFECD",
14243                 "final": "\uFECE",
14244                 "initial": "\uFECF",
14245                 "medial": "\uFED0"
14246             },
14247             "feh": {
14248                 "normal": [
14249                     "\u0641"
14250                 ],
14251                 "dotless": [
14252                     "\u06A1"
14253                 ],
14254                 "dot_moved_below": [
14255                     "\u06A2"
14256                 ],
14257                 "dot_below": [
14258                     "\u06A3"
14259                 ],
14260                 "three_dots_below": [
14261                     "\u06A5"
14262                 ],
14263                 "two_dots_below": [
14264                     "\u0760"
14265                 ],
14266                 "three_dots_pointing_upwards_below": [
14267                     "\u0761"
14268                 ],
14269                 "dot_below_three_dots_above": [
14270                     "\u08A4"
14271                 ],
14272                 "isolated": "\uFED1",
14273                 "final": "\uFED2",
14274                 "initial": "\uFED3",
14275                 "medial": "\uFED4"
14276             },
14277             "qaf": {
14278                 "normal": [
14279                     "\u0642"
14280                 ],
14281                 "dotless": [
14282                     "\u066F"
14283                 ],
14284                 "dot_above": [
14285                     "\u06A7"
14286                 ],
14287                 "three_dots_above": [
14288                     "\u06A8"
14289                 ],
14290                 "dot_below": [
14291                     "\u08A5"
14292                 ],
14293                 "isolated": "\uFED5",
14294                 "final": "\uFED6",
14295                 "initial": "\uFED7",
14296                 "medial": "\uFED8"
14297             },
14298             "kaf": {
14299                 "normal": [
14300                     "\u0643"
14301                 ],
14302                 "swash": [
14303                     "\u06AA"
14304                 ],
14305                 "ring": [
14306                     "\u06AB"
14307                 ],
14308                 "dot_above": [
14309                     "\u06AC"
14310                 ],
14311                 "three_dots_below": [
14312                     "\u06AE"
14313                 ],
14314                 "two_dots_above": [
14315                     "\u077F"
14316                 ],
14317                 "dot_below": [
14318                     "\u08B4"
14319                 ],
14320                 "isolated": "\uFED9",
14321                 "final": "\uFEDA",
14322                 "initial": "\uFEDB",
14323                 "medial": "\uFEDC"
14324             },
14325             "lam": {
14326                 "normal": [
14327                     "\u0644"
14328                 ],
14329                 "small_v": [
14330                     "\u06B5"
14331                 ],
14332                 "dot_above": [
14333                     "\u06B6"
14334                 ],
14335                 "three_dots_above": [
14336                     "\u06B7"
14337                 ],
14338                 "three_dots_below": [
14339                     "\u06B8"
14340                 ],
14341                 "bar": [
14342                     "\u076A"
14343                 ],
14344                 "double_bar": [
14345                     "\u08A6"
14346                 ],
14347                 "isolated": "\uFEDD",
14348                 "final": "\uFEDE",
14349                 "initial": "\uFEDF",
14350                 "medial": "\uFEE0"
14351             },
14352             "meem": {
14353                 "normal": [
14354                     "\u0645"
14355                 ],
14356                 "dot_above": [
14357                     "\u0765"
14358                 ],
14359                 "dot_below": [
14360                     "\u0766"
14361                 ],
14362                 "three_dots_above": [
14363                     "\u08A7"
14364                 ],
14365                 "isolated": "\uFEE1",
14366                 "final": "\uFEE2",
14367                 "initial": "\uFEE3",
14368                 "medial": "\uFEE4"
14369             },
14370             "noon": {
14371                 "normal": [
14372                     "\u0646"
14373                 ],
14374                 "dot_below": [
14375                     "\u06B9"
14376                 ],
14377                 "ring": [
14378                     "\u06BC"
14379                 ],
14380                 "three_dots_above": [
14381                     "\u06BD"
14382                 ],
14383                 "two_dots_below": [
14384                     "\u0767"
14385                 ],
14386                 "small_tah": [
14387                     "\u0768"
14388                 ],
14389                 "small_v": [
14390                     "\u0769"
14391                 ],
14392                 "isolated": "\uFEE5",
14393                 "final": "\uFEE6",
14394                 "initial": "\uFEE7",
14395                 "medial": "\uFEE8"
14396             },
14397             "heh": {
14398                 "normal": [
14399                     "\u0647"
14400                 ],
14401                 "isolated": "\uFEE9",
14402                 "final": "\uFEEA",
14403                 "initial": "\uFEEB",
14404                 "medial": "\uFEEC"
14405             },
14406             "waw": {
14407                 "normal": [
14408                     "\u0648"
14409                 ],
14410                 "hamza_above": {
14411                     "normal": [
14412                         "\u0624",
14413                         "\u0648\u0654"
14414                     ],
14415                     "isolated": "\uFE85",
14416                     "final": "\uFE86"
14417                 },
14418                 "high_hamza": [
14419                     "\u0676",
14420                     "\u0648\u0674"
14421                 ],
14422                 "ring": [
14423                     "\u06C4"
14424                 ],
14425                 "two_dots_above": [
14426                     "\u06CA"
14427                 ],
14428                 "dot_above": [
14429                     "\u06CF"
14430                 ],
14431                 "indic_two_above": [
14432                     "\u0778"
14433                 ],
14434                 "indic_three_above": [
14435                     "\u0779"
14436                 ],
14437                 "dot_within": [
14438                     "\u08AB"
14439                 ],
14440                 "isolated": "\uFEED",
14441                 "final": "\uFEEE"
14442             },
14443             "alef_maksura": {
14444                 "normal": [
14445                     "\u0649"
14446                 ],
14447                 "hamza_above": [
14448                     "\u0626",
14449                     "\u064A\u0654"
14450                 ],
14451                 "initial": "\uFBE8",
14452                 "medial": "\uFBE9",
14453                 "isolated": "\uFEEF",
14454                 "final": "\uFEF0"
14455             },
14456             "yeh": {
14457                 "normal": [
14458                     "\u064A"
14459                 ],
14460                 "hamza_above": {
14461                     "normal": [
14462                         "\u0626",
14463                         "\u0649\u0654"
14464                     ],
14465                     "isolated": "\uFE89",
14466                     "final": "\uFE8A",
14467                     "initial": "\uFE8B",
14468                     "medial": "\uFE8C"
14469                 },
14470                 "two_dots_below_hamza_above": [
14471                     "\u08A8"
14472                 ],
14473                 "high_hamza": [
14474                     "\u0678",
14475                     "\u064A\u0674"
14476                 ],
14477                 "tail": [
14478                     "\u06CD"
14479                 ],
14480                 "small_v": [
14481                     "\u06CE"
14482                 ],
14483                 "three_dots_below": [
14484                     "\u06D1"
14485                 ],
14486                 "two_dots_below_dot_above": [
14487                     "\u08A9"
14488                 ],
14489                 "two_dots_below_small_noon_above": [
14490                     "\u08BA"
14491                 ],
14492                 "isolated": "\uFEF1",
14493                 "final": "\uFEF2",
14494                 "initial": "\uFEF3",
14495                 "medial": "\uFEF4"
14496             },
14497             "tteh": {
14498                 "normal": [
14499                     "\u0679"
14500                 ],
14501                 "isolated": "\uFB66",
14502                 "final": "\uFB67",
14503                 "initial": "\uFB68",
14504                 "medial": "\uFB69"
14505             },
14506             "tteheh": {
14507                 "normal": [
14508                     "\u067A"
14509                 ],
14510                 "isolated": "\uFB5E",
14511                 "final": "\uFB5F",
14512                 "initial": "\uFB60",
14513                 "medial": "\uFB61"
14514             },
14515             "beeh": {
14516                 "normal": [
14517                     "\u067B"
14518                 ],
14519                 "isolated": "\uFB52",
14520                 "final": "\uFB53",
14521                 "initial": "\uFB54",
14522                 "medial": "\uFB55"
14523             },
14524             "peh": {
14525                 "normal": [
14526                     "\u067E"
14527                 ],
14528                 "small_meem_above": [
14529                     "\u08B7"
14530                 ],
14531                 "isolated": "\uFB56",
14532                 "final": "\uFB57",
14533                 "initial": "\uFB58",
14534                 "medial": "\uFB59"
14535             },
14536             "teheh": {
14537                 "normal": [
14538                     "\u067F"
14539                 ],
14540                 "isolated": "\uFB62",
14541                 "final": "\uFB63",
14542                 "initial": "\uFB64",
14543                 "medial": "\uFB65"
14544             },
14545             "beheh": {
14546                 "normal": [
14547                     "\u0680"
14548                 ],
14549                 "isolated": "\uFB5A",
14550                 "final": "\uFB5B",
14551                 "initial": "\uFB5C",
14552                 "medial": "\uFB5D"
14553             },
14554             "nyeh": {
14555                 "normal": [
14556                     "\u0683"
14557                 ],
14558                 "isolated": "\uFB76",
14559                 "final": "\uFB77",
14560                 "initial": "\uFB78",
14561                 "medial": "\uFB79"
14562             },
14563             "dyeh": {
14564                 "normal": [
14565                     "\u0684"
14566                 ],
14567                 "isolated": "\uFB72",
14568                 "final": "\uFB73",
14569                 "initial": "\uFB74",
14570                 "medial": "\uFB75"
14571             },
14572             "tcheh": {
14573                 "normal": [
14574                     "\u0686"
14575                 ],
14576                 "dot_above": [
14577                     "\u06BF"
14578                 ],
14579                 "isolated": "\uFB7A",
14580                 "final": "\uFB7B",
14581                 "initial": "\uFB7C",
14582                 "medial": "\uFB7D"
14583             },
14584             "tcheheh": {
14585                 "normal": [
14586                     "\u0687"
14587                 ],
14588                 "isolated": "\uFB7E",
14589                 "final": "\uFB7F",
14590                 "initial": "\uFB80",
14591                 "medial": "\uFB81"
14592             },
14593             "ddal": {
14594                 "normal": [
14595                     "\u0688"
14596                 ],
14597                 "isolated": "\uFB88",
14598                 "final": "\uFB89"
14599             },
14600             "dahal": {
14601                 "normal": [
14602                     "\u068C"
14603                 ],
14604                 "isolated": "\uFB84",
14605                 "final": "\uFB85"
14606             },
14607             "ddahal": {
14608                 "normal": [
14609                     "\u068D"
14610                 ],
14611                 "isolated": "\uFB82",
14612                 "final": "\uFB83"
14613             },
14614             "dul": {
14615                 "normal": [
14616                     "\u068F",
14617                     "\u068E"
14618                 ],
14619                 "isolated": "\uFB86",
14620                 "final": "\uFB87"
14621             },
14622             "rreh": {
14623                 "normal": [
14624                     "\u0691"
14625                 ],
14626                 "isolated": "\uFB8C",
14627                 "final": "\uFB8D"
14628             },
14629             "jeh": {
14630                 "normal": [
14631                     "\u0698"
14632                 ],
14633                 "isolated": "\uFB8A",
14634                 "final": "\uFB8B"
14635             },
14636             "veh": {
14637                 "normal": [
14638                     "\u06A4"
14639                 ],
14640                 "isolated": "\uFB6A",
14641                 "final": "\uFB6B",
14642                 "initial": "\uFB6C",
14643                 "medial": "\uFB6D"
14644             },
14645             "peheh": {
14646                 "normal": [
14647                     "\u06A6"
14648                 ],
14649                 "isolated": "\uFB6E",
14650                 "final": "\uFB6F",
14651                 "initial": "\uFB70",
14652                 "medial": "\uFB71"
14653             },
14654             "keheh": {
14655                 "normal": [
14656                     "\u06A9"
14657                 ],
14658                 "dot_above": [
14659                     "\u0762"
14660                 ],
14661                 "three_dots_above": [
14662                     "\u0763"
14663                 ],
14664                 "three_dots_pointing_upwards_below": [
14665                     "\u0764"
14666                 ],
14667                 "isolated": "\uFB8E",
14668                 "final": "\uFB8F",
14669                 "initial": "\uFB90",
14670                 "medial": "\uFB91"
14671             },
14672             "ng": {
14673                 "normal": [
14674                     "\u06AD"
14675                 ],
14676                 "isolated": "\uFBD3",
14677                 "final": "\uFBD4",
14678                 "initial": "\uFBD5",
14679                 "medial": "\uFBD6"
14680             },
14681             "gaf": {
14682                 "normal": [
14683                     "\u06AF"
14684                 ],
14685                 "ring": [
14686                     "\u06B0"
14687                 ],
14688                 "two_dots_below": [
14689                     "\u06B2"
14690                 ],
14691                 "three_dots_above": [
14692                     "\u06B4"
14693                 ],
14694                 "inverted_stroke": [
14695                     "\u08B0"
14696                 ],
14697                 "isolated": "\uFB92",
14698                 "final": "\uFB93",
14699                 "initial": "\uFB94",
14700                 "medial": "\uFB95"
14701             },
14702             "ngoeh": {
14703                 "normal": [
14704                     "\u06B1"
14705                 ],
14706                 "isolated": "\uFB9A",
14707                 "final": "\uFB9B",
14708                 "initial": "\uFB9C",
14709                 "medial": "\uFB9D"
14710             },
14711             "gueh": {
14712                 "normal": [
14713                     "\u06B3"
14714                 ],
14715                 "isolated": "\uFB96",
14716                 "final": "\uFB97",
14717                 "initial": "\uFB98",
14718                 "medial": "\uFB99"
14719             },
14720             "noon ghunna": {
14721                 "normal": [
14722                     "\u06BA"
14723                 ],
14724                 "isolated": "\uFB9E",
14725                 "final": "\uFB9F"
14726             },
14727             "rnoon": {
14728                 "normal": [
14729                     "\u06BB"
14730                 ],
14731                 "isolated": "\uFBA0",
14732                 "final": "\uFBA1",
14733                 "initial": "\uFBA2",
14734                 "medial": "\uFBA3"
14735             },
14736             "heh doachashmee": {
14737                 "normal": [
14738                     "\u06BE"
14739                 ],
14740                 "isolated": "\uFBAA",
14741                 "final": "\uFBAB",
14742                 "initial": "\uFBAC",
14743                 "medial": "\uFBAD"
14744             },
14745             "heh goal": {
14746                 "normal": [
14747                     "\u06C1"
14748                 ],
14749                 "hamza_above": [
14750                     "\u06C1\u0654",
14751                     "\u06C2"
14752                 ],
14753                 "isolated": "\uFBA6",
14754                 "final": "\uFBA7",
14755                 "initial": "\uFBA8",
14756                 "medial": "\uFBA9"
14757             },
14758             "teh marbuta goal": {
14759                 "normal": [
14760                     "\u06C3"
14761                 ]
14762             },
14763             "kirghiz oe": {
14764                 "normal": [
14765                     "\u06C5"
14766                 ],
14767                 "isolated": "\uFBE0",
14768                 "final": "\uFBE1"
14769             },
14770             "oe": {
14771                 "normal": [
14772                     "\u06C6"
14773                 ],
14774                 "isolated": "\uFBD9",
14775                 "final": "\uFBDA"
14776             },
14777             "u": {
14778                 "normal": [
14779                     "\u06C7"
14780                 ],
14781                 "hamza_above": {
14782                     "normal": [
14783                         "\u0677",
14784                         "\u06C7\u0674"
14785                     ],
14786                     "isolated": "\uFBDD"
14787                 },
14788                 "isolated": "\uFBD7",
14789                 "final": "\uFBD8"
14790             },
14791             "yu": {
14792                 "normal": [
14793                     "\u06C8"
14794                 ],
14795                 "isolated": "\uFBDB",
14796                 "final": "\uFBDC"
14797             },
14798             "kirghiz yu": {
14799                 "normal": [
14800                     "\u06C9"
14801                 ],
14802                 "isolated": "\uFBE2",
14803                 "final": "\uFBE3"
14804             },
14805             "ve": {
14806                 "normal": [
14807                     "\u06CB"
14808                 ],
14809                 "isolated": "\uFBDE",
14810                 "final": "\uFBDF"
14811             },
14812             "farsi yeh": {
14813                 "normal": [
14814                     "\u06CC"
14815                 ],
14816                 "indic_two_above": [
14817                     "\u0775"
14818                 ],
14819                 "indic_three_above": [
14820                     "\u0776"
14821                 ],
14822                 "indic_four_above": [
14823                     "\u0777"
14824                 ],
14825                 "isolated": "\uFBFC",
14826                 "final": "\uFBFD",
14827                 "initial": "\uFBFE",
14828                 "medial": "\uFBFF"
14829             },
14830             "e": {
14831                 "normal": [
14832                     "\u06D0"
14833                 ],
14834                 "isolated": "\uFBE4",
14835                 "final": "\uFBE5",
14836                 "initial": "\uFBE6",
14837                 "medial": "\uFBE7"
14838             },
14839             "yeh barree": {
14840                 "normal": [
14841                     "\u06D2"
14842                 ],
14843                 "hamza_above": {
14844                     "normal": [
14845                         "\u06D2\u0654",
14846                         "\u06D3"
14847                     ],
14848                     "isolated": "\uFBB0",
14849                     "final": "\uFBB1"
14850                 },
14851                 "indic_two_above": [
14852                     "\u077A"
14853                 ],
14854                 "indic_three_above": [
14855                     "\u077B"
14856                 ],
14857                 "isolated": "\uFBAE",
14858                 "final": "\uFBAF"
14859             },
14860             "ae": {
14861                 "normal": [
14862                     "\u06D5"
14863                 ],
14864                 "isolated": "\u06D5",
14865                 "final": "\uFEEA",
14866                 "yeh_above": {
14867                     "normal": [
14868                         "\u06C0",
14869                         "\u06D5\u0654"
14870                     ],
14871                     "isolated": "\uFBA4",
14872                     "final": "\uFBA5"
14873                 }
14874             },
14875             "rohingya yeh": {
14876                 "normal": [
14877                     "\u08AC"
14878                 ]
14879             },
14880             "low alef": {
14881                 "normal": [
14882                     "\u08AD"
14883                 ]
14884             },
14885             "straight waw": {
14886                 "normal": [
14887                     "\u08B1"
14888                 ]
14889             },
14890             "african feh": {
14891                 "normal": [
14892                     "\u08BB"
14893                 ]
14894             },
14895             "african qaf": {
14896                 "normal": [
14897                     "\u08BC"
14898                 ]
14899             },
14900             "african noon": {
14901                 "normal": [
14902                     "\u08BD"
14903                 ]
14904             }
14905         };
14906         exports.default = arabicReference;
14907         });
14908
14909         var unicodeLigatures = createCommonjsModule(function (module, exports) {
14910         Object.defineProperty(exports, "__esModule", { value: true });
14911         const ligatureReference = {
14912             "\u0626\u0627": {
14913                 "isolated": "\uFBEA",
14914                 "final": "\uFBEB"
14915             },
14916             "\u0626\u06D5": {
14917                 "isolated": "\uFBEC",
14918                 "final": "\uFBED"
14919             },
14920             "\u0626\u0648": {
14921                 "isolated": "\uFBEE",
14922                 "final": "\uFBEF"
14923             },
14924             "\u0626\u06C7": {
14925                 "isolated": "\uFBF0",
14926                 "final": "\uFBF1"
14927             },
14928             "\u0626\u06C6": {
14929                 "isolated": "\uFBF2",
14930                 "final": "\uFBF3"
14931             },
14932             "\u0626\u06C8": {
14933                 "isolated": "\uFBF4",
14934                 "final": "\uFBF5"
14935             },
14936             "\u0626\u06D0": {
14937                 "isolated": "\uFBF6",
14938                 "final": "\uFBF7",
14939                 "initial": "\uFBF8"
14940             },
14941             "\u0626\u0649": {
14942                 "uighur_kirghiz": {
14943                     "isolated": "\uFBF9",
14944                     "final": "\uFBFA",
14945                     "initial": "\uFBFB"
14946                 },
14947                 "isolated": "\uFC03",
14948                 "final": "\uFC68"
14949             },
14950             "\u0626\u062C": {
14951                 "isolated": "\uFC00",
14952                 "initial": "\uFC97"
14953             },
14954             "\u0626\u062D": {
14955                 "isolated": "\uFC01",
14956                 "initial": "\uFC98"
14957             },
14958             "\u0626\u0645": {
14959                 "isolated": "\uFC02",
14960                 "final": "\uFC66",
14961                 "initial": "\uFC9A",
14962                 "medial": "\uFCDF"
14963             },
14964             "\u0626\u064A": {
14965                 "isolated": "\uFC04",
14966                 "final": "\uFC69"
14967             },
14968             "\u0628\u062C": {
14969                 "isolated": "\uFC05",
14970                 "initial": "\uFC9C"
14971             },
14972             "\u0628\u062D": {
14973                 "isolated": "\uFC06",
14974                 "initial": "\uFC9D"
14975             },
14976             "\u0628\u062E": {
14977                 "isolated": "\uFC07",
14978                 "initial": "\uFC9E"
14979             },
14980             "\u0628\u0645": {
14981                 "isolated": "\uFC08",
14982                 "final": "\uFC6C",
14983                 "initial": "\uFC9F",
14984                 "medial": "\uFCE1"
14985             },
14986             "\u0628\u0649": {
14987                 "isolated": "\uFC09",
14988                 "final": "\uFC6E"
14989             },
14990             "\u0628\u064A": {
14991                 "isolated": "\uFC0A",
14992                 "final": "\uFC6F"
14993             },
14994             "\u062A\u062C": {
14995                 "isolated": "\uFC0B",
14996                 "initial": "\uFCA1"
14997             },
14998             "\u062A\u062D": {
14999                 "isolated": "\uFC0C",
15000                 "initial": "\uFCA2"
15001             },
15002             "\u062A\u062E": {
15003                 "isolated": "\uFC0D",
15004                 "initial": "\uFCA3"
15005             },
15006             "\u062A\u0645": {
15007                 "isolated": "\uFC0E",
15008                 "final": "\uFC72",
15009                 "initial": "\uFCA4",
15010                 "medial": "\uFCE3"
15011             },
15012             "\u062A\u0649": {
15013                 "isolated": "\uFC0F",
15014                 "final": "\uFC74"
15015             },
15016             "\u062A\u064A": {
15017                 "isolated": "\uFC10",
15018                 "final": "\uFC75"
15019             },
15020             "\u062B\u062C": {
15021                 "isolated": "\uFC11"
15022             },
15023             "\u062B\u0645": {
15024                 "isolated": "\uFC12",
15025                 "final": "\uFC78",
15026                 "initial": "\uFCA6",
15027                 "medial": "\uFCE5"
15028             },
15029             "\u062B\u0649": {
15030                 "isolated": "\uFC13",
15031                 "final": "\uFC7A"
15032             },
15033             "\u062B\u0648": {
15034                 "isolated": "\uFC14"
15035             },
15036             "\u062C\u062D": {
15037                 "isolated": "\uFC15",
15038                 "initial": "\uFCA7"
15039             },
15040             "\u062C\u0645": {
15041                 "isolated": "\uFC16",
15042                 "initial": "\uFCA8"
15043             },
15044             "\u062D\u062C": {
15045                 "isolated": "\uFC17",
15046                 "initial": "\uFCA9"
15047             },
15048             "\u062D\u0645": {
15049                 "isolated": "\uFC18",
15050                 "initial": "\uFCAA"
15051             },
15052             "\u062E\u062C": {
15053                 "isolated": "\uFC19",
15054                 "initial": "\uFCAB"
15055             },
15056             "\u062E\u062D": {
15057                 "isolated": "\uFC1A"
15058             },
15059             "\u062E\u0645": {
15060                 "isolated": "\uFC1B",
15061                 "initial": "\uFCAC"
15062             },
15063             "\u0633\u062C": {
15064                 "isolated": "\uFC1C",
15065                 "initial": "\uFCAD",
15066                 "medial": "\uFD34"
15067             },
15068             "\u0633\u062D": {
15069                 "isolated": "\uFC1D",
15070                 "initial": "\uFCAE",
15071                 "medial": "\uFD35"
15072             },
15073             "\u0633\u062E": {
15074                 "isolated": "\uFC1E",
15075                 "initial": "\uFCAF",
15076                 "medial": "\uFD36"
15077             },
15078             "\u0633\u0645": {
15079                 "isolated": "\uFC1F",
15080                 "initial": "\uFCB0",
15081                 "medial": "\uFCE7"
15082             },
15083             "\u0635\u062D": {
15084                 "isolated": "\uFC20",
15085                 "initial": "\uFCB1"
15086             },
15087             "\u0635\u0645": {
15088                 "isolated": "\uFC21",
15089                 "initial": "\uFCB3"
15090             },
15091             "\u0636\u062C": {
15092                 "isolated": "\uFC22",
15093                 "initial": "\uFCB4"
15094             },
15095             "\u0636\u062D": {
15096                 "isolated": "\uFC23",
15097                 "initial": "\uFCB5"
15098             },
15099             "\u0636\u062E": {
15100                 "isolated": "\uFC24",
15101                 "initial": "\uFCB6"
15102             },
15103             "\u0636\u0645": {
15104                 "isolated": "\uFC25",
15105                 "initial": "\uFCB7"
15106             },
15107             "\u0637\u062D": {
15108                 "isolated": "\uFC26",
15109                 "initial": "\uFCB8"
15110             },
15111             "\u0637\u0645": {
15112                 "isolated": "\uFC27",
15113                 "initial": "\uFD33",
15114                 "medial": "\uFD3A"
15115             },
15116             "\u0638\u0645": {
15117                 "isolated": "\uFC28",
15118                 "initial": "\uFCB9",
15119                 "medial": "\uFD3B"
15120             },
15121             "\u0639\u062C": {
15122                 "isolated": "\uFC29",
15123                 "initial": "\uFCBA"
15124             },
15125             "\u0639\u0645": {
15126                 "isolated": "\uFC2A",
15127                 "initial": "\uFCBB"
15128             },
15129             "\u063A\u062C": {
15130                 "isolated": "\uFC2B",
15131                 "initial": "\uFCBC"
15132             },
15133             "\u063A\u0645": {
15134                 "isolated": "\uFC2C",
15135                 "initial": "\uFCBD"
15136             },
15137             "\u0641\u062C": {
15138                 "isolated": "\uFC2D",
15139                 "initial": "\uFCBE"
15140             },
15141             "\u0641\u062D": {
15142                 "isolated": "\uFC2E",
15143                 "initial": "\uFCBF"
15144             },
15145             "\u0641\u062E": {
15146                 "isolated": "\uFC2F",
15147                 "initial": "\uFCC0"
15148             },
15149             "\u0641\u0645": {
15150                 "isolated": "\uFC30",
15151                 "initial": "\uFCC1"
15152             },
15153             "\u0641\u0649": {
15154                 "isolated": "\uFC31",
15155                 "final": "\uFC7C"
15156             },
15157             "\u0641\u064A": {
15158                 "isolated": "\uFC32",
15159                 "final": "\uFC7D"
15160             },
15161             "\u0642\u062D": {
15162                 "isolated": "\uFC33",
15163                 "initial": "\uFCC2"
15164             },
15165             "\u0642\u0645": {
15166                 "isolated": "\uFC34",
15167                 "initial": "\uFCC3"
15168             },
15169             "\u0642\u0649": {
15170                 "isolated": "\uFC35",
15171                 "final": "\uFC7E"
15172             },
15173             "\u0642\u064A": {
15174                 "isolated": "\uFC36",
15175                 "final": "\uFC7F"
15176             },
15177             "\u0643\u0627": {
15178                 "isolated": "\uFC37",
15179                 "final": "\uFC80"
15180             },
15181             "\u0643\u062C": {
15182                 "isolated": "\uFC38",
15183                 "initial": "\uFCC4"
15184             },
15185             "\u0643\u062D": {
15186                 "isolated": "\uFC39",
15187                 "initial": "\uFCC5"
15188             },
15189             "\u0643\u062E": {
15190                 "isolated": "\uFC3A",
15191                 "initial": "\uFCC6"
15192             },
15193             "\u0643\u0644": {
15194                 "isolated": "\uFC3B",
15195                 "final": "\uFC81",
15196                 "initial": "\uFCC7",
15197                 "medial": "\uFCEB"
15198             },
15199             "\u0643\u0645": {
15200                 "isolated": "\uFC3C",
15201                 "final": "\uFC82",
15202                 "initial": "\uFCC8",
15203                 "medial": "\uFCEC"
15204             },
15205             "\u0643\u0649": {
15206                 "isolated": "\uFC3D",
15207                 "final": "\uFC83"
15208             },
15209             "\u0643\u064A": {
15210                 "isolated": "\uFC3E",
15211                 "final": "\uFC84"
15212             },
15213             "\u0644\u062C": {
15214                 "isolated": "\uFC3F",
15215                 "initial": "\uFCC9"
15216             },
15217             "\u0644\u062D": {
15218                 "isolated": "\uFC40",
15219                 "initial": "\uFCCA"
15220             },
15221             "\u0644\u062E": {
15222                 "isolated": "\uFC41",
15223                 "initial": "\uFCCB"
15224             },
15225             "\u0644\u0645": {
15226                 "isolated": "\uFC42",
15227                 "final": "\uFC85",
15228                 "initial": "\uFCCC",
15229                 "medial": "\uFCED"
15230             },
15231             "\u0644\u0649": {
15232                 "isolated": "\uFC43",
15233                 "final": "\uFC86"
15234             },
15235             "\u0644\u064A": {
15236                 "isolated": "\uFC44",
15237                 "final": "\uFC87"
15238             },
15239             "\u0645\u062C": {
15240                 "isolated": "\uFC45",
15241                 "initial": "\uFCCE"
15242             },
15243             "\u0645\u062D": {
15244                 "isolated": "\uFC46",
15245                 "initial": "\uFCCF"
15246             },
15247             "\u0645\u062E": {
15248                 "isolated": "\uFC47",
15249                 "initial": "\uFCD0"
15250             },
15251             "\u0645\u0645": {
15252                 "isolated": "\uFC48",
15253                 "final": "\uFC89",
15254                 "initial": "\uFCD1"
15255             },
15256             "\u0645\u0649": {
15257                 "isolated": "\uFC49"
15258             },
15259             "\u0645\u064A": {
15260                 "isolated": "\uFC4A"
15261             },
15262             "\u0646\u062C": {
15263                 "isolated": "\uFC4B",
15264                 "initial": "\uFCD2"
15265             },
15266             "\u0646\u062D": {
15267                 "isolated": "\uFC4C",
15268                 "initial": "\uFCD3"
15269             },
15270             "\u0646\u062E": {
15271                 "isolated": "\uFC4D",
15272                 "initial": "\uFCD4"
15273             },
15274             "\u0646\u0645": {
15275                 "isolated": "\uFC4E",
15276                 "final": "\uFC8C",
15277                 "initial": "\uFCD5",
15278                 "medial": "\uFCEE"
15279             },
15280             "\u0646\u0649": {
15281                 "isolated": "\uFC4F",
15282                 "final": "\uFC8E"
15283             },
15284             "\u0646\u064A": {
15285                 "isolated": "\uFC50",
15286                 "final": "\uFC8F"
15287             },
15288             "\u0647\u062C": {
15289                 "isolated": "\uFC51",
15290                 "initial": "\uFCD7"
15291             },
15292             "\u0647\u0645": {
15293                 "isolated": "\uFC52",
15294                 "initial": "\uFCD8"
15295             },
15296             "\u0647\u0649": {
15297                 "isolated": "\uFC53"
15298             },
15299             "\u0647\u064A": {
15300                 "isolated": "\uFC54"
15301             },
15302             "\u064A\u062C": {
15303                 "isolated": "\uFC55",
15304                 "initial": "\uFCDA"
15305             },
15306             "\u064A\u062D": {
15307                 "isolated": "\uFC56",
15308                 "initial": "\uFCDB"
15309             },
15310             "\u064A\u062E": {
15311                 "isolated": "\uFC57",
15312                 "initial": "\uFCDC"
15313             },
15314             "\u064A\u0645": {
15315                 "isolated": "\uFC58",
15316                 "final": "\uFC93",
15317                 "initial": "\uFCDD",
15318                 "medial": "\uFCF0"
15319             },
15320             "\u064A\u0649": {
15321                 "isolated": "\uFC59",
15322                 "final": "\uFC95"
15323             },
15324             "\u064A\u064A": {
15325                 "isolated": "\uFC5A",
15326                 "final": "\uFC96"
15327             },
15328             "\u0630\u0670": {
15329                 "isolated": "\uFC5B"
15330             },
15331             "\u0631\u0670": {
15332                 "isolated": "\uFC5C"
15333             },
15334             "\u0649\u0670": {
15335                 "isolated": "\uFC5D",
15336                 "final": "\uFC90"
15337             },
15338             "\u064C\u0651": {
15339                 "isolated": "\uFC5E"
15340             },
15341             "\u064D\u0651": {
15342                 "isolated": "\uFC5F"
15343             },
15344             "\u064E\u0651": {
15345                 "isolated": "\uFC60"
15346             },
15347             "\u064F\u0651": {
15348                 "isolated": "\uFC61"
15349             },
15350             "\u0650\u0651": {
15351                 "isolated": "\uFC62"
15352             },
15353             "\u0651\u0670": {
15354                 "isolated": "\uFC63"
15355             },
15356             "\u0626\u0631": {
15357                 "final": "\uFC64"
15358             },
15359             "\u0626\u0632": {
15360                 "final": "\uFC65"
15361             },
15362             "\u0626\u0646": {
15363                 "final": "\uFC67"
15364             },
15365             "\u0628\u0631": {
15366                 "final": "\uFC6A"
15367             },
15368             "\u0628\u0632": {
15369                 "final": "\uFC6B"
15370             },
15371             "\u0628\u0646": {
15372                 "final": "\uFC6D"
15373             },
15374             "\u062A\u0631": {
15375                 "final": "\uFC70"
15376             },
15377             "\u062A\u0632": {
15378                 "final": "\uFC71"
15379             },
15380             "\u062A\u0646": {
15381                 "final": "\uFC73"
15382             },
15383             "\u062B\u0631": {
15384                 "final": "\uFC76"
15385             },
15386             "\u062B\u0632": {
15387                 "final": "\uFC77"
15388             },
15389             "\u062B\u0646": {
15390                 "final": "\uFC79"
15391             },
15392             "\u062B\u064A": {
15393                 "final": "\uFC7B"
15394             },
15395             "\u0645\u0627": {
15396                 "final": "\uFC88"
15397             },
15398             "\u0646\u0631": {
15399                 "final": "\uFC8A"
15400             },
15401             "\u0646\u0632": {
15402                 "final": "\uFC8B"
15403             },
15404             "\u0646\u0646": {
15405                 "final": "\uFC8D"
15406             },
15407             "\u064A\u0631": {
15408                 "final": "\uFC91"
15409             },
15410             "\u064A\u0632": {
15411                 "final": "\uFC92"
15412             },
15413             "\u064A\u0646": {
15414                 "final": "\uFC94"
15415             },
15416             "\u0626\u062E": {
15417                 "initial": "\uFC99"
15418             },
15419             "\u0626\u0647": {
15420                 "initial": "\uFC9B",
15421                 "medial": "\uFCE0"
15422             },
15423             "\u0628\u0647": {
15424                 "initial": "\uFCA0",
15425                 "medial": "\uFCE2"
15426             },
15427             "\u062A\u0647": {
15428                 "initial": "\uFCA5",
15429                 "medial": "\uFCE4"
15430             },
15431             "\u0635\u062E": {
15432                 "initial": "\uFCB2"
15433             },
15434             "\u0644\u0647": {
15435                 "initial": "\uFCCD"
15436             },
15437             "\u0646\u0647": {
15438                 "initial": "\uFCD6",
15439                 "medial": "\uFCEF"
15440             },
15441             "\u0647\u0670": {
15442                 "initial": "\uFCD9"
15443             },
15444             "\u064A\u0647": {
15445                 "initial": "\uFCDE",
15446                 "medial": "\uFCF1"
15447             },
15448             "\u062B\u0647": {
15449                 "medial": "\uFCE6"
15450             },
15451             "\u0633\u0647": {
15452                 "medial": "\uFCE8",
15453                 "initial": "\uFD31"
15454             },
15455             "\u0634\u0645": {
15456                 "medial": "\uFCE9",
15457                 "isolated": "\uFD0C",
15458                 "final": "\uFD28",
15459                 "initial": "\uFD30"
15460             },
15461             "\u0634\u0647": {
15462                 "medial": "\uFCEA",
15463                 "initial": "\uFD32"
15464             },
15465             "\u0640\u064E\u0651": {
15466                 "medial": "\uFCF2"
15467             },
15468             "\u0640\u064F\u0651": {
15469                 "medial": "\uFCF3"
15470             },
15471             "\u0640\u0650\u0651": {
15472                 "medial": "\uFCF4"
15473             },
15474             "\u0637\u0649": {
15475                 "isolated": "\uFCF5",
15476                 "final": "\uFD11"
15477             },
15478             "\u0637\u064A": {
15479                 "isolated": "\uFCF6",
15480                 "final": "\uFD12"
15481             },
15482             "\u0639\u0649": {
15483                 "isolated": "\uFCF7",
15484                 "final": "\uFD13"
15485             },
15486             "\u0639\u064A": {
15487                 "isolated": "\uFCF8",
15488                 "final": "\uFD14"
15489             },
15490             "\u063A\u0649": {
15491                 "isolated": "\uFCF9",
15492                 "final": "\uFD15"
15493             },
15494             "\u063A\u064A": {
15495                 "isolated": "\uFCFA",
15496                 "final": "\uFD16"
15497             },
15498             "\u0633\u0649": {
15499                 "isolated": "\uFCFB"
15500             },
15501             "\u0633\u064A": {
15502                 "isolated": "\uFCFC",
15503                 "final": "\uFD18"
15504             },
15505             "\u0634\u0649": {
15506                 "isolated": "\uFCFD",
15507                 "final": "\uFD19"
15508             },
15509             "\u0634\u064A": {
15510                 "isolated": "\uFCFE",
15511                 "final": "\uFD1A"
15512             },
15513             "\u062D\u0649": {
15514                 "isolated": "\uFCFF",
15515                 "final": "\uFD1B"
15516             },
15517             "\u062D\u064A": {
15518                 "isolated": "\uFD00",
15519                 "final": "\uFD1C"
15520             },
15521             "\u062C\u0649": {
15522                 "isolated": "\uFD01",
15523                 "final": "\uFD1D"
15524             },
15525             "\u062C\u064A": {
15526                 "isolated": "\uFD02",
15527                 "final": "\uFD1E"
15528             },
15529             "\u062E\u0649": {
15530                 "isolated": "\uFD03",
15531                 "final": "\uFD1F"
15532             },
15533             "\u062E\u064A": {
15534                 "isolated": "\uFD04",
15535                 "final": "\uFD20"
15536             },
15537             "\u0635\u0649": {
15538                 "isolated": "\uFD05",
15539                 "final": "\uFD21"
15540             },
15541             "\u0635\u064A": {
15542                 "isolated": "\uFD06",
15543                 "final": "\uFD22"
15544             },
15545             "\u0636\u0649": {
15546                 "isolated": "\uFD07",
15547                 "final": "\uFD23"
15548             },
15549             "\u0636\u064A": {
15550                 "isolated": "\uFD08",
15551                 "final": "\uFD24"
15552             },
15553             "\u0634\u062C": {
15554                 "isolated": "\uFD09",
15555                 "final": "\uFD25",
15556                 "initial": "\uFD2D",
15557                 "medial": "\uFD37"
15558             },
15559             "\u0634\u062D": {
15560                 "isolated": "\uFD0A",
15561                 "final": "\uFD26",
15562                 "initial": "\uFD2E",
15563                 "medial": "\uFD38"
15564             },
15565             "\u0634\u062E": {
15566                 "isolated": "\uFD0B",
15567                 "final": "\uFD27",
15568                 "initial": "\uFD2F",
15569                 "medial": "\uFD39"
15570             },
15571             "\u0634\u0631": {
15572                 "isolated": "\uFD0D",
15573                 "final": "\uFD29"
15574             },
15575             "\u0633\u0631": {
15576                 "isolated": "\uFD0E",
15577                 "final": "\uFD2A"
15578             },
15579             "\u0635\u0631": {
15580                 "isolated": "\uFD0F",
15581                 "final": "\uFD2B"
15582             },
15583             "\u0636\u0631": {
15584                 "isolated": "\uFD10",
15585                 "final": "\uFD2C"
15586             },
15587             "\u0633\u0639": {
15588                 "final": "\uFD17"
15589             },
15590             "\u062A\u062C\u0645": {
15591                 "initial": "\uFD50"
15592             },
15593             "\u062A\u062D\u062C": {
15594                 "final": "\uFD51",
15595                 "initial": "\uFD52"
15596             },
15597             "\u062A\u062D\u0645": {
15598                 "initial": "\uFD53"
15599             },
15600             "\u062A\u062E\u0645": {
15601                 "initial": "\uFD54"
15602             },
15603             "\u062A\u0645\u062C": {
15604                 "initial": "\uFD55"
15605             },
15606             "\u062A\u0645\u062D": {
15607                 "initial": "\uFD56"
15608             },
15609             "\u062A\u0645\u062E": {
15610                 "initial": "\uFD57"
15611             },
15612             "\u062C\u0645\u062D": {
15613                 "final": "\uFD58",
15614                 "initial": "\uFD59"
15615             },
15616             "\u062D\u0645\u064A": {
15617                 "final": "\uFD5A"
15618             },
15619             "\u062D\u0645\u0649": {
15620                 "final": "\uFD5B"
15621             },
15622             "\u0633\u062D\u062C": {
15623                 "initial": "\uFD5C"
15624             },
15625             "\u0633\u062C\u062D": {
15626                 "initial": "\uFD5D"
15627             },
15628             "\u0633\u062C\u0649": {
15629                 "final": "\uFD5E"
15630             },
15631             "\u0633\u0645\u062D": {
15632                 "final": "\uFD5F",
15633                 "initial": "\uFD60"
15634             },
15635             "\u0633\u0645\u062C": {
15636                 "initial": "\uFD61"
15637             },
15638             "\u0633\u0645\u0645": {
15639                 "final": "\uFD62",
15640                 "initial": "\uFD63"
15641             },
15642             "\u0635\u062D\u062D": {
15643                 "final": "\uFD64",
15644                 "initial": "\uFD65"
15645             },
15646             "\u0635\u0645\u0645": {
15647                 "final": "\uFD66",
15648                 "initial": "\uFDC5"
15649             },
15650             "\u0634\u062D\u0645": {
15651                 "final": "\uFD67",
15652                 "initial": "\uFD68"
15653             },
15654             "\u0634\u062C\u064A": {
15655                 "final": "\uFD69"
15656             },
15657             "\u0634\u0645\u062E": {
15658                 "final": "\uFD6A",
15659                 "initial": "\uFD6B"
15660             },
15661             "\u0634\u0645\u0645": {
15662                 "final": "\uFD6C",
15663                 "initial": "\uFD6D"
15664             },
15665             "\u0636\u062D\u0649": {
15666                 "final": "\uFD6E"
15667             },
15668             "\u0636\u062E\u0645": {
15669                 "final": "\uFD6F",
15670                 "initial": "\uFD70"
15671             },
15672             "\u0636\u0645\u062D": {
15673                 "final": "\uFD71"
15674             },
15675             "\u0637\u0645\u062D": {
15676                 "initial": "\uFD72"
15677             },
15678             "\u0637\u0645\u0645": {
15679                 "initial": "\uFD73"
15680             },
15681             "\u0637\u0645\u064A": {
15682                 "final": "\uFD74"
15683             },
15684             "\u0639\u062C\u0645": {
15685                 "final": "\uFD75",
15686                 "initial": "\uFDC4"
15687             },
15688             "\u0639\u0645\u0645": {
15689                 "final": "\uFD76",
15690                 "initial": "\uFD77"
15691             },
15692             "\u0639\u0645\u0649": {
15693                 "final": "\uFD78"
15694             },
15695             "\u063A\u0645\u0645": {
15696                 "final": "\uFD79"
15697             },
15698             "\u063A\u0645\u064A": {
15699                 "final": "\uFD7A"
15700             },
15701             "\u063A\u0645\u0649": {
15702                 "final": "\uFD7B"
15703             },
15704             "\u0641\u062E\u0645": {
15705                 "final": "\uFD7C",
15706                 "initial": "\uFD7D"
15707             },
15708             "\u0642\u0645\u062D": {
15709                 "final": "\uFD7E",
15710                 "initial": "\uFDB4"
15711             },
15712             "\u0642\u0645\u0645": {
15713                 "final": "\uFD7F"
15714             },
15715             "\u0644\u062D\u0645": {
15716                 "final": "\uFD80",
15717                 "initial": "\uFDB5"
15718             },
15719             "\u0644\u062D\u064A": {
15720                 "final": "\uFD81"
15721             },
15722             "\u0644\u062D\u0649": {
15723                 "final": "\uFD82"
15724             },
15725             "\u0644\u062C\u062C": {
15726                 "initial": "\uFD83",
15727                 "final": "\uFD84"
15728             },
15729             "\u0644\u062E\u0645": {
15730                 "final": "\uFD85",
15731                 "initial": "\uFD86"
15732             },
15733             "\u0644\u0645\u062D": {
15734                 "final": "\uFD87",
15735                 "initial": "\uFD88"
15736             },
15737             "\u0645\u062D\u062C": {
15738                 "initial": "\uFD89"
15739             },
15740             "\u0645\u062D\u0645": {
15741                 "initial": "\uFD8A"
15742             },
15743             "\u0645\u062D\u064A": {
15744                 "final": "\uFD8B"
15745             },
15746             "\u0645\u062C\u062D": {
15747                 "initial": "\uFD8C"
15748             },
15749             "\u0645\u062C\u0645": {
15750                 "initial": "\uFD8D"
15751             },
15752             "\u0645\u062E\u062C": {
15753                 "initial": "\uFD8E"
15754             },
15755             "\u0645\u062E\u0645": {
15756                 "initial": "\uFD8F"
15757             },
15758             "\u0645\u062C\u062E": {
15759                 "initial": "\uFD92"
15760             },
15761             "\u0647\u0645\u062C": {
15762                 "initial": "\uFD93"
15763             },
15764             "\u0647\u0645\u0645": {
15765                 "initial": "\uFD94"
15766             },
15767             "\u0646\u062D\u0645": {
15768                 "initial": "\uFD95"
15769             },
15770             "\u0646\u062D\u0649": {
15771                 "final": "\uFD96"
15772             },
15773             "\u0646\u062C\u0645": {
15774                 "final": "\uFD97",
15775                 "initial": "\uFD98"
15776             },
15777             "\u0646\u062C\u0649": {
15778                 "final": "\uFD99"
15779             },
15780             "\u0646\u0645\u064A": {
15781                 "final": "\uFD9A"
15782             },
15783             "\u0646\u0645\u0649": {
15784                 "final": "\uFD9B"
15785             },
15786             "\u064A\u0645\u0645": {
15787                 "final": "\uFD9C",
15788                 "initial": "\uFD9D"
15789             },
15790             "\u0628\u062E\u064A": {
15791                 "final": "\uFD9E"
15792             },
15793             "\u062A\u062C\u064A": {
15794                 "final": "\uFD9F"
15795             },
15796             "\u062A\u062C\u0649": {
15797                 "final": "\uFDA0"
15798             },
15799             "\u062A\u062E\u064A": {
15800                 "final": "\uFDA1"
15801             },
15802             "\u062A\u062E\u0649": {
15803                 "final": "\uFDA2"
15804             },
15805             "\u062A\u0645\u064A": {
15806                 "final": "\uFDA3"
15807             },
15808             "\u062A\u0645\u0649": {
15809                 "final": "\uFDA4"
15810             },
15811             "\u062C\u0645\u064A": {
15812                 "final": "\uFDA5"
15813             },
15814             "\u062C\u062D\u0649": {
15815                 "final": "\uFDA6"
15816             },
15817             "\u062C\u0645\u0649": {
15818                 "final": "\uFDA7"
15819             },
15820             "\u0633\u062E\u0649": {
15821                 "final": "\uFDA8"
15822             },
15823             "\u0635\u062D\u064A": {
15824                 "final": "\uFDA9"
15825             },
15826             "\u0634\u062D\u064A": {
15827                 "final": "\uFDAA"
15828             },
15829             "\u0636\u062D\u064A": {
15830                 "final": "\uFDAB"
15831             },
15832             "\u0644\u062C\u064A": {
15833                 "final": "\uFDAC"
15834             },
15835             "\u0644\u0645\u064A": {
15836                 "final": "\uFDAD"
15837             },
15838             "\u064A\u062D\u064A": {
15839                 "final": "\uFDAE"
15840             },
15841             "\u064A\u062C\u064A": {
15842                 "final": "\uFDAF"
15843             },
15844             "\u064A\u0645\u064A": {
15845                 "final": "\uFDB0"
15846             },
15847             "\u0645\u0645\u064A": {
15848                 "final": "\uFDB1"
15849             },
15850             "\u0642\u0645\u064A": {
15851                 "final": "\uFDB2"
15852             },
15853             "\u0646\u062D\u064A": {
15854                 "final": "\uFDB3"
15855             },
15856             "\u0639\u0645\u064A": {
15857                 "final": "\uFDB6"
15858             },
15859             "\u0643\u0645\u064A": {
15860                 "final": "\uFDB7"
15861             },
15862             "\u0646\u062C\u062D": {
15863                 "initial": "\uFDB8",
15864                 "final": "\uFDBD"
15865             },
15866             "\u0645\u062E\u064A": {
15867                 "final": "\uFDB9"
15868             },
15869             "\u0644\u062C\u0645": {
15870                 "initial": "\uFDBA",
15871                 "final": "\uFDBC"
15872             },
15873             "\u0643\u0645\u0645": {
15874                 "final": "\uFDBB",
15875                 "initial": "\uFDC3"
15876             },
15877             "\u062C\u062D\u064A": {
15878                 "final": "\uFDBE"
15879             },
15880             "\u062D\u062C\u064A": {
15881                 "final": "\uFDBF"
15882             },
15883             "\u0645\u062C\u064A": {
15884                 "final": "\uFDC0"
15885             },
15886             "\u0641\u0645\u064A": {
15887                 "final": "\uFDC1"
15888             },
15889             "\u0628\u062D\u064A": {
15890                 "final": "\uFDC2"
15891             },
15892             "\u0633\u062E\u064A": {
15893                 "final": "\uFDC6"
15894             },
15895             "\u0646\u062C\u064A": {
15896                 "final": "\uFDC7"
15897             },
15898             "\u0644\u0622": {
15899                 "isolated": "\uFEF5",
15900                 "final": "\uFEF6"
15901             },
15902             "\u0644\u0623": {
15903                 "isolated": "\uFEF7",
15904                 "final": "\uFEF8"
15905             },
15906             "\u0644\u0625": {
15907                 "isolated": "\uFEF9",
15908                 "final": "\uFEFA"
15909             },
15910             "\u0644\u0627": {
15911                 "isolated": "\uFEFB",
15912                 "final": "\uFEFC"
15913             },
15914             "words": {
15915                 "\u0635\u0644\u06D2": "\uFDF0",
15916                 "\u0642\u0644\u06D2": "\uFDF1",
15917                 "\u0627\u0644\u0644\u0647": "\uFDF2",
15918                 "\u0627\u0643\u0628\u0631": "\uFDF3",
15919                 "\u0645\u062D\u0645\u062F": "\uFDF4",
15920                 "\u0635\u0644\u0639\u0645": "\uFDF5",
15921                 "\u0631\u0633\u0648\u0644": "\uFDF6",
15922                 "\u0639\u0644\u064A\u0647": "\uFDF7",
15923                 "\u0648\u0633\u0644\u0645": "\uFDF8",
15924                 "\u0635\u0644\u0649": "\uFDF9",
15925                 "\u0635\u0644\u0649\u0627\u0644\u0644\u0647\u0639\u0644\u064A\u0647\u0648\u0633\u0644\u0645": "\uFDFA",
15926                 "\u062C\u0644\u062C\u0644\u0627\u0644\u0647": "\uFDFB",
15927                 "\u0631\u06CC\u0627\u0644": "\uFDFC"
15928             }
15929         };
15930         exports.default = ligatureReference;
15931         });
15932
15933         var reference = createCommonjsModule(function (module, exports) {
15934         Object.defineProperty(exports, "__esModule", { value: true });
15935
15936
15937         const letterList = Object.keys(unicodeArabic.default);
15938         exports.letterList = letterList;
15939         const ligatureList = Object.keys(unicodeLigatures.default);
15940         exports.ligatureList = ligatureList;
15941         const ligatureWordList = Object.keys(unicodeLigatures.default.words);
15942         exports.ligatureWordList = ligatureWordList;
15943         const lams = '\u0644\u06B5\u06B6\u06B7\u06B8';
15944         exports.lams = lams;
15945         const alefs = '\u0627\u0622\u0623\u0625\u0671\u0672\u0673\u0675\u0773\u0774';
15946         exports.alefs = alefs;
15947         // for (var l = 1; l < lams.length; l++) {
15948         //   console.log('-');
15949         //   for (var a = 0; a < alefs.length; a++) {
15950         //     console.log(a + ': ' + lams[l] + alefs[a]);
15951         //   }
15952         // }
15953         let tashkeel = '\u0605\u0640\u0670\u0674\u06DF\u06E7\u06E8';
15954         exports.tashkeel = tashkeel;
15955         function addToTashkeel(start, finish) {
15956             for (var i = start; i <= finish; i++) {
15957                 exports.tashkeel = tashkeel += String.fromCharCode(i);
15958             }
15959         }
15960         addToTashkeel(0x0610, 0x061A);
15961         addToTashkeel(0x064B, 0x065F);
15962         addToTashkeel(0x06D6, 0x06DC);
15963         addToTashkeel(0x06E0, 0x06E4);
15964         addToTashkeel(0x06EA, 0x06ED);
15965         addToTashkeel(0x08D3, 0x08E1);
15966         addToTashkeel(0x08E3, 0x08FF);
15967         addToTashkeel(0xFE70, 0xFE7F);
15968         let lineBreakers = '\u0627\u0629\u0648\u06C0\u06CF\u06FD\u06FE\u076B\u076C\u0771\u0773\u0774\u0778\u0779\u08E2\u08B1\u08B2\u08B9';
15969         exports.lineBreakers = lineBreakers;
15970         function addToLineBreakers(start, finish) {
15971             for (var i = start; i <= finish; i++) {
15972                 exports.lineBreakers = lineBreakers += String.fromCharCode(i);
15973             }
15974         }
15975         addToLineBreakers(0x0600, 0x061F); // it's OK to include tashkeel in this range as it is ignored
15976         addToLineBreakers(0x0621, 0x0625);
15977         addToLineBreakers(0x062F, 0x0632);
15978         addToLineBreakers(0x0660, 0x066D); // numerals, math
15979         addToLineBreakers(0x0671, 0x0677);
15980         addToLineBreakers(0x0688, 0x0699);
15981         addToLineBreakers(0x06C3, 0x06CB);
15982         addToLineBreakers(0x06D2, 0x06F9);
15983         addToLineBreakers(0x0759, 0x075B);
15984         addToLineBreakers(0x08AA, 0x08AE);
15985         addToLineBreakers(0xFB50, 0xFDFD); // presentation forms look like they could connect, but never do
15986         // Presentation Forms A includes diacritics but they are meant to stand alone
15987         addToLineBreakers(0xFE80, 0xFEFC); // presentation forms look like they could connect, but never do
15988         // numerals, math
15989         addToLineBreakers(0x10E60, 0x10E7F);
15990         addToLineBreakers(0x1EC70, 0x1ECBF);
15991         addToLineBreakers(0x1EE00, 0x1EEFF);
15992         });
15993
15994         var GlyphSplitter_1 = createCommonjsModule(function (module, exports) {
15995         Object.defineProperty(exports, "__esModule", { value: true });
15996
15997
15998         function GlyphSplitter(word) {
15999             let letters = [];
16000             let lastLetter = '';
16001             word.split('').forEach((letter) => {
16002                 if (isArabic_1.isArabic(letter)) {
16003                     if (reference.tashkeel.indexOf(letter) > -1) {
16004                         letters[letters.length - 1] += letter;
16005                     }
16006                     else if (lastLetter.length && ((reference.lams.indexOf(lastLetter) === 0 && reference.alefs.indexOf(letter) > -1) || (reference.lams.indexOf(lastLetter) > 0 && reference.alefs.indexOf(letter) === 0))) {
16007                         // valid LA forms
16008                         letters[letters.length - 1] += letter;
16009                     }
16010                     else {
16011                         letters.push(letter);
16012                     }
16013                 }
16014                 else {
16015                     letters.push(letter);
16016                 }
16017                 if (reference.tashkeel.indexOf(letter) === -1) {
16018                     lastLetter = letter;
16019                 }
16020             });
16021             return letters;
16022         }
16023         exports.GlyphSplitter = GlyphSplitter;
16024         });
16025
16026         var BaselineSplitter_1 = createCommonjsModule(function (module, exports) {
16027         Object.defineProperty(exports, "__esModule", { value: true });
16028
16029
16030         function BaselineSplitter(word) {
16031             let letters = [];
16032             let lastLetter = '';
16033             word.split('').forEach((letter) => {
16034                 if (isArabic_1.isArabic(letter) && isArabic_1.isArabic(lastLetter)) {
16035                     if (lastLetter.length && reference.tashkeel.indexOf(letter) > -1) {
16036                         letters[letters.length - 1] += letter;
16037                     }
16038                     else if (reference.lineBreakers.indexOf(lastLetter) > -1) {
16039                         letters.push(letter);
16040                     }
16041                     else {
16042                         letters[letters.length - 1] += letter;
16043                     }
16044                 }
16045                 else {
16046                     letters.push(letter);
16047                 }
16048                 if (reference.tashkeel.indexOf(letter) === -1) {
16049                     // don't allow tashkeel to hide line break
16050                     lastLetter = letter;
16051                 }
16052             });
16053             return letters;
16054         }
16055         exports.BaselineSplitter = BaselineSplitter;
16056         });
16057
16058         var Normalization = createCommonjsModule(function (module, exports) {
16059         Object.defineProperty(exports, "__esModule", { value: true });
16060
16061
16062
16063
16064         function Normal(word, breakPresentationForm) {
16065             // default is to turn initial/isolated/medial/final presentation form to generic
16066             if (typeof breakPresentationForm === 'undefined') {
16067                 breakPresentationForm = true;
16068             }
16069             let returnable = '';
16070             word.split('').forEach((letter) => {
16071                 if (!isArabic_1.isArabic(letter)) {
16072                     returnable += letter;
16073                     return;
16074                 }
16075                 for (let w = 0; w < reference.letterList.length; w++) {
16076                     // ok so we are checking this potential lettertron
16077                     let letterForms = unicodeArabic.default[reference.letterList[w]];
16078                     let versions = Object.keys(letterForms);
16079                     for (let v = 0; v < versions.length; v++) {
16080                         let localVersion = letterForms[versions[v]];
16081                         if (typeof localVersion === 'object' && typeof localVersion.indexOf === 'undefined') {
16082                             // look at this embedded object
16083                             let embeddedForms = Object.keys(localVersion);
16084                             for (let ef = 0; ef < embeddedForms.length; ef++) {
16085                                 let form = localVersion[embeddedForms[ef]];
16086                                 if (form === letter || (typeof form === 'object' && form.indexOf && form.indexOf(letter) > -1)) {
16087                                     // match
16088                                     // console.log('embedded match');
16089                                     if (form === letter) {
16090                                         // match exact
16091                                         if (breakPresentationForm && localVersion['normal'] && ['isolated', 'initial', 'medial', 'final'].indexOf(embeddedForms[ef]) > -1) {
16092                                             // replace presentation form
16093                                             // console.log('keeping normal form of the letter');
16094                                             if (typeof localVersion['normal'] === 'object') {
16095                                                 returnable += localVersion['normal'][0];
16096                                             }
16097                                             else {
16098                                                 returnable += localVersion['normal'];
16099                                             }
16100                                             return;
16101                                         }
16102                                         // console.log('keeping this letter');
16103                                         returnable += letter;
16104                                         return;
16105                                     }
16106                                     else if (typeof form === 'object' && form.indexOf && form.indexOf(letter) > -1) {
16107                                         // match
16108                                         returnable += form[0];
16109                                         // console.log('added the first letter from the same array');
16110                                         return;
16111                                     }
16112                                 }
16113                             }
16114                         }
16115                         else if (localVersion === letter) {
16116                             // match exact
16117                             if (breakPresentationForm && letterForms['normal'] && ['isolated', 'initial', 'medial', 'final'].indexOf(versions[v]) > -1) {
16118                                 // replace presentation form
16119                                 // console.log('keeping normal form of the letter');
16120                                 if (typeof letterForms['normal'] === 'object') {
16121                                     returnable += letterForms['normal'][0];
16122                                 }
16123                                 else {
16124                                     returnable += letterForms['normal'];
16125                                 }
16126                                 return;
16127                             }
16128                             // console.log('keeping this letter');
16129                             returnable += letter;
16130                             return;
16131                         }
16132                         else if (typeof localVersion === 'object' && localVersion.indexOf && localVersion.indexOf(letter) > -1) {
16133                             // match
16134                             returnable += localVersion[0];
16135                             // console.log('added the first letter from the same array');
16136                             return;
16137                         }
16138                     }
16139                 }
16140                 // try ligatures
16141                 for (let v2 = 0; v2 < reference.ligatureList.length; v2++) {
16142                     let normalForm = reference.ligatureList[v2];
16143                     if (normalForm !== 'words') {
16144                         let ligForms = Object.keys(unicodeLigatures.default[normalForm]);
16145                         for (let f = 0; f < ligForms.length; f++) {
16146                             if (unicodeLigatures.default[normalForm][ligForms[f]] === letter) {
16147                                 returnable += normalForm;
16148                                 return;
16149                             }
16150                         }
16151                     }
16152                 }
16153                 // try words ligatures
16154                 for (let v3 = 0; v3 < reference.ligatureWordList.length; v3++) {
16155                     let normalForm = reference.ligatureWordList[v3];
16156                     if (unicodeLigatures.default.words[normalForm] === letter) {
16157                         returnable += normalForm;
16158                         return;
16159                     }
16160                 }
16161                 returnable += letter;
16162                 // console.log('kept the letter')
16163             });
16164             return returnable;
16165         }
16166         exports.Normal = Normal;
16167         });
16168
16169         var CharShaper_1 = createCommonjsModule(function (module, exports) {
16170         Object.defineProperty(exports, "__esModule", { value: true });
16171
16172
16173
16174         function CharShaper(letter, form) {
16175             if (!isArabic_1.isArabic(letter)) {
16176                 // fail not Arabic
16177                 throw new Error('Not Arabic');
16178             }
16179             if (letter === "\u0621") {
16180                 // hamza alone
16181                 return "\u0621";
16182             }
16183             for (let w = 0; w < reference.letterList.length; w++) {
16184                 // ok so we are checking this potential lettertron
16185                 let letterForms = unicodeArabic.default[reference.letterList[w]];
16186                 let versions = Object.keys(letterForms);
16187                 for (let v = 0; v < versions.length; v++) {
16188                     let localVersion = letterForms[versions[v]];
16189                     if ((localVersion === letter) ||
16190                         (typeof localVersion === 'object' && localVersion.indexOf && localVersion.indexOf(letter) > -1)) {
16191                         if (versions.indexOf(form) > -1) {
16192                             return letterForms[form];
16193                         }
16194                     }
16195                     else if (typeof localVersion === 'object' && typeof localVersion.indexOf === 'undefined') {
16196                         // check embedded
16197                         let embeddedVersions = Object.keys(localVersion);
16198                         for (let ev = 0; ev < embeddedVersions.length; ev++) {
16199                             if ((localVersion[embeddedVersions[ev]] === letter) ||
16200                                 (typeof localVersion[embeddedVersions[ev]] === 'object' && localVersion[embeddedVersions[ev]].indexOf && localVersion[embeddedVersions[ev]].indexOf(letter) > -1)) {
16201                                 if (embeddedVersions.indexOf(form) > -1) {
16202                                     return localVersion[form];
16203                                 }
16204                             }
16205                         }
16206                     }
16207                 }
16208             }
16209         }
16210         exports.CharShaper = CharShaper;
16211         });
16212
16213         var WordShaper_1 = createCommonjsModule(function (module, exports) {
16214         Object.defineProperty(exports, "__esModule", { value: true });
16215
16216
16217
16218
16219         function WordShaper(word) {
16220             let state = 'initial';
16221             let output = '';
16222             for (let w = 0; w < word.length; w++) {
16223                 let nextLetter = ' ';
16224                 for (let nxw = w + 1; nxw < word.length; nxw++) {
16225                     if (!isArabic_1.isArabic(word[nxw])) {
16226                         break;
16227                     }
16228                     if (reference.tashkeel.indexOf(word[nxw]) === -1) {
16229                         nextLetter = word[nxw];
16230                         break;
16231                     }
16232                 }
16233                 if (!isArabic_1.isArabic(word[w]) || isArabic_1.isMath(word[w])) {
16234                     // space or other non-Arabic
16235                     output += word[w];
16236                     state = 'initial';
16237                 }
16238                 else if (reference.tashkeel.indexOf(word[w]) > -1) {
16239                     // tashkeel - add without changing state
16240                     output += word[w];
16241                 }
16242                 else if ((nextLetter === ' ') // last Arabic letter in this word
16243                     || (reference.lineBreakers.indexOf(word[w]) > -1)) { // the current letter is known to break lines
16244                     output += CharShaper_1.CharShaper(word[w], state === 'initial' ? 'isolated' : 'final');
16245                     state = 'initial';
16246                 }
16247                 else if (reference.lams.indexOf(word[w]) > -1 && reference.alefs.indexOf(nextLetter) > -1) {
16248                     // LA letters - advance an additional letter after this
16249                     output += unicodeLigatures.default[word[w] + nextLetter][(state === 'initial' ? 'isolated' : 'final')];
16250                     while (word[w] !== nextLetter) {
16251                         w++;
16252                     }
16253                     state = 'initial';
16254                 }
16255                 else {
16256                     output += CharShaper_1.CharShaper(word[w], state);
16257                     state = 'medial';
16258                 }
16259             }
16260             return output;
16261         }
16262         exports.WordShaper = WordShaper;
16263         });
16264
16265         var ParentLetter_1 = createCommonjsModule(function (module, exports) {
16266         Object.defineProperty(exports, "__esModule", { value: true });
16267
16268
16269
16270         function ParentLetter(letter) {
16271             if (!isArabic_1.isArabic(letter)) {
16272                 throw new Error('Not an Arabic letter');
16273             }
16274             for (let w = 0; w < reference.letterList.length; w++) {
16275                 // ok so we are checking this potential lettertron
16276                 let letterForms = unicodeArabic.default[reference.letterList[w]];
16277                 let versions = Object.keys(letterForms);
16278                 for (let v = 0; v < versions.length; v++) {
16279                     let localVersion = letterForms[versions[v]];
16280                     if (typeof localVersion === 'object' && typeof localVersion.indexOf === 'undefined') {
16281                         // look at this embedded object
16282                         let embeddedForms = Object.keys(localVersion);
16283                         for (let ef = 0; ef < embeddedForms.length; ef++) {
16284                             let form = localVersion[embeddedForms[ef]];
16285                             if (form === letter || (typeof form === 'object' && form.indexOf && form.indexOf(letter) > -1)) {
16286                                 // match
16287                                 return localVersion;
16288                             }
16289                         }
16290                     }
16291                     else if (localVersion === letter || (typeof localVersion === 'object' && localVersion.indexOf && localVersion.indexOf(letter) > -1)) {
16292                         // match
16293                         return letterForms;
16294                     }
16295                 }
16296                 return null;
16297             }
16298         }
16299         exports.ParentLetter = ParentLetter;
16300         function GrandparentLetter(letter) {
16301             if (!isArabic_1.isArabic(letter)) {
16302                 throw new Error('Not an Arabic letter');
16303             }
16304             for (let w = 0; w < reference.letterList.length; w++) {
16305                 // ok so we are checking this potential lettertron
16306                 let letterForms = unicodeArabic.default[reference.letterList[w]];
16307                 let versions = Object.keys(letterForms);
16308                 for (let v = 0; v < versions.length; v++) {
16309                     let localVersion = letterForms[versions[v]];
16310                     if (typeof localVersion === 'object' && typeof localVersion.indexOf === 'undefined') {
16311                         // look at this embedded object
16312                         let embeddedForms = Object.keys(localVersion);
16313                         for (let ef = 0; ef < embeddedForms.length; ef++) {
16314                             let form = localVersion[embeddedForms[ef]];
16315                             if (form === letter || (typeof form === 'object' && form.indexOf && form.indexOf(letter) > -1)) {
16316                                 // match
16317                                 return letterForms;
16318                             }
16319                         }
16320                     }
16321                     else if (localVersion === letter || (typeof localVersion === 'object' && localVersion.indexOf && localVersion.indexOf(letter) > -1)) {
16322                         // match
16323                         return letterForms;
16324                     }
16325                 }
16326                 return null;
16327             }
16328         }
16329         exports.GrandparentLetter = GrandparentLetter;
16330         });
16331
16332         var lib$1 = createCommonjsModule(function (module, exports) {
16333         Object.defineProperty(exports, "__esModule", { value: true });
16334
16335         exports.isArabic = isArabic_1.isArabic;
16336
16337         exports.GlyphSplitter = GlyphSplitter_1.GlyphSplitter;
16338
16339         exports.BaselineSplitter = BaselineSplitter_1.BaselineSplitter;
16340
16341         exports.Normal = Normalization.Normal;
16342
16343         exports.CharShaper = CharShaper_1.CharShaper;
16344
16345         exports.WordShaper = WordShaper_1.WordShaper;
16346
16347         exports.ParentLetter = ParentLetter_1.ParentLetter;
16348         exports.GrandparentLetter = ParentLetter_1.GrandparentLetter;
16349         });
16350
16351         // see https://github.com/openstreetmap/iD/pull/3707
16352
16353         var rtlRegex = /[\u0590-\u05FF\u0600-\u06FF\u0750-\u07BF\u08A0–\u08BF]/;
16354
16355         function fixRTLTextForSvg(inputText) {
16356             var ret = '', rtlBuffer = [];
16357             var arabicRegex = /[\u0600-\u06FF]/g;
16358             var arabicDiacritics = /[\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06ED]/g;
16359             var arabicMath = /[\u0660-\u066C\u06F0-\u06F9]+/g;
16360             var thaanaVowel = /[\u07A6-\u07B0]/;
16361             var hebrewSign = /[\u0591-\u05bd\u05bf\u05c1-\u05c5\u05c7]/;
16362
16363             // Arabic word shaping
16364             if (arabicRegex.test(inputText)) {
16365                 inputText = lib$1.WordShaper(inputText);
16366             }
16367
16368             for (var n = 0; n < inputText.length; n++) {
16369                 var c = inputText[n];
16370                 if (arabicMath.test(c)) {
16371                     // Arabic numbers go LTR
16372                     ret += rtlBuffer.reverse().join('');
16373                     rtlBuffer = [c];
16374                 } else {
16375                     if (rtlBuffer.length && arabicMath.test(rtlBuffer[rtlBuffer.length - 1])) {
16376                         ret += rtlBuffer.reverse().join('');
16377                         rtlBuffer = [];
16378                     }
16379                     if ((thaanaVowel.test(c) || hebrewSign.test(c) || arabicDiacritics.test(c)) && rtlBuffer.length) {
16380                         rtlBuffer[rtlBuffer.length - 1] += c;
16381                     } else if (rtlRegex.test(c)
16382                         // include Arabic presentation forms
16383                         || (c.charCodeAt(0) >= 64336 && c.charCodeAt(0) <= 65023)
16384                         || (c.charCodeAt(0) >= 65136 && c.charCodeAt(0) <= 65279)) {
16385                         rtlBuffer.push(c);
16386                     } else if (c === ' ' && rtlBuffer.length) {
16387                         // whitespace within RTL text
16388                         rtlBuffer = [rtlBuffer.reverse().join('') + ' '];
16389                     } else {
16390                         // non-RTL character
16391                         ret += rtlBuffer.reverse().join('') + c;
16392                         rtlBuffer = [];
16393                     }
16394                 }
16395             }
16396             ret += rtlBuffer.reverse().join('');
16397             return ret;
16398         }
16399
16400         // https://github.com/openstreetmap/iD/issues/772
16401         // http://mathiasbynens.be/notes/localstorage-pattern#comment-9
16402         let _storage;
16403         try { _storage = localStorage; } catch (e) {}  // eslint-disable-line no-empty
16404         _storage = _storage || (() => {
16405           let s = {};
16406           return {
16407             getItem: (k) => s[k],
16408             setItem: (k, v) => s[k] = v,
16409             removeItem: (k) => delete s[k]
16410           };
16411         })();
16412
16413         //
16414         // corePreferences is an interface for persisting basic key-value strings
16415         // within and between iD sessions on the same site.
16416         //
16417         function corePreferences(k, v) {
16418
16419           try {
16420             if (arguments.length === 1) return _storage.getItem(k);
16421             else if (v === null) _storage.removeItem(k);
16422             else _storage.setItem(k, v);
16423           } catch (e) {
16424             /* eslint-disable no-console */
16425             if (typeof console !== 'undefined') {
16426               console.error('localStorage quota exceeded');
16427             }
16428             /* eslint-enable no-console */
16429           }
16430
16431         }
16432
16433         function responseText(response) {
16434           if (!response.ok) throw new Error(response.status + " " + response.statusText);
16435           return response.text();
16436         }
16437
16438         function d3_text(input, init) {
16439           return fetch(input, init).then(responseText);
16440         }
16441
16442         function responseJson(response) {
16443           if (!response.ok) throw new Error(response.status + " " + response.statusText);
16444           if (response.status === 204 || response.status === 205) return;
16445           return response.json();
16446         }
16447
16448         function d3_json(input, init) {
16449           return fetch(input, init).then(responseJson);
16450         }
16451
16452         function parser(type) {
16453           return function(input, init)  {
16454             return d3_text(input, init).then(function(text) {
16455               return (new DOMParser).parseFromString(text, type);
16456             });
16457           };
16458         }
16459
16460         var d3_xml = parser("application/xml");
16461
16462         var svg = parser("image/svg+xml");
16463
16464         let _mainFileFetcher = coreFileFetcher(); // singleton
16465
16466         //
16467         // coreFileFetcher asynchronously fetches data from JSON files
16468         //
16469         function coreFileFetcher() {
16470           let _this = {};
16471           let _inflight = {};
16472           let _fileMap = {
16473             'address_formats': 'data/address_formats.min.json',
16474             'deprecated': 'data/deprecated.min.json',
16475             'discarded': 'data/discarded.min.json',
16476             'imagery': 'data/imagery.min.json',
16477             'intro_graph': 'data/intro_graph.min.json',
16478             'keepRight': 'data/keepRight.min.json',
16479             'languages': 'data/languages.min.json',
16480             'locales': 'data/locales.min.json',
16481             'nsi_brands': 'https://cdn.jsdelivr.net/npm/name-suggestion-index@4/dist/brands.min.json',
16482             'nsi_filters': 'https://cdn.jsdelivr.net/npm/name-suggestion-index@4/dist/filters.min.json',
16483             'oci_features': 'https://cdn.jsdelivr.net/npm/osm-community-index@2/dist/features.min.json',
16484             'oci_resources': 'https://cdn.jsdelivr.net/npm/osm-community-index@2/dist/resources.min.json',
16485             'preset_categories': 'data/preset_categories.min.json',
16486             'preset_defaults': 'data/preset_defaults.min.json',
16487             'preset_fields': 'data/preset_fields.min.json',
16488             'preset_presets': 'data/preset_presets.min.json',
16489             'phone_formats': 'data/phone_formats.min.json',
16490             'qa_data': 'data/qa_data.min.json',
16491             'shortcuts': 'data/shortcuts.min.json',
16492             'territory_languages': 'data/territory_languages.min.json',
16493             'wmf_sitematrix': 'https://cdn.jsdelivr.net/npm/wmf-sitematrix@0.1/wikipedia.min.json'
16494           };
16495
16496           let _cachedData = {};
16497           // expose the cache; useful for tests
16498           _this.cache = () => _cachedData;
16499
16500
16501           // Returns a Promise to fetch data
16502           // (resolved with the data if we have it already)
16503           _this.get = (which) => {
16504             if (_cachedData[which]) {
16505               return Promise.resolve(_cachedData[which]);
16506             }
16507
16508             const file = _fileMap[which];
16509             const url = file && _this.asset(file);
16510             if (!url) {
16511               return Promise.reject(`Unknown data file for "${which}"`);
16512             }
16513
16514             let prom = _inflight[url];
16515             if (!prom) {
16516               _inflight[url] = prom = d3_json(url)
16517                 .then(result => {
16518                   delete _inflight[url];
16519                   if (!result) {
16520                     throw new Error(`No data loaded for "${which}"`);
16521                   }
16522                   _cachedData[which] = result;
16523                   return result;
16524                 })
16525                 .catch(err => {
16526                   delete _inflight[url];
16527                   throw err;
16528                 });
16529             }
16530
16531             return prom;
16532           };
16533
16534
16535           // Accessor for the file map
16536           _this.fileMap = function(val) {
16537             if (!arguments.length) return _fileMap;
16538             _fileMap = val;
16539             return _this;
16540           };
16541
16542           let _assetPath = '';
16543           _this.assetPath = function(val) {
16544             if (!arguments.length) return _assetPath;
16545             _assetPath = val;
16546             return _this;
16547           };
16548
16549           let _assetMap = {};
16550           _this.assetMap = function(val) {
16551             if (!arguments.length) return _assetMap;
16552             _assetMap = val;
16553             return _this;
16554           };
16555
16556           _this.asset = (val) => {
16557             if (/^http(s)?:\/\//i.test(val)) return val;
16558             const filename = _assetPath + val;
16559             return _assetMap[filename] || filename;
16560           };
16561
16562           return _this;
16563         }
16564
16565         let _detected;
16566
16567         function utilDetect(refresh) {
16568           if (_detected && !refresh) return _detected;
16569           _detected = {};
16570
16571           const ua = navigator.userAgent;
16572           let m = null;
16573
16574           /* Browser */
16575           m = ua.match(/(edge)\/?\s*(\.?\d+(\.\d+)*)/i);   // Edge
16576           if (m !== null) {
16577             _detected.browser = m[1];
16578             _detected.version = m[2];
16579           }
16580           if (!_detected.browser) {
16581             m = ua.match(/Trident\/.*rv:([0-9]{1,}[\.0-9]{0,})/i);   // IE11
16582             if (m !== null) {
16583               _detected.browser = 'msie';
16584               _detected.version = m[1];
16585             }
16586           }
16587           if (!_detected.browser) {
16588             m = ua.match(/(opr)\/?\s*(\.?\d+(\.\d+)*)/i);   // Opera 15+
16589             if (m !== null) {
16590               _detected.browser = 'Opera';
16591               _detected.version = m[2];
16592             }
16593           }
16594           if (!_detected.browser) {
16595             m = ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
16596             if (m !== null) {
16597               _detected.browser = m[1];
16598               _detected.version = m[2];
16599               m = ua.match(/version\/([\.\d]+)/i);
16600               if (m !== null) _detected.version = m[1];
16601             }
16602           }
16603           if (!_detected.browser) {
16604             _detected.browser = navigator.appName;
16605             _detected.version = navigator.appVersion;
16606           }
16607
16608           // keep major.minor version only..
16609           _detected.version = _detected.version.split(/\W/).slice(0,2).join('.');
16610
16611           // detect other browser capabilities
16612           // Legacy Opera has incomplete svg style support. See #715
16613           _detected.opera = (_detected.browser.toLowerCase() === 'opera' && parseFloat(_detected.version) < 15 );
16614
16615           if (_detected.browser.toLowerCase() === 'msie') {
16616             _detected.ie = true;
16617             _detected.browser = 'Internet Explorer';
16618             _detected.support = parseFloat(_detected.version) >= 11;
16619           } else {
16620             _detected.ie = false;
16621             _detected.support = true;
16622           }
16623
16624           _detected.filedrop = (window.FileReader && 'ondrop' in window);
16625           _detected.download = !(_detected.ie || _detected.browser.toLowerCase() === 'edge');
16626           _detected.cssfilters = !(_detected.ie || _detected.browser.toLowerCase() === 'edge');
16627
16628
16629           /* Platform */
16630           if (/Win/.test(ua)) {
16631             _detected.os = 'win';
16632             _detected.platform = 'Windows';
16633           } else if (/Mac/.test(ua)) {
16634             _detected.os = 'mac';
16635             _detected.platform = 'Macintosh';
16636           } else if (/X11/.test(ua) || /Linux/.test(ua)) {
16637             _detected.os = 'linux';
16638             _detected.platform = 'Linux';
16639           } else {
16640             _detected.os = 'win';
16641             _detected.platform = 'Unknown';
16642           }
16643
16644           _detected.isMobileWebKit = (/\b(iPad|iPhone|iPod)\b/.test(ua) ||
16645             // HACK: iPadOS 13+ requests desktop sites by default by using a Mac user agent,
16646             // so assume any "mac" with multitouch is actually iOS
16647             (navigator.platform === 'MacIntel' && 'maxTouchPoints' in navigator && navigator.maxTouchPoints > 1)) &&
16648             /WebKit/.test(ua) &&
16649             !/Edge/.test(ua) &&
16650             !window.MSStream;
16651
16652
16653           /* Locale */
16654           // An array of locales requested by the browser in priority order.
16655           _detected.browserLocales = Array.from(new Set( // remove duplicates
16656               [navigator.language]
16657                 .concat(navigator.languages || [])
16658                 .concat([
16659                     // old property for backwards compatibility
16660                     navigator.userLanguage,
16661                     // fallback to English
16662                     'en'
16663                 ])
16664                 // remove any undefined values
16665                 .filter(Boolean)
16666             ));
16667
16668
16669           /* Host */
16670           const loc = window.top.location;
16671           let origin = loc.origin;
16672           if (!origin) {  // for unpatched IE11
16673             origin = loc.protocol + '//' + loc.hostname + (loc.port ? ':' + loc.port: '');
16674           }
16675
16676           _detected.host = origin + loc.pathname;
16677
16678
16679           return _detected;
16680         }
16681
16682         var aesJs = createCommonjsModule(function (module, exports) {
16683         /*! MIT License. Copyright 2015-2018 Richard Moore <me@ricmoo.com>. See LICENSE.txt. */
16684         (function(root) {
16685
16686             function checkInt(value) {
16687                 return (parseInt(value) === value);
16688             }
16689
16690             function checkInts(arrayish) {
16691                 if (!checkInt(arrayish.length)) { return false; }
16692
16693                 for (var i = 0; i < arrayish.length; i++) {
16694                     if (!checkInt(arrayish[i]) || arrayish[i] < 0 || arrayish[i] > 255) {
16695                         return false;
16696                     }
16697                 }
16698
16699                 return true;
16700             }
16701
16702             function coerceArray(arg, copy) {
16703
16704                 // ArrayBuffer view
16705                 if (arg.buffer && arg.name === 'Uint8Array') {
16706
16707                     if (copy) {
16708                         if (arg.slice) {
16709                             arg = arg.slice();
16710                         } else {
16711                             arg = Array.prototype.slice.call(arg);
16712                         }
16713                     }
16714
16715                     return arg;
16716                 }
16717
16718                 // It's an array; check it is a valid representation of a byte
16719                 if (Array.isArray(arg)) {
16720                     if (!checkInts(arg)) {
16721                         throw new Error('Array contains invalid value: ' + arg);
16722                     }
16723
16724                     return new Uint8Array(arg);
16725                 }
16726
16727                 // Something else, but behaves like an array (maybe a Buffer? Arguments?)
16728                 if (checkInt(arg.length) && checkInts(arg)) {
16729                     return new Uint8Array(arg);
16730                 }
16731
16732                 throw new Error('unsupported array-like object');
16733             }
16734
16735             function createArray(length) {
16736                 return new Uint8Array(length);
16737             }
16738
16739             function copyArray(sourceArray, targetArray, targetStart, sourceStart, sourceEnd) {
16740                 if (sourceStart != null || sourceEnd != null) {
16741                     if (sourceArray.slice) {
16742                         sourceArray = sourceArray.slice(sourceStart, sourceEnd);
16743                     } else {
16744                         sourceArray = Array.prototype.slice.call(sourceArray, sourceStart, sourceEnd);
16745                     }
16746                 }
16747                 targetArray.set(sourceArray, targetStart);
16748             }
16749
16750
16751
16752             var convertUtf8 = (function() {
16753                 function toBytes(text) {
16754                     var result = [], i = 0;
16755                     text = encodeURI(text);
16756                     while (i < text.length) {
16757                         var c = text.charCodeAt(i++);
16758
16759                         // if it is a % sign, encode the following 2 bytes as a hex value
16760                         if (c === 37) {
16761                             result.push(parseInt(text.substr(i, 2), 16));
16762                             i += 2;
16763
16764                         // otherwise, just the actual byte
16765                         } else {
16766                             result.push(c);
16767                         }
16768                     }
16769
16770                     return coerceArray(result);
16771                 }
16772
16773                 function fromBytes(bytes) {
16774                     var result = [], i = 0;
16775
16776                     while (i < bytes.length) {
16777                         var c = bytes[i];
16778
16779                         if (c < 128) {
16780                             result.push(String.fromCharCode(c));
16781                             i++;
16782                         } else if (c > 191 && c < 224) {
16783                             result.push(String.fromCharCode(((c & 0x1f) << 6) | (bytes[i + 1] & 0x3f)));
16784                             i += 2;
16785                         } else {
16786                             result.push(String.fromCharCode(((c & 0x0f) << 12) | ((bytes[i + 1] & 0x3f) << 6) | (bytes[i + 2] & 0x3f)));
16787                             i += 3;
16788                         }
16789                     }
16790
16791                     return result.join('');
16792                 }
16793
16794                 return {
16795                     toBytes: toBytes,
16796                     fromBytes: fromBytes,
16797                 }
16798             })();
16799
16800             var convertHex = (function() {
16801                 function toBytes(text) {
16802                     var result = [];
16803                     for (var i = 0; i < text.length; i += 2) {
16804                         result.push(parseInt(text.substr(i, 2), 16));
16805                     }
16806
16807                     return result;
16808                 }
16809
16810                 // http://ixti.net/development/javascript/2011/11/11/base64-encodedecode-of-utf8-in-browser-with-js.html
16811                 var Hex = '0123456789abcdef';
16812
16813                 function fromBytes(bytes) {
16814                         var result = [];
16815                         for (var i = 0; i < bytes.length; i++) {
16816                             var v = bytes[i];
16817                             result.push(Hex[(v & 0xf0) >> 4] + Hex[v & 0x0f]);
16818                         }
16819                         return result.join('');
16820                 }
16821
16822                 return {
16823                     toBytes: toBytes,
16824                     fromBytes: fromBytes,
16825                 }
16826             })();
16827
16828
16829             // Number of rounds by keysize
16830             var numberOfRounds = {16: 10, 24: 12, 32: 14};
16831
16832             // Round constant words
16833             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];
16834
16835             // S-box and Inverse S-box (S is for Substitution)
16836             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];
16837             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];
16838
16839             // Transformations for encryption
16840             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];
16841             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];
16842             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];
16843             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];
16844
16845             // Transformations for decryption
16846             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];
16847             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];
16848             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];
16849             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];
16850
16851             // Transformations for decryption key expansion
16852             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];
16853             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];
16854             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];
16855             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];
16856
16857             function convertToInt32(bytes) {
16858                 var result = [];
16859                 for (var i = 0; i < bytes.length; i += 4) {
16860                     result.push(
16861                         (bytes[i    ] << 24) |
16862                         (bytes[i + 1] << 16) |
16863                         (bytes[i + 2] <<  8) |
16864                          bytes[i + 3]
16865                     );
16866                 }
16867                 return result;
16868             }
16869
16870             var AES = function(key) {
16871                 if (!(this instanceof AES)) {
16872                     throw Error('AES must be instanitated with `new`');
16873                 }
16874
16875                 Object.defineProperty(this, 'key', {
16876                     value: coerceArray(key, true)
16877                 });
16878
16879                 this._prepare();
16880             };
16881
16882
16883             AES.prototype._prepare = function() {
16884
16885                 var rounds = numberOfRounds[this.key.length];
16886                 if (rounds == null) {
16887                     throw new Error('invalid key size (must be 16, 24 or 32 bytes)');
16888                 }
16889
16890                 // encryption round keys
16891                 this._Ke = [];
16892
16893                 // decryption round keys
16894                 this._Kd = [];
16895
16896                 for (var i = 0; i <= rounds; i++) {
16897                     this._Ke.push([0, 0, 0, 0]);
16898                     this._Kd.push([0, 0, 0, 0]);
16899                 }
16900
16901                 var roundKeyCount = (rounds + 1) * 4;
16902                 var KC = this.key.length / 4;
16903
16904                 // convert the key into ints
16905                 var tk = convertToInt32(this.key);
16906
16907                 // copy values into round key arrays
16908                 var index;
16909                 for (var i = 0; i < KC; i++) {
16910                     index = i >> 2;
16911                     this._Ke[index][i % 4] = tk[i];
16912                     this._Kd[rounds - index][i % 4] = tk[i];
16913                 }
16914
16915                 // key expansion (fips-197 section 5.2)
16916                 var rconpointer = 0;
16917                 var t = KC, tt;
16918                 while (t < roundKeyCount) {
16919                     tt = tk[KC - 1];
16920                     tk[0] ^= ((S[(tt >> 16) & 0xFF] << 24) ^
16921                               (S[(tt >>  8) & 0xFF] << 16) ^
16922                               (S[ tt        & 0xFF] <<  8) ^
16923                                S[(tt >> 24) & 0xFF]        ^
16924                               (rcon[rconpointer] << 24));
16925                     rconpointer += 1;
16926
16927                     // key expansion (for non-256 bit)
16928                     if (KC != 8) {
16929                         for (var i = 1; i < KC; i++) {
16930                             tk[i] ^= tk[i - 1];
16931                         }
16932
16933                     // key expansion for 256-bit keys is "slightly different" (fips-197)
16934                     } else {
16935                         for (var i = 1; i < (KC / 2); i++) {
16936                             tk[i] ^= tk[i - 1];
16937                         }
16938                         tt = tk[(KC / 2) - 1];
16939
16940                         tk[KC / 2] ^= (S[ tt        & 0xFF]        ^
16941                                       (S[(tt >>  8) & 0xFF] <<  8) ^
16942                                       (S[(tt >> 16) & 0xFF] << 16) ^
16943                                       (S[(tt >> 24) & 0xFF] << 24));
16944
16945                         for (var i = (KC / 2) + 1; i < KC; i++) {
16946                             tk[i] ^= tk[i - 1];
16947                         }
16948                     }
16949
16950                     // copy values into round key arrays
16951                     var i = 0, r, c;
16952                     while (i < KC && t < roundKeyCount) {
16953                         r = t >> 2;
16954                         c = t % 4;
16955                         this._Ke[r][c] = tk[i];
16956                         this._Kd[rounds - r][c] = tk[i++];
16957                         t++;
16958                     }
16959                 }
16960
16961                 // inverse-cipher-ify the decryption round key (fips-197 section 5.3)
16962                 for (var r = 1; r < rounds; r++) {
16963                     for (var c = 0; c < 4; c++) {
16964                         tt = this._Kd[r][c];
16965                         this._Kd[r][c] = (U1[(tt >> 24) & 0xFF] ^
16966                                           U2[(tt >> 16) & 0xFF] ^
16967                                           U3[(tt >>  8) & 0xFF] ^
16968                                           U4[ tt        & 0xFF]);
16969                     }
16970                 }
16971             };
16972
16973             AES.prototype.encrypt = function(plaintext) {
16974                 if (plaintext.length != 16) {
16975                     throw new Error('invalid plaintext size (must be 16 bytes)');
16976                 }
16977
16978                 var rounds = this._Ke.length - 1;
16979                 var a = [0, 0, 0, 0];
16980
16981                 // convert plaintext to (ints ^ key)
16982                 var t = convertToInt32(plaintext);
16983                 for (var i = 0; i < 4; i++) {
16984                     t[i] ^= this._Ke[0][i];
16985                 }
16986
16987                 // apply round transforms
16988                 for (var r = 1; r < rounds; r++) {
16989                     for (var i = 0; i < 4; i++) {
16990                         a[i] = (T1[(t[ i         ] >> 24) & 0xff] ^
16991                                 T2[(t[(i + 1) % 4] >> 16) & 0xff] ^
16992                                 T3[(t[(i + 2) % 4] >>  8) & 0xff] ^
16993                                 T4[ t[(i + 3) % 4]        & 0xff] ^
16994                                 this._Ke[r][i]);
16995                     }
16996                     t = a.slice();
16997                 }
16998
16999                 // the last round is special
17000                 var result = createArray(16), tt;
17001                 for (var i = 0; i < 4; i++) {
17002                     tt = this._Ke[rounds][i];
17003                     result[4 * i    ] = (S[(t[ i         ] >> 24) & 0xff] ^ (tt >> 24)) & 0xff;
17004                     result[4 * i + 1] = (S[(t[(i + 1) % 4] >> 16) & 0xff] ^ (tt >> 16)) & 0xff;
17005                     result[4 * i + 2] = (S[(t[(i + 2) % 4] >>  8) & 0xff] ^ (tt >>  8)) & 0xff;
17006                     result[4 * i + 3] = (S[ t[(i + 3) % 4]        & 0xff] ^  tt       ) & 0xff;
17007                 }
17008
17009                 return result;
17010             };
17011
17012             AES.prototype.decrypt = function(ciphertext) {
17013                 if (ciphertext.length != 16) {
17014                     throw new Error('invalid ciphertext size (must be 16 bytes)');
17015                 }
17016
17017                 var rounds = this._Kd.length - 1;
17018                 var a = [0, 0, 0, 0];
17019
17020                 // convert plaintext to (ints ^ key)
17021                 var t = convertToInt32(ciphertext);
17022                 for (var i = 0; i < 4; i++) {
17023                     t[i] ^= this._Kd[0][i];
17024                 }
17025
17026                 // apply round transforms
17027                 for (var r = 1; r < rounds; r++) {
17028                     for (var i = 0; i < 4; i++) {
17029                         a[i] = (T5[(t[ i          ] >> 24) & 0xff] ^
17030                                 T6[(t[(i + 3) % 4] >> 16) & 0xff] ^
17031                                 T7[(t[(i + 2) % 4] >>  8) & 0xff] ^
17032                                 T8[ t[(i + 1) % 4]        & 0xff] ^
17033                                 this._Kd[r][i]);
17034                     }
17035                     t = a.slice();
17036                 }
17037
17038                 // the last round is special
17039                 var result = createArray(16), tt;
17040                 for (var i = 0; i < 4; i++) {
17041                     tt = this._Kd[rounds][i];
17042                     result[4 * i    ] = (Si[(t[ i         ] >> 24) & 0xff] ^ (tt >> 24)) & 0xff;
17043                     result[4 * i + 1] = (Si[(t[(i + 3) % 4] >> 16) & 0xff] ^ (tt >> 16)) & 0xff;
17044                     result[4 * i + 2] = (Si[(t[(i + 2) % 4] >>  8) & 0xff] ^ (tt >>  8)) & 0xff;
17045                     result[4 * i + 3] = (Si[ t[(i + 1) % 4]        & 0xff] ^  tt       ) & 0xff;
17046                 }
17047
17048                 return result;
17049             };
17050
17051
17052             /**
17053              *  Mode Of Operation - Electonic Codebook (ECB)
17054              */
17055             var ModeOfOperationECB = function(key) {
17056                 if (!(this instanceof ModeOfOperationECB)) {
17057                     throw Error('AES must be instanitated with `new`');
17058                 }
17059
17060                 this.description = "Electronic Code Block";
17061                 this.name = "ecb";
17062
17063                 this._aes = new AES(key);
17064             };
17065
17066             ModeOfOperationECB.prototype.encrypt = function(plaintext) {
17067                 plaintext = coerceArray(plaintext);
17068
17069                 if ((plaintext.length % 16) !== 0) {
17070                     throw new Error('invalid plaintext size (must be multiple of 16 bytes)');
17071                 }
17072
17073                 var ciphertext = createArray(plaintext.length);
17074                 var block = createArray(16);
17075
17076                 for (var i = 0; i < plaintext.length; i += 16) {
17077                     copyArray(plaintext, block, 0, i, i + 16);
17078                     block = this._aes.encrypt(block);
17079                     copyArray(block, ciphertext, i);
17080                 }
17081
17082                 return ciphertext;
17083             };
17084
17085             ModeOfOperationECB.prototype.decrypt = function(ciphertext) {
17086                 ciphertext = coerceArray(ciphertext);
17087
17088                 if ((ciphertext.length % 16) !== 0) {
17089                     throw new Error('invalid ciphertext size (must be multiple of 16 bytes)');
17090                 }
17091
17092                 var plaintext = createArray(ciphertext.length);
17093                 var block = createArray(16);
17094
17095                 for (var i = 0; i < ciphertext.length; i += 16) {
17096                     copyArray(ciphertext, block, 0, i, i + 16);
17097                     block = this._aes.decrypt(block);
17098                     copyArray(block, plaintext, i);
17099                 }
17100
17101                 return plaintext;
17102             };
17103
17104
17105             /**
17106              *  Mode Of Operation - Cipher Block Chaining (CBC)
17107              */
17108             var ModeOfOperationCBC = function(key, iv) {
17109                 if (!(this instanceof ModeOfOperationCBC)) {
17110                     throw Error('AES must be instanitated with `new`');
17111                 }
17112
17113                 this.description = "Cipher Block Chaining";
17114                 this.name = "cbc";
17115
17116                 if (!iv) {
17117                     iv = createArray(16);
17118
17119                 } else if (iv.length != 16) {
17120                     throw new Error('invalid initialation vector size (must be 16 bytes)');
17121                 }
17122
17123                 this._lastCipherblock = coerceArray(iv, true);
17124
17125                 this._aes = new AES(key);
17126             };
17127
17128             ModeOfOperationCBC.prototype.encrypt = function(plaintext) {
17129                 plaintext = coerceArray(plaintext);
17130
17131                 if ((plaintext.length % 16) !== 0) {
17132                     throw new Error('invalid plaintext size (must be multiple of 16 bytes)');
17133                 }
17134
17135                 var ciphertext = createArray(plaintext.length);
17136                 var block = createArray(16);
17137
17138                 for (var i = 0; i < plaintext.length; i += 16) {
17139                     copyArray(plaintext, block, 0, i, i + 16);
17140
17141                     for (var j = 0; j < 16; j++) {
17142                         block[j] ^= this._lastCipherblock[j];
17143                     }
17144
17145                     this._lastCipherblock = this._aes.encrypt(block);
17146                     copyArray(this._lastCipherblock, ciphertext, i);
17147                 }
17148
17149                 return ciphertext;
17150             };
17151
17152             ModeOfOperationCBC.prototype.decrypt = function(ciphertext) {
17153                 ciphertext = coerceArray(ciphertext);
17154
17155                 if ((ciphertext.length % 16) !== 0) {
17156                     throw new Error('invalid ciphertext size (must be multiple of 16 bytes)');
17157                 }
17158
17159                 var plaintext = createArray(ciphertext.length);
17160                 var block = createArray(16);
17161
17162                 for (var i = 0; i < ciphertext.length; i += 16) {
17163                     copyArray(ciphertext, block, 0, i, i + 16);
17164                     block = this._aes.decrypt(block);
17165
17166                     for (var j = 0; j < 16; j++) {
17167                         plaintext[i + j] = block[j] ^ this._lastCipherblock[j];
17168                     }
17169
17170                     copyArray(ciphertext, this._lastCipherblock, 0, i, i + 16);
17171                 }
17172
17173                 return plaintext;
17174             };
17175
17176
17177             /**
17178              *  Mode Of Operation - Cipher Feedback (CFB)
17179              */
17180             var ModeOfOperationCFB = function(key, iv, segmentSize) {
17181                 if (!(this instanceof ModeOfOperationCFB)) {
17182                     throw Error('AES must be instanitated with `new`');
17183                 }
17184
17185                 this.description = "Cipher Feedback";
17186                 this.name = "cfb";
17187
17188                 if (!iv) {
17189                     iv = createArray(16);
17190
17191                 } else if (iv.length != 16) {
17192                     throw new Error('invalid initialation vector size (must be 16 size)');
17193                 }
17194
17195                 if (!segmentSize) { segmentSize = 1; }
17196
17197                 this.segmentSize = segmentSize;
17198
17199                 this._shiftRegister = coerceArray(iv, true);
17200
17201                 this._aes = new AES(key);
17202             };
17203
17204             ModeOfOperationCFB.prototype.encrypt = function(plaintext) {
17205                 if ((plaintext.length % this.segmentSize) != 0) {
17206                     throw new Error('invalid plaintext size (must be segmentSize bytes)');
17207                 }
17208
17209                 var encrypted = coerceArray(plaintext, true);
17210
17211                 var xorSegment;
17212                 for (var i = 0; i < encrypted.length; i += this.segmentSize) {
17213                     xorSegment = this._aes.encrypt(this._shiftRegister);
17214                     for (var j = 0; j < this.segmentSize; j++) {
17215                         encrypted[i + j] ^= xorSegment[j];
17216                     }
17217
17218                     // Shift the register
17219                     copyArray(this._shiftRegister, this._shiftRegister, 0, this.segmentSize);
17220                     copyArray(encrypted, this._shiftRegister, 16 - this.segmentSize, i, i + this.segmentSize);
17221                 }
17222
17223                 return encrypted;
17224             };
17225
17226             ModeOfOperationCFB.prototype.decrypt = function(ciphertext) {
17227                 if ((ciphertext.length % this.segmentSize) != 0) {
17228                     throw new Error('invalid ciphertext size (must be segmentSize bytes)');
17229                 }
17230
17231                 var plaintext = coerceArray(ciphertext, true);
17232
17233                 var xorSegment;
17234                 for (var i = 0; i < plaintext.length; i += this.segmentSize) {
17235                     xorSegment = this._aes.encrypt(this._shiftRegister);
17236
17237                     for (var j = 0; j < this.segmentSize; j++) {
17238                         plaintext[i + j] ^= xorSegment[j];
17239                     }
17240
17241                     // Shift the register
17242                     copyArray(this._shiftRegister, this._shiftRegister, 0, this.segmentSize);
17243                     copyArray(ciphertext, this._shiftRegister, 16 - this.segmentSize, i, i + this.segmentSize);
17244                 }
17245
17246                 return plaintext;
17247             };
17248
17249             /**
17250              *  Mode Of Operation - Output Feedback (OFB)
17251              */
17252             var ModeOfOperationOFB = function(key, iv) {
17253                 if (!(this instanceof ModeOfOperationOFB)) {
17254                     throw Error('AES must be instanitated with `new`');
17255                 }
17256
17257                 this.description = "Output Feedback";
17258                 this.name = "ofb";
17259
17260                 if (!iv) {
17261                     iv = createArray(16);
17262
17263                 } else if (iv.length != 16) {
17264                     throw new Error('invalid initialation vector size (must be 16 bytes)');
17265                 }
17266
17267                 this._lastPrecipher = coerceArray(iv, true);
17268                 this._lastPrecipherIndex = 16;
17269
17270                 this._aes = new AES(key);
17271             };
17272
17273             ModeOfOperationOFB.prototype.encrypt = function(plaintext) {
17274                 var encrypted = coerceArray(plaintext, true);
17275
17276                 for (var i = 0; i < encrypted.length; i++) {
17277                     if (this._lastPrecipherIndex === 16) {
17278                         this._lastPrecipher = this._aes.encrypt(this._lastPrecipher);
17279                         this._lastPrecipherIndex = 0;
17280                     }
17281                     encrypted[i] ^= this._lastPrecipher[this._lastPrecipherIndex++];
17282                 }
17283
17284                 return encrypted;
17285             };
17286
17287             // Decryption is symetric
17288             ModeOfOperationOFB.prototype.decrypt = ModeOfOperationOFB.prototype.encrypt;
17289
17290
17291             /**
17292              *  Counter object for CTR common mode of operation
17293              */
17294             var Counter = function(initialValue) {
17295                 if (!(this instanceof Counter)) {
17296                     throw Error('Counter must be instanitated with `new`');
17297                 }
17298
17299                 // We allow 0, but anything false-ish uses the default 1
17300                 if (initialValue !== 0 && !initialValue) { initialValue = 1; }
17301
17302                 if (typeof(initialValue) === 'number') {
17303                     this._counter = createArray(16);
17304                     this.setValue(initialValue);
17305
17306                 } else {
17307                     this.setBytes(initialValue);
17308                 }
17309             };
17310
17311             Counter.prototype.setValue = function(value) {
17312                 if (typeof(value) !== 'number' || parseInt(value) != value) {
17313                     throw new Error('invalid counter value (must be an integer)');
17314                 }
17315
17316                 // We cannot safely handle numbers beyond the safe range for integers
17317                 if (value > Number.MAX_SAFE_INTEGER) {
17318                     throw new Error('integer value out of safe range');
17319                 }
17320
17321                 for (var index = 15; index >= 0; --index) {
17322                     this._counter[index] = value % 256;
17323                     value = parseInt(value / 256);
17324                 }
17325             };
17326
17327             Counter.prototype.setBytes = function(bytes) {
17328                 bytes = coerceArray(bytes, true);
17329
17330                 if (bytes.length != 16) {
17331                     throw new Error('invalid counter bytes size (must be 16 bytes)');
17332                 }
17333
17334                 this._counter = bytes;
17335             };
17336
17337             Counter.prototype.increment = function() {
17338                 for (var i = 15; i >= 0; i--) {
17339                     if (this._counter[i] === 255) {
17340                         this._counter[i] = 0;
17341                     } else {
17342                         this._counter[i]++;
17343                         break;
17344                     }
17345                 }
17346             };
17347
17348
17349             /**
17350              *  Mode Of Operation - Counter (CTR)
17351              */
17352             var ModeOfOperationCTR = function(key, counter) {
17353                 if (!(this instanceof ModeOfOperationCTR)) {
17354                     throw Error('AES must be instanitated with `new`');
17355                 }
17356
17357                 this.description = "Counter";
17358                 this.name = "ctr";
17359
17360                 if (!(counter instanceof Counter)) {
17361                     counter = new Counter(counter);
17362                 }
17363
17364                 this._counter = counter;
17365
17366                 this._remainingCounter = null;
17367                 this._remainingCounterIndex = 16;
17368
17369                 this._aes = new AES(key);
17370             };
17371
17372             ModeOfOperationCTR.prototype.encrypt = function(plaintext) {
17373                 var encrypted = coerceArray(plaintext, true);
17374
17375                 for (var i = 0; i < encrypted.length; i++) {
17376                     if (this._remainingCounterIndex === 16) {
17377                         this._remainingCounter = this._aes.encrypt(this._counter._counter);
17378                         this._remainingCounterIndex = 0;
17379                         this._counter.increment();
17380                     }
17381                     encrypted[i] ^= this._remainingCounter[this._remainingCounterIndex++];
17382                 }
17383
17384                 return encrypted;
17385             };
17386
17387             // Decryption is symetric
17388             ModeOfOperationCTR.prototype.decrypt = ModeOfOperationCTR.prototype.encrypt;
17389
17390
17391             ///////////////////////
17392             // Padding
17393
17394             // See:https://tools.ietf.org/html/rfc2315
17395             function pkcs7pad(data) {
17396                 data = coerceArray(data, true);
17397                 var padder = 16 - (data.length % 16);
17398                 var result = createArray(data.length + padder);
17399                 copyArray(data, result);
17400                 for (var i = data.length; i < result.length; i++) {
17401                     result[i] = padder;
17402                 }
17403                 return result;
17404             }
17405
17406             function pkcs7strip(data) {
17407                 data = coerceArray(data, true);
17408                 if (data.length < 16) { throw new Error('PKCS#7 invalid length'); }
17409
17410                 var padder = data[data.length - 1];
17411                 if (padder > 16) { throw new Error('PKCS#7 padding byte out of range'); }
17412
17413                 var length = data.length - padder;
17414                 for (var i = 0; i < padder; i++) {
17415                     if (data[length + i] !== padder) {
17416                         throw new Error('PKCS#7 invalid padding byte');
17417                     }
17418                 }
17419
17420                 var result = createArray(length);
17421                 copyArray(data, result, 0, 0, length);
17422                 return result;
17423             }
17424
17425             ///////////////////////
17426             // Exporting
17427
17428
17429             // The block cipher
17430             var aesjs = {
17431                 AES: AES,
17432                 Counter: Counter,
17433
17434                 ModeOfOperation: {
17435                     ecb: ModeOfOperationECB,
17436                     cbc: ModeOfOperationCBC,
17437                     cfb: ModeOfOperationCFB,
17438                     ofb: ModeOfOperationOFB,
17439                     ctr: ModeOfOperationCTR
17440                 },
17441
17442                 utils: {
17443                     hex: convertHex,
17444                     utf8: convertUtf8
17445                 },
17446
17447                 padding: {
17448                     pkcs7: {
17449                         pad: pkcs7pad,
17450                         strip: pkcs7strip
17451                     }
17452                 },
17453
17454                 _arrayTest: {
17455                     coerceArray: coerceArray,
17456                     createArray: createArray,
17457                     copyArray: copyArray,
17458                 }
17459             };
17460
17461
17462             // node.js
17463             {
17464                 module.exports = aesjs;
17465
17466             // RequireJS/AMD
17467             // http://www.requirejs.org/docs/api.html
17468             // https://github.com/amdjs/amdjs-api/wiki/AMD
17469             }
17470
17471
17472         })();
17473         });
17474
17475         // See https://github.com/ricmoo/aes-js
17476         // We can use keys that are 128 bits (16 bytes), 192 bits (24 bytes) or 256 bits (32 bytes).
17477         // To generate a random key:  window.crypto.getRandomValues(new Uint8Array(16));
17478
17479         // This default signing key is built into iD and can be used to mask/unmask sensitive values.
17480         const DEFAULT_128 = [250, 157, 60, 79, 142, 134, 229, 129, 138, 126, 210, 129, 29, 71, 160, 208];
17481
17482
17483         function utilAesEncrypt(text, key) {
17484           key = key || DEFAULT_128;
17485           const textBytes = aesJs.utils.utf8.toBytes(text);
17486           const aesCtr = new aesJs.ModeOfOperation.ctr(key);
17487           const encryptedBytes = aesCtr.encrypt(textBytes);
17488           const encryptedHex = aesJs.utils.hex.fromBytes(encryptedBytes);
17489           return encryptedHex;
17490         }
17491
17492
17493         function utilAesDecrypt(encryptedHex, key) {
17494           key = key || DEFAULT_128;
17495           const encryptedBytes = aesJs.utils.hex.toBytes(encryptedHex);
17496           const aesCtr = new aesJs.ModeOfOperation.ctr(key);
17497           const decryptedBytes = aesCtr.decrypt(encryptedBytes);
17498           const text = aesJs.utils.utf8.fromBytes(decryptedBytes);
17499           return text;
17500         }
17501
17502         function utilCleanTags(tags) {
17503             var out = {};
17504             for (var k in tags) {
17505                 if (!k) continue;
17506                 var v = tags[k];
17507                 if (v !== undefined) {
17508                     out[k] = cleanValue(k, v);
17509                 }
17510             }
17511
17512             return out;
17513
17514
17515             function cleanValue(k, v) {
17516                 function keepSpaces(k) {
17517                     return /_hours|_times|:conditional$/.test(k);
17518                 }
17519
17520                 function skip(k) {
17521                     return /^(description|note|fixme)$/.test(k);
17522                 }
17523
17524                 if (skip(k)) return v;
17525
17526                 var cleaned = v
17527                     .split(';')
17528                     .map(function(s) { return s.trim(); })
17529                     .join(keepSpaces(k) ? '; ' : ';');
17530
17531                 // The code below is not intended to validate websites and emails.
17532                 // It is only intended to prevent obvious copy-paste errors. (#2323)
17533                 // clean website- and email-like tags
17534                 if (k.indexOf('website') !== -1 ||
17535                     k.indexOf('email') !== -1 ||
17536                     cleaned.indexOf('http') === 0) {
17537                     cleaned = cleaned
17538                         .replace(/[\u200B-\u200F\uFEFF]/g, '');  // strip LRM and other zero width chars
17539
17540                 }
17541
17542                 return cleaned;
17543             }
17544         }
17545
17546         // Like selection.property('value', ...), but avoids no-op value sets,
17547         // which can result in layout/repaint thrashing in some situations.
17548         function utilGetSetValue(selection, value) {
17549             function d3_selection_value(value) {
17550                 function valueNull() {
17551                     delete this.value;
17552                 }
17553
17554                 function valueConstant() {
17555                     if (this.value !== value) {
17556                         this.value = value;
17557                     }
17558                 }
17559
17560                 function valueFunction() {
17561                     var x = value.apply(this, arguments);
17562                     if (x == null) {
17563                         delete this.value;
17564                     } else if (this.value !== x) {
17565                         this.value = x;
17566                     }
17567                 }
17568
17569                 return value == null
17570                     ? valueNull : (typeof value === 'function'
17571                     ? valueFunction : valueConstant);
17572             }
17573
17574             if (arguments.length === 1) {
17575                 return selection.property('value');
17576             }
17577
17578             return selection.each(d3_selection_value(value));
17579         }
17580
17581         function utilKeybinding(namespace) {
17582             var _keybindings = {};
17583
17584
17585             function testBindings(isCapturing) {
17586                 var didMatch = false;
17587                 var bindings = Object.keys(_keybindings).map(function(id) { return _keybindings[id]; });
17588                 var i, binding;
17589
17590                 // Most key shortcuts will accept either lower or uppercase ('h' or 'H'),
17591                 // so we don't strictly match on the shift key, but we prioritize
17592                 // shifted keybindings first, and fallback to unshifted only if no match.
17593                 // (This lets us differentiate between '←'/'⇧←' or '⌘Z'/'⌘⇧Z')
17594
17595                 // priority match shifted keybindings first
17596                 for (i = 0; i < bindings.length; i++) {
17597                     binding = bindings[i];
17598                     if (!binding.event.modifiers.shiftKey) continue;  // no shift
17599                     if (!!binding.capture !== isCapturing) continue;
17600                     if (matches(binding, true)) {
17601                         binding.callback();
17602                         didMatch = true;
17603                     }
17604                 }
17605
17606                 // then unshifted keybindings
17607                 if (didMatch) return;
17608                 for (i = 0; i < bindings.length; i++) {
17609                     binding = bindings[i];
17610                     if (binding.event.modifiers.shiftKey) continue;   // shift
17611                     if (!!binding.capture !== isCapturing) continue;
17612                     if (matches(binding, false)) {
17613                         binding.callback();
17614                     }
17615                 }
17616
17617
17618                 function matches(binding, testShift) {
17619                     var event$1 = event;
17620                     var isMatch = false;
17621                     var tryKeyCode = true;
17622
17623                     // Prefer a match on `KeyboardEvent.key`
17624                     if (event$1.key !== undefined) {
17625                         tryKeyCode = (event$1.key.charCodeAt(0) > 255);  // outside ISO-Latin-1
17626                         isMatch = true;
17627
17628                         if (binding.event.key === undefined) {
17629                             isMatch = false;
17630                         } else if (Array.isArray(binding.event.key)) {
17631                             if (binding.event.key.map(function(s) { return s.toLowerCase(); }).indexOf(event$1.key.toLowerCase()) === -1)
17632                                 isMatch = false;
17633                         } else {
17634                             if (event$1.key.toLowerCase() !== binding.event.key.toLowerCase())
17635                                 isMatch = false;
17636                         }
17637                     }
17638
17639                     // Fallback match on `KeyboardEvent.keyCode`, can happen if:
17640                     // - browser doesn't support `KeyboardEvent.key`
17641                     // - `KeyboardEvent.key` is outside ISO-Latin-1 range (cyrillic?)
17642                     if (!isMatch && tryKeyCode) {
17643                         isMatch = (event$1.keyCode === binding.event.keyCode);
17644                     }
17645
17646                     if (!isMatch) return false;
17647
17648                     // test modifier keys
17649                     if (!(event$1.ctrlKey && event$1.altKey)) {  // if both are set, assume AltGr and skip it - #4096
17650                         if (event$1.ctrlKey !== binding.event.modifiers.ctrlKey) return false;
17651                         if (event$1.altKey !== binding.event.modifiers.altKey) return false;
17652                     }
17653                     if (event$1.metaKey !== binding.event.modifiers.metaKey) return false;
17654                     if (testShift && event$1.shiftKey !== binding.event.modifiers.shiftKey) return false;
17655
17656                     return true;
17657                 }
17658             }
17659
17660
17661             function capture() {
17662                 testBindings(true);
17663             }
17664
17665
17666             function bubble() {
17667                 var tagName = select(event.target).node().tagName;
17668                 if (tagName === 'INPUT' || tagName === 'SELECT' || tagName === 'TEXTAREA') {
17669                     return;
17670                 }
17671                 testBindings(false);
17672             }
17673
17674
17675             function keybinding(selection) {
17676                 selection = selection || select(document);
17677                 selection.on('keydown.capture.' + namespace, capture, true);
17678                 selection.on('keydown.bubble.' + namespace, bubble, false);
17679                 return keybinding;
17680             }
17681
17682             // was: keybinding.off()
17683             keybinding.unbind = function(selection) {
17684                 _keybindings = [];
17685                 selection = selection || select(document);
17686                 selection.on('keydown.capture.' + namespace, null);
17687                 selection.on('keydown.bubble.' + namespace, null);
17688                 return keybinding;
17689             };
17690
17691
17692             keybinding.clear = function() {
17693                 _keybindings = {};
17694                 return keybinding;
17695             };
17696
17697
17698             // Remove one or more keycode bindings.
17699             keybinding.off = function(codes, capture) {
17700                 var arr = utilArrayUniq([].concat(codes));
17701
17702                 for (var i = 0; i < arr.length; i++) {
17703                     var id = arr[i] + (capture ? '-capture' : '-bubble');
17704                     delete _keybindings[id];
17705                 }
17706                 return keybinding;
17707             };
17708
17709
17710             // Add one or more keycode bindings.
17711             keybinding.on = function(codes, callback, capture) {
17712                 if (typeof callback !== 'function') {
17713                     return keybinding.off(codes, capture);
17714                 }
17715
17716                 var arr = utilArrayUniq([].concat(codes));
17717
17718                 for (var i = 0; i < arr.length; i++) {
17719                     var id = arr[i] + (capture ? '-capture' : '-bubble');
17720                     var binding = {
17721                         id: id,
17722                         capture: capture,
17723                         callback: callback,
17724                         event: {
17725                             key: undefined,  // preferred
17726                             keyCode: 0,      // fallback
17727                             modifiers: {
17728                                 shiftKey: false,
17729                                 ctrlKey: false,
17730                                 altKey: false,
17731                                 metaKey: false
17732                             }
17733                         }
17734                     };
17735
17736                     if (_keybindings[id]) {
17737                         console.warn('warning: duplicate keybinding for "' + id + '"'); // eslint-disable-line no-console
17738                     }
17739
17740                     _keybindings[id] = binding;
17741
17742                     var matches = arr[i].toLowerCase().match(/(?:(?:[^+⇧⌃⌥⌘])+|[⇧⌃⌥⌘]|\+\+|^\+$)/g);
17743                     for (var j = 0; j < matches.length; j++) {
17744                         // Normalise matching errors
17745                         if (matches[j] === '++') matches[j] = '+';
17746
17747                         if (matches[j] in utilKeybinding.modifierCodes) {
17748                             var prop = utilKeybinding.modifierProperties[utilKeybinding.modifierCodes[matches[j]]];
17749                             binding.event.modifiers[prop] = true;
17750                         } else {
17751                             binding.event.key = utilKeybinding.keys[matches[j]] || matches[j];
17752                             if (matches[j] in utilKeybinding.keyCodes) {
17753                                 binding.event.keyCode = utilKeybinding.keyCodes[matches[j]];
17754                             }
17755                         }
17756                     }
17757                 }
17758
17759                 return keybinding;
17760             };
17761
17762
17763             return keybinding;
17764         }
17765
17766
17767         /*
17768          * See https://github.com/keithamus/jwerty
17769          */
17770
17771         utilKeybinding.modifierCodes = {
17772             // Shift key, ⇧
17773             '⇧': 16, shift: 16,
17774             // CTRL key, on Mac: ⌃
17775             '⌃': 17, ctrl: 17,
17776             // ALT key, on Mac: ⌥ (Alt)
17777             '⌥': 18, alt: 18, option: 18,
17778             // META, on Mac: ⌘ (CMD), on Windows (Win), on Linux (Super)
17779             '⌘': 91, meta: 91, cmd: 91, 'super': 91, win: 91
17780         };
17781
17782         utilKeybinding.modifierProperties = {
17783             16: 'shiftKey',
17784             17: 'ctrlKey',
17785             18: 'altKey',
17786             91: 'metaKey'
17787         };
17788
17789         utilKeybinding.keys = {
17790             // Backspace key, on Mac: ⌫ (Backspace)
17791             '⌫': 'Backspace', backspace: 'Backspace',
17792             // Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥
17793             '⇥': 'Tab', '⇆': 'Tab', tab: 'Tab',
17794             // Return key, ↩
17795             '↩': 'Enter', 'return': 'Enter', enter: 'Enter', '⌅': 'Enter',
17796             // Pause/Break key
17797             'pause': 'Pause', 'pause-break': 'Pause',
17798             // Caps Lock key, ⇪
17799             '⇪': 'CapsLock', caps: 'CapsLock', 'caps-lock': 'CapsLock',
17800             // Escape key, on Mac: ⎋, on Windows: Esc
17801             '⎋': ['Escape', 'Esc'], escape: ['Escape', 'Esc'], esc: ['Escape', 'Esc'],
17802             // Space key
17803             space: [' ', 'Spacebar'],
17804             // Page-Up key, or pgup, on Mac: ↖
17805             '↖': 'PageUp', pgup: 'PageUp', 'page-up': 'PageUp',
17806             // Page-Down key, or pgdown, on Mac: ↘
17807             '↘': 'PageDown', pgdown: 'PageDown', 'page-down': 'PageDown',
17808             // END key, on Mac: ⇟
17809             '⇟': 'End', end: 'End',
17810             // HOME key, on Mac: ⇞
17811             '⇞': 'Home', home: 'Home',
17812             // Insert key, or ins
17813             ins: 'Insert', insert: 'Insert',
17814             // Delete key, on Mac: ⌦ (Delete)
17815             '⌦': ['Delete', 'Del'], del: ['Delete', 'Del'], 'delete': ['Delete', 'Del'],
17816             // Left Arrow Key, or ←
17817             '←': ['ArrowLeft', 'Left'], left: ['ArrowLeft', 'Left'], 'arrow-left': ['ArrowLeft', 'Left'],
17818             // Up Arrow Key, or ↑
17819             '↑': ['ArrowUp', 'Up'], up: ['ArrowUp', 'Up'], 'arrow-up': ['ArrowUp', 'Up'],
17820             // Right Arrow Key, or →
17821             '→': ['ArrowRight', 'Right'], right: ['ArrowRight', 'Right'], 'arrow-right': ['ArrowRight', 'Right'],
17822             // Up Arrow Key, or ↓
17823             '↓': ['ArrowDown', 'Down'], down: ['ArrowDown', 'Down'], 'arrow-down': ['ArrowDown', 'Down'],
17824             // odities, stuff for backward compatibility (browsers and code):
17825             // Num-Multiply, or *
17826             '*': ['*', 'Multiply'], star: ['*', 'Multiply'], asterisk: ['*', 'Multiply'], multiply: ['*', 'Multiply'],
17827             // Num-Plus or +
17828             '+': ['+', 'Add'], 'plus': ['+', 'Add'],
17829             // Num-Subtract, or -
17830             '-': ['-', 'Subtract'], subtract: ['-', 'Subtract'], 'dash': ['-', 'Subtract'],
17831             // Semicolon
17832             semicolon: ';',
17833             // = or equals
17834             equals: '=',
17835             // Comma, or ,
17836             comma: ',',
17837             // Period, or ., or full-stop
17838             period: '.', 'full-stop': '.',
17839             // Slash, or /, or forward-slash
17840             slash: '/', 'forward-slash': '/',
17841             // Tick, or `, or back-quote
17842             tick: '`', 'back-quote': '`',
17843             // Open bracket, or [
17844             'open-bracket': '[',
17845             // Back slash, or \
17846             'back-slash': '\\',
17847             // Close backet, or ]
17848             'close-bracket': ']',
17849             // Apostrophe, or Quote, or '
17850             quote: '\'', apostrophe: '\'',
17851             // NUMPAD 0-9
17852             'num-0': '0',
17853             'num-1': '1',
17854             'num-2': '2',
17855             'num-3': '3',
17856             'num-4': '4',
17857             'num-5': '5',
17858             'num-6': '6',
17859             'num-7': '7',
17860             'num-8': '8',
17861             'num-9': '9',
17862             // F1-F25
17863             f1: 'F1',
17864             f2: 'F2',
17865             f3: 'F3',
17866             f4: 'F4',
17867             f5: 'F5',
17868             f6: 'F6',
17869             f7: 'F7',
17870             f8: 'F8',
17871             f9: 'F9',
17872             f10: 'F10',
17873             f11: 'F11',
17874             f12: 'F12',
17875             f13: 'F13',
17876             f14: 'F14',
17877             f15: 'F15',
17878             f16: 'F16',
17879             f17: 'F17',
17880             f18: 'F18',
17881             f19: 'F19',
17882             f20: 'F20',
17883             f21: 'F21',
17884             f22: 'F22',
17885             f23: 'F23',
17886             f24: 'F24',
17887             f25: 'F25'
17888         };
17889
17890         utilKeybinding.keyCodes = {
17891             // Backspace key, on Mac: ⌫ (Backspace)
17892             '⌫': 8, backspace: 8,
17893             // Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥
17894             '⇥': 9, '⇆': 9, tab: 9,
17895             // Return key, ↩
17896             '↩': 13, 'return': 13, enter: 13, '⌅': 13,
17897             // Pause/Break key
17898             'pause': 19, 'pause-break': 19,
17899             // Caps Lock key, ⇪
17900             '⇪': 20, caps: 20, 'caps-lock': 20,
17901             // Escape key, on Mac: ⎋, on Windows: Esc
17902             '⎋': 27, escape: 27, esc: 27,
17903             // Space key
17904             space: 32,
17905             // Page-Up key, or pgup, on Mac: ↖
17906             '↖': 33, pgup: 33, 'page-up': 33,
17907             // Page-Down key, or pgdown, on Mac: ↘
17908             '↘': 34, pgdown: 34, 'page-down': 34,
17909             // END key, on Mac: ⇟
17910             '⇟': 35, end: 35,
17911             // HOME key, on Mac: ⇞
17912             '⇞': 36, home: 36,
17913             // Insert key, or ins
17914             ins: 45, insert: 45,
17915             // Delete key, on Mac: ⌦ (Delete)
17916             '⌦': 46, del: 46, 'delete': 46,
17917             // Left Arrow Key, or ←
17918             '←': 37, left: 37, 'arrow-left': 37,
17919             // Up Arrow Key, or ↑
17920             '↑': 38, up: 38, 'arrow-up': 38,
17921             // Right Arrow Key, or →
17922             '→': 39, right: 39, 'arrow-right': 39,
17923             // Up Arrow Key, or ↓
17924             '↓': 40, down: 40, 'arrow-down': 40,
17925             // odities, printing characters that come out wrong:
17926             // Firefox Equals
17927             'ffequals': 61,
17928             // Num-Multiply, or *
17929             '*': 106, star: 106, asterisk: 106, multiply: 106,
17930             // Num-Plus or +
17931             '+': 107, 'plus': 107,
17932             // Num-Subtract, or -
17933             '-': 109, subtract: 109,
17934             // Firefox Plus
17935             'ffplus': 171,
17936             // Firefox Minus
17937             'ffminus': 173,
17938             // Semicolon
17939             ';': 186, semicolon: 186,
17940             // = or equals
17941             '=': 187, 'equals': 187,
17942             // Comma, or ,
17943             ',': 188, comma: 188,
17944             // Dash / Underscore key
17945             'dash': 189,
17946             // Period, or ., or full-stop
17947             '.': 190, period: 190, 'full-stop': 190,
17948             // Slash, or /, or forward-slash
17949             '/': 191, slash: 191, 'forward-slash': 191,
17950             // Tick, or `, or back-quote
17951             '`': 192, tick: 192, 'back-quote': 192,
17952             // Open bracket, or [
17953             '[': 219, 'open-bracket': 219,
17954             // Back slash, or \
17955             '\\': 220, 'back-slash': 220,
17956             // Close backet, or ]
17957             ']': 221, 'close-bracket': 221,
17958             // Apostrophe, or Quote, or '
17959             '\'': 222, quote: 222, apostrophe: 222
17960         };
17961
17962         // NUMPAD 0-9
17963         var i$2 = 95, n = 0;
17964         while (++i$2 < 106) {
17965             utilKeybinding.keyCodes['num-' + n] = i$2;
17966             ++n;
17967         }
17968
17969         // 0-9
17970         i$2 = 47; n = 0;
17971         while (++i$2 < 58) {
17972             utilKeybinding.keyCodes[n] = i$2;
17973             ++n;
17974         }
17975
17976         // F1-F25
17977         i$2 = 111; n = 1;
17978         while (++i$2 < 136) {
17979             utilKeybinding.keyCodes['f' + n] = i$2;
17980             ++n;
17981         }
17982
17983         // a-z
17984         i$2 = 64;
17985         while (++i$2 < 91) {
17986             utilKeybinding.keyCodes[String.fromCharCode(i$2).toLowerCase()] = i$2;
17987         }
17988
17989         function utilObjectOmit(obj, omitKeys) {
17990             return Object.keys(obj).reduce(function(result, key) {
17991                 if (omitKeys.indexOf(key) === -1) {
17992                     result[key] = obj[key];  // keep
17993                 }
17994                 return result;
17995             }, {});
17996         }
17997
17998         // Copies a variable number of methods from source to target.
17999         function utilRebind(target, source) {
18000             var i = 1, n = arguments.length, method;
18001             while (++i < n) {
18002                 target[method = arguments[i]] = d3_rebind(target, source, source[method]);
18003             }
18004             return target;
18005         }
18006
18007         // Method is assumed to be a standard D3 getter-setter:
18008         // If passed with no arguments, gets the value.
18009         // If passed with arguments, sets the value and returns the target.
18010         function d3_rebind(target, source, method) {
18011             return function() {
18012                 var value = method.apply(source, arguments);
18013                 return value === source ? target : value;
18014             };
18015         }
18016
18017         // A per-domain session mutex backed by a cookie and dead man's
18018         // switch. If the session crashes, the mutex will auto-release
18019         // after 5 seconds.
18020
18021         // This accepts a string and returns an object that complies with utilSessionMutexType
18022         function utilSessionMutex(name) {
18023             var mutex = {};
18024             var intervalID;
18025
18026             function renew() {
18027                 var expires = new Date();
18028                 expires.setSeconds(expires.getSeconds() + 5);
18029                 document.cookie = name + '=1; expires=' + expires.toUTCString() + '; sameSite=strict';
18030             }
18031
18032             mutex.lock = function () {
18033                 if (intervalID) return true;
18034                 var cookie = document.cookie.replace(new RegExp('(?:(?:^|.*;)\\s*' + name + '\\s*\\=\\s*([^;]*).*$)|^.*$'), '$1');
18035                 if (cookie) return false;
18036                 renew();
18037                 intervalID = window.setInterval(renew, 4000);
18038                 return true;
18039             };
18040
18041             mutex.unlock = function () {
18042                 if (!intervalID) return;
18043                 document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT; sameSite=strict';
18044                 clearInterval(intervalID);
18045                 intervalID = null;
18046             };
18047
18048             mutex.locked = function () {
18049                 return !!intervalID;
18050             };
18051
18052             return mutex;
18053         }
18054
18055         function utilTiler() {
18056             var _size = [256, 256];
18057             var _scale = 256;
18058             var _tileSize = 256;
18059             var _zoomExtent = [0, 20];
18060             var _translate = [_size[0] / 2, _size[1] / 2];
18061             var _margin = 0;
18062             var _skipNullIsland = false;
18063
18064
18065             function clamp(num, min, max) {
18066                 return Math.max(min, Math.min(num, max));
18067             }
18068
18069
18070             function nearNullIsland(tile) {
18071                 var x = tile[0];
18072                 var y = tile[1];
18073                 var z = tile[2];
18074                 if (z >= 7) {
18075                     var center = Math.pow(2, z - 1);
18076                     var width = Math.pow(2, z - 6);
18077                     var min = center - (width / 2);
18078                     var max = center + (width / 2) - 1;
18079                     return x >= min && x <= max && y >= min && y <= max;
18080                 }
18081                 return false;
18082             }
18083
18084
18085             function tiler() {
18086                 var z = geoScaleToZoom(_scale / (2 * Math.PI), _tileSize);
18087                 var z0 = clamp(Math.round(z), _zoomExtent[0], _zoomExtent[1]);
18088                 var tileMin = 0;
18089                 var tileMax = Math.pow(2, z0) - 1;
18090                 var log2ts = Math.log(_tileSize) * Math.LOG2E;
18091                 var k = Math.pow(2, z - z0 + log2ts);
18092                 var origin = [
18093                     (_translate[0] - _scale / 2) / k,
18094                     (_translate[1] - _scale / 2) / k
18095                 ];
18096
18097                 var cols = range$1(
18098                     clamp(Math.floor(-origin[0]) - _margin,               tileMin, tileMax + 1),
18099                     clamp(Math.ceil(_size[0] / k - origin[0]) + _margin,  tileMin, tileMax + 1)
18100                 );
18101                 var rows = range$1(
18102                     clamp(Math.floor(-origin[1]) - _margin,               tileMin, tileMax + 1),
18103                     clamp(Math.ceil(_size[1] / k - origin[1]) + _margin,  tileMin, tileMax + 1)
18104                 );
18105
18106                 var tiles = [];
18107                 for (var i = 0; i < rows.length; i++) {
18108                     var y = rows[i];
18109                     for (var j = 0; j < cols.length; j++) {
18110                         var x = cols[j];
18111
18112                         if (i >= _margin && i <= rows.length - _margin &&
18113                             j >= _margin && j <= cols.length - _margin) {
18114                             tiles.unshift([x, y, z0]);  // tiles in view at beginning
18115                         } else {
18116                             tiles.push([x, y, z0]);     // tiles in margin at the end
18117                         }
18118                     }
18119                 }
18120
18121                 tiles.translate = origin;
18122                 tiles.scale = k;
18123
18124                 return tiles;
18125             }
18126
18127
18128             /**
18129              * getTiles() returns an array of tiles that cover the map view
18130              */
18131             tiler.getTiles = function(projection) {
18132                 var origin = [
18133                     projection.scale() * Math.PI - projection.translate()[0],
18134                     projection.scale() * Math.PI - projection.translate()[1]
18135                 ];
18136
18137                 this
18138                     .size(projection.clipExtent()[1])
18139                     .scale(projection.scale() * 2 * Math.PI)
18140                     .translate(projection.translate());
18141
18142                 var tiles = tiler();
18143                 var ts = tiles.scale;
18144
18145                 return tiles
18146                     .map(function(tile) {
18147                         if (_skipNullIsland && nearNullIsland(tile)) {
18148                             return false;
18149                         }
18150                         var x = tile[0] * ts - origin[0];
18151                         var y = tile[1] * ts - origin[1];
18152                         return {
18153                             id: tile.toString(),
18154                             xyz: tile,
18155                             extent: geoExtent(
18156                                 projection.invert([x, y + ts]),
18157                                 projection.invert([x + ts, y])
18158                             )
18159                         };
18160                     }).filter(Boolean);
18161             };
18162
18163
18164             /**
18165              * getGeoJSON() returns a FeatureCollection for debugging tiles
18166              */
18167             tiler.getGeoJSON = function(projection) {
18168                 var features = tiler.getTiles(projection).map(function(tile) {
18169                     return {
18170                         type: 'Feature',
18171                         properties: {
18172                             id: tile.id,
18173                             name: tile.id
18174                         },
18175                         geometry: {
18176                             type: 'Polygon',
18177                             coordinates: [ tile.extent.polygon() ]
18178                         }
18179                     };
18180                 });
18181
18182                 return {
18183                     type: 'FeatureCollection',
18184                     features: features
18185                 };
18186             };
18187
18188
18189             tiler.tileSize = function(val) {
18190                 if (!arguments.length) return _tileSize;
18191                 _tileSize = val;
18192                 return tiler;
18193             };
18194
18195
18196             tiler.zoomExtent = function(val) {
18197                 if (!arguments.length) return _zoomExtent;
18198                 _zoomExtent = val;
18199                 return tiler;
18200             };
18201
18202
18203             tiler.size = function(val) {
18204                 if (!arguments.length) return _size;
18205                 _size = val;
18206                 return tiler;
18207             };
18208
18209
18210             tiler.scale = function(val) {
18211                 if (!arguments.length) return _scale;
18212                 _scale = val;
18213                 return tiler;
18214             };
18215
18216
18217             tiler.translate = function(val) {
18218                 if (!arguments.length) return _translate;
18219                 _translate = val;
18220                 return tiler;
18221             };
18222
18223
18224             // number to extend the rows/columns beyond those covering the viewport
18225             tiler.margin = function(val) {
18226                 if (!arguments.length) return _margin;
18227                 _margin = +val;
18228                 return tiler;
18229             };
18230
18231
18232             tiler.skipNullIsland = function(val) {
18233                 if (!arguments.length) return _skipNullIsland;
18234                 _skipNullIsland = val;
18235                 return tiler;
18236             };
18237
18238
18239             return tiler;
18240         }
18241
18242         function utilTriggerEvent(target, type) {
18243             target.each(function() {
18244                 var evt = document.createEvent('HTMLEvents');
18245                 evt.initEvent(type, true, true);
18246                 this.dispatchEvent(evt);
18247             });
18248         }
18249
18250         let _mainLocalizer = coreLocalizer(); // singleton
18251         let _t = _mainLocalizer.t;
18252
18253         //
18254         // coreLocalizer manages language and locale parameters including translated strings
18255         //
18256         function coreLocalizer() {
18257
18258             let localizer = {};
18259
18260             let _dataLanguages = {};
18261
18262             // `localeData` is an object containing all _supported_ locale codes -> language info.
18263             // {
18264             // en: { rtl: false, languageNames: {…}, scriptNames: {…} },
18265             // de: { rtl: false, languageNames: {…}, scriptNames: {…} },
18266             // …
18267             // }
18268             let _dataLocales = {};
18269
18270             // `localeStrings` is an object containing all _loaded_ locale codes -> string data.
18271             // {
18272             // en: { icons: {…}, toolbar: {…}, modes: {…}, operations: {…}, … },
18273             // de: { icons: {…}, toolbar: {…}, modes: {…}, operations: {…}, … },
18274             // …
18275             // }
18276             let _localeStrings = {};
18277
18278             // the current locale parameters
18279             let _localeCode = 'en-US';
18280             let _languageCode = 'en';
18281             let _textDirection = 'ltr';
18282             let _usesMetric = false;
18283             let _languageNames = {};
18284             let _scriptNames = {};
18285
18286             // getters for the current locale parameters
18287             localizer.localeCode = () => _localeCode;
18288             localizer.languageCode = () => _languageCode;
18289             localizer.textDirection = () => _textDirection;
18290             localizer.usesMetric = () => _usesMetric;
18291             localizer.languageNames = () => _languageNames;
18292             localizer.scriptNames = () => _scriptNames;
18293
18294
18295             var _loadPromise;
18296
18297             localizer.ensureLoaded = () => {
18298
18299                 if (_loadPromise) return _loadPromise;
18300
18301                 return _loadPromise = Promise.all([
18302                         // load the list of langauges
18303                         _mainFileFetcher.get('languages'),
18304                         // load the list of supported locales
18305                         _mainFileFetcher.get('locales')
18306                     ])
18307                     .then(results => {
18308                         _dataLanguages = results[0];
18309                         _dataLocales = results[1];
18310                     })
18311                     .then(() => {
18312                         const hash = utilStringQs(window.location.hash);
18313
18314                         if (hash.locale && _dataLocales[hash.locale]) {
18315                             // the locale can be manually set in the URL hash
18316                             _localeCode = hash.locale;
18317                         } else {
18318                             // otherwise use the locale specified by the browser
18319                             _localeCode = supportedBrowserLocale();
18320                         }
18321
18322                         return Promise.all([
18323                             // always load the English locale strings as fallbacks
18324                             localizer.loadLocale('en'),
18325                             // load the preferred locale
18326                             localizer.loadLocale(_localeCode)
18327                         ]);
18328                     })
18329                     .then(() => {
18330                         updateForCurrentLocale();
18331                     })
18332                     .catch(err => console.error(err));  // eslint-disable-line
18333             };
18334
18335             // Returns the best locale requested by the browser supported by iD, if any
18336             function supportedBrowserLocale() {
18337                 // list of locales preferred by the browser in priority order
18338                 let browserLocales = utilDetect().browserLocales;
18339                 let supportedLocales = _dataLocales;
18340
18341                 for (let i in browserLocales) {
18342                     let browserLocale = browserLocales[i];
18343                     if (browserLocale.includes('-')) { // full locale ('es-ES')
18344
18345                         if (supportedLocales[browserLocale]) return browserLocale;
18346
18347                         // If full locale not supported ('es-FAKE'), fallback to the base ('es')
18348                         let langPart = browserLocale.split('-')[0];
18349                         if (supportedLocales[langPart]) return langPart;
18350
18351                     } else { // base locale ('es')
18352
18353                         // prefer a lower-priority full locale with this base ('es' < 'es-ES')
18354                         let fullLocale = browserLocales.find((locale, index) => {
18355                             return index > i &&
18356                                 locale !== browserLocale &&
18357                                 locale.split('-')[0] === browserLocale &&
18358                                 supportedLocales[locale];
18359                         });
18360                         if (fullLocale) return fullLocale;
18361
18362                         if (supportedLocales[browserLocale]) return browserLocale;
18363                     }
18364                 }
18365
18366                 return null;
18367             }
18368
18369             function updateForCurrentLocale() {
18370                 if (!_localeCode) return;
18371
18372                 _languageCode = _localeCode.split('-')[0];
18373
18374                 const currentData = _dataLocales[_localeCode] || _dataLocales[_languageCode];
18375
18376                 const hash = utilStringQs(window.location.hash);
18377
18378                 if (hash.rtl === 'true') {
18379                     _textDirection = 'rtl';
18380                 } else if (hash.rtl === 'false') {
18381                     _textDirection = 'ltr';
18382                 }  else {
18383                     _textDirection = currentData && currentData.rtl ? 'rtl' : 'ltr';
18384                 }
18385
18386                 _languageNames = currentData && currentData.languageNames;
18387                 _scriptNames = currentData && currentData.scriptNames;
18388
18389                 _usesMetric = _localeCode.slice(-3).toLowerCase() !== '-us';
18390             }
18391
18392
18393             /* Locales */
18394             // Returns a Promise to load the strings for the requested locale
18395             localizer.loadLocale = (requested) => {
18396
18397                 if (!_dataLocales) {
18398                     return Promise.reject('loadLocale called before init');
18399                 }
18400
18401                 let locale = requested;
18402
18403                 // US English is the default
18404                 if (locale.toLowerCase() === 'en-us') locale = 'en';
18405
18406                 if (!_dataLocales[locale]) {
18407                     return Promise.reject(`Unsupported locale: ${requested}`);
18408                 }
18409
18410                 if (_localeStrings[locale]) {    // already loaded
18411                     return Promise.resolve(locale);
18412                 }
18413
18414                 let fileMap = _mainFileFetcher.fileMap();
18415                 const key = `locale_${locale}`;
18416                 fileMap[key] = `locales/${locale}.json`;
18417
18418                 return _mainFileFetcher.get(key)
18419                     .then(d => {
18420                         _localeStrings[locale] = d[locale];
18421                         return locale;
18422                     });
18423             };
18424
18425             /**
18426             * Given a string identifier, try to find that string in the current
18427             * language, and return it.  This function will be called recursively
18428             * with locale `en` if a string can not be found in the requested language.
18429             *
18430             * @param  {string}   s             string identifier
18431             * @param  {object?}  replacements  token replacements and default string
18432             * @param  {string?}  locale        locale to use (defaults to currentLocale)
18433             * @return {string?}  localized string
18434             */
18435             localizer.t = function(s, replacements, locale) {
18436                 locale = locale || _localeCode;
18437
18438                 // US English is the default
18439                 if (locale.toLowerCase() === 'en-us') locale = 'en';
18440
18441                 let path = s
18442                   .split('.')
18443                   .map(s => s.replace(/<TX_DOT>/g, '.'))
18444                   .reverse();
18445
18446                 let result = _localeStrings[locale];
18447
18448                 while (result !== undefined && path.length) {
18449                   result = result[path.pop()];
18450                 }
18451
18452                 if (result !== undefined) {
18453                   if (replacements) {
18454                     for (let k in replacements) {
18455                       const token = `{${k}}`;
18456                       const regex = new RegExp(token, 'g');
18457                       result = result.replace(regex, replacements[k]);
18458                     }
18459                   }
18460                   return result;
18461                 }
18462
18463                 if (locale !== 'en') {
18464                   return localizer.t(s, replacements, 'en');  // fallback - recurse with 'en'
18465                 }
18466
18467                 if (replacements && 'default' in replacements) {
18468                   return replacements.default;      // fallback - replacements.default
18469                 }
18470
18471                 const missing = `Missing ${locale} translation: ${s}`;
18472                 if (typeof console !== 'undefined') console.error(missing);  // eslint-disable-line
18473
18474                 return missing;
18475             };
18476
18477             localizer.languageName = (code, options) => {
18478
18479                 if (_languageNames[code]) {  // name in locale langauge
18480                   // e.g. "German"
18481                   return _languageNames[code];
18482                 }
18483
18484                 // sometimes we only want the local name
18485                 if (options && options.localOnly) return null;
18486
18487                 const langInfo = _dataLanguages[code];
18488                 if (langInfo) {
18489                   if (langInfo.nativeName) {  // name in native language
18490                     // e.g. "Deutsch (de)"
18491                     return localizer.t('translate.language_and_code', { language: langInfo.nativeName, code: code });
18492
18493                   } else if (langInfo.base && langInfo.script) {
18494                     const base = langInfo.base;   // the code of the langauge this is based on
18495
18496                     if (_languageNames[base]) {   // base language name in locale langauge
18497                       const scriptCode = langInfo.script;
18498                       const script = _scriptNames[scriptCode] || scriptCode;
18499                       // e.g. "Serbian (Cyrillic)"
18500                       return localizer.t('translate.language_and_code', { language: _languageNames[base], code: script });
18501
18502                     } else if (_dataLanguages[base] && _dataLanguages[base].nativeName) {
18503                       // e.g. "српски (sr-Cyrl)"
18504                       return localizer.t('translate.language_and_code', { language: _dataLanguages[base].nativeName, code: code });
18505                     }
18506                   }
18507                 }
18508                 return code;  // if not found, use the code
18509             };
18510
18511             return localizer;
18512         }
18513
18514         //
18515         // `presetCollection` is a wrapper around an `Array` of presets `collection`,
18516         // and decorated with some extra methods for searching and matching geometry
18517         //
18518         function presetCollection(collection) {
18519           const MAXRESULTS = 50;
18520           let _this = {};
18521           let _memo = {};
18522
18523           _this.collection = collection;
18524
18525           _this.item = (id) => {
18526             if (_memo[id]) return _memo[id];
18527             const found = _this.collection.find(d => d.id === id);
18528             if (found) _memo[id] = found;
18529             return found;
18530           };
18531
18532           _this.index = (id) => _this.collection.findIndex(d => d.id === id);
18533
18534           _this.matchGeometry = (geometry) => {
18535             return presetCollection(
18536               _this.collection.filter(d => d.matchGeometry(geometry))
18537             );
18538           };
18539
18540           _this.matchAllGeometry = (geometries) => {
18541             return presetCollection(
18542               _this.collection.filter(d => d && d.matchAllGeometry(geometries))
18543             );
18544           };
18545
18546           _this.matchAnyGeometry = (geometries) => {
18547             return presetCollection(
18548               _this.collection.filter(d => geometries.some(geom => d.matchGeometry(geom)))
18549             );
18550           };
18551
18552           _this.fallback = (geometry) => {
18553             let id = geometry;
18554             if (id === 'vertex') id = 'point';
18555             return _this.item(id);
18556           };
18557
18558           _this.search = (value, geometry, countryCode) => {
18559             if (!value) return _this;
18560
18561             value = value.toLowerCase().trim();
18562
18563             // match at name beginning or just after a space (e.g. "office" -> match "Law Office")
18564             function leading(a) {
18565               const index = a.indexOf(value);
18566               return index === 0 || a[index - 1] === ' ';
18567             }
18568
18569             // match at name beginning only
18570             function leadingStrict(a) {
18571               const index = a.indexOf(value);
18572               return index === 0;
18573             }
18574
18575             function sortNames(a, b) {
18576               let aCompare = (a.suggestion ? a.originalName : a.name()).toLowerCase();
18577               let bCompare = (b.suggestion ? b.originalName : b.name()).toLowerCase();
18578
18579               // priority if search string matches preset name exactly - #4325
18580               if (value === aCompare) return -1;
18581               if (value === bCompare) return 1;
18582
18583               // priority for higher matchScore
18584               let i = b.originalScore - a.originalScore;
18585               if (i !== 0) return i;
18586
18587               // priority if search string appears earlier in preset name
18588               i = aCompare.indexOf(value) - bCompare.indexOf(value);
18589               if (i !== 0) return i;
18590
18591               // priority for shorter preset names
18592               return aCompare.length - bCompare.length;
18593             }
18594
18595             let pool = _this.collection;
18596             if (countryCode) {
18597               pool = pool.filter(a => {
18598                 if (a.countryCodes && a.countryCodes.indexOf(countryCode) === -1) return false;
18599                 if (a.notCountryCodes && a.notCountryCodes.indexOf(countryCode) !== -1) return false;
18600                 return true;
18601               });
18602             }
18603             const searchable = pool.filter(a => a.searchable !== false && a.suggestion !== true);
18604             const suggestions = pool.filter(a => a.suggestion === true);
18605
18606             // matches value to preset.name
18607             const leading_name = searchable
18608               .filter(a => leading(a.name().toLowerCase()))
18609               .sort(sortNames);
18610
18611             // matches value to preset suggestion name (original name is unhyphenated)
18612             const leading_suggestions = suggestions
18613               .filter(a => leadingStrict(a.originalName.toLowerCase()))
18614               .sort(sortNames);
18615
18616             // matches value to preset.terms values
18617             const leading_terms = searchable
18618               .filter(a => (a.terms() || []).some(leading));
18619
18620             // matches value to preset.tags values
18621             const leading_tag_values = searchable
18622               .filter(a => Object.values(a.tags || {}).filter(val => val !== '*').some(leading));
18623
18624             // finds close matches to value in preset.name
18625             const similar_name = searchable
18626               .map(a => ({ preset: a, dist: utilEditDistance(value, a.name()) }))
18627               .filter(a => a.dist + Math.min(value.length - a.preset.name().length, 0) < 3)
18628               .sort((a, b) => a.dist - b.dist)
18629               .map(a => a.preset);
18630
18631             // finds close matches to value to preset suggestion name (original name is unhyphenated)
18632             const similar_suggestions = suggestions
18633               .map(a => ({ preset: a, dist: utilEditDistance(value, a.originalName.toLowerCase()) }))
18634               .filter(a => a.dist + Math.min(value.length - a.preset.originalName.length, 0) < 1)
18635               .sort((a, b) => a.dist - b.dist)
18636               .map(a => a.preset);
18637
18638             // finds close matches to value in preset.terms
18639             const similar_terms = searchable
18640               .filter(a => {
18641                 return (a.terms() || []).some(b => {
18642                   return utilEditDistance(value, b) + Math.min(value.length - b.length, 0) < 3;
18643                 });
18644               });
18645
18646             let results = leading_name.concat(
18647               leading_suggestions,
18648               leading_terms,
18649               leading_tag_values,
18650               similar_name,
18651               similar_suggestions,
18652               similar_terms
18653             ).slice(0, MAXRESULTS - 1);
18654
18655             if (geometry) {
18656               if (typeof geometry === 'string') {
18657                 results.push(_this.fallback(geometry));
18658               } else {
18659                 geometry.forEach(geom => results.push(_this.fallback(geom)));
18660               }
18661             }
18662
18663             return presetCollection(utilArrayUniq(results));
18664           };
18665
18666
18667           return _this;
18668         }
18669
18670         //
18671         // `presetCategory` builds a `presetCollection` of member presets,
18672         // decorated with some extra methods for searching and matching geometry
18673         //
18674         function presetCategory(categoryID, category, all) {
18675           let _this = Object.assign({}, category);   // shallow copy
18676
18677           _this.id = categoryID;
18678
18679           _this.members = presetCollection(
18680             category.members.map(presetID => all.item(presetID)).filter(Boolean)
18681           );
18682
18683           _this.geometry = _this.members.collection
18684             .reduce((acc, preset) => {
18685               for (let i in preset.geometry) {
18686                 const geometry = preset.geometry[i];
18687                 if (acc.indexOf(geometry) === -1) {
18688                   acc.push(geometry);
18689                 }
18690               }
18691               return acc;
18692             }, []);
18693
18694           _this.matchGeometry = (geom) => _this.geometry.indexOf(geom) >= 0;
18695
18696           _this.matchAllGeometry = (geometries) => _this.members.collection
18697             .some(preset => preset.matchAllGeometry(geometries));
18698
18699           _this.matchScore = () => -1;
18700
18701           _this.name = () => _t(`presets.categories.${categoryID}.name`, { 'default': categoryID });
18702
18703           _this.terms = () => [];
18704
18705
18706           return _this;
18707         }
18708
18709         //
18710         // `presetField` decorates a given `field` Object
18711         // with some extra methods for searching and matching geometry
18712         //
18713         function presetField(fieldID, field) {
18714           let _this = Object.assign({}, field);   // shallow copy
18715
18716           _this.id = fieldID;
18717
18718           // for use in classes, element ids, css selectors
18719           _this.safeid = utilSafeClassName(fieldID);
18720
18721           _this.matchGeometry = (geom) => !_this.geometry || _this.geometry.indexOf(geom) !== -1;
18722
18723           _this.matchAllGeometry = (geometries) => {
18724             return !_this.geometry || geometries.every(geom => _this.geometry.indexOf(geom) !== -1);
18725           };
18726
18727           _this.t = (scope, options) => _t(`presets.fields.${fieldID}.${scope}`, options);
18728
18729           _this.label = () => _this.overrideLabel || _this.t('label', { 'default': fieldID });
18730
18731           const _placeholder = _this.placeholder;
18732           _this.placeholder = () => _this.t('placeholder', { 'default': _placeholder });
18733
18734           _this.originalTerms = (_this.terms || []).join();
18735
18736           _this.terms = () => _this.t('terms', { 'default': _this.originalTerms })
18737             .toLowerCase().trim().split(/\s*,+\s*/);
18738
18739
18740           return _this;
18741         }
18742
18743         //
18744         // `presetPreset` decorates a given `preset` Object
18745         // with some extra methods for searching and matching geometry
18746         //
18747         function presetPreset(presetID, preset, addable, allFields, allPresets) {
18748           allFields = allFields || {};
18749           allPresets = allPresets || {};
18750           let _this = Object.assign({}, preset);   // shallow copy
18751           let _addable = addable || false;
18752           let _resolvedFields;      // cache
18753           let _resolvedMoreFields;  // cache
18754
18755           _this.id = presetID;
18756
18757           _this.safeid = utilSafeClassName(presetID);  // for use in css classes, selectors, element ids
18758
18759           _this.originalTerms = (_this.terms || []).join();
18760
18761           _this.originalName = _this.name || '';
18762
18763           _this.originalScore = _this.matchScore || 1;
18764
18765           _this.originalReference = _this.reference || {};
18766
18767           _this.originalFields = (_this.fields || []);
18768
18769           _this.originalMoreFields = (_this.moreFields || []);
18770
18771           _this.fields = () => _resolvedFields || (_resolvedFields = resolve('fields'));
18772
18773           _this.moreFields = () => _resolvedMoreFields || (_resolvedMoreFields = resolve('moreFields'));
18774
18775           _this.resetFields = () => _resolvedFields = _resolvedMoreFields = null;
18776
18777           _this.tags = _this.tags || {};
18778
18779           _this.addTags = _this.addTags || _this.tags;
18780
18781           _this.removeTags = _this.removeTags || _this.addTags;
18782
18783           _this.geometry = (_this.geometry || []);
18784
18785           _this.matchGeometry = (geom) => _this.geometry.indexOf(geom) >= 0;
18786
18787           _this.matchAllGeometry = (geoms) => geoms.every(_this.matchGeometry);
18788
18789           _this.matchScore = (entityTags) => {
18790             const tags = _this.tags;
18791             let seen = {};
18792             let score = 0;
18793
18794             // match on tags
18795             for (let k in tags) {
18796               seen[k] = true;
18797               if (entityTags[k] === tags[k]) {
18798                 score += _this.originalScore;
18799               } else if (tags[k] === '*' && k in entityTags) {
18800                 score += _this.originalScore / 2;
18801               } else {
18802                 return -1;
18803               }
18804             }
18805
18806             // boost score for additional matches in addTags - #6802
18807             const addTags = _this.addTags;
18808             for (let k in addTags) {
18809               if (!seen[k] && entityTags[k] === addTags[k]) {
18810                 score += _this.originalScore;
18811               }
18812             }
18813
18814             return score;
18815           };
18816
18817
18818           let _textCache = {};
18819           _this.t = (scope, options) => {
18820             const textID = `presets.presets.${presetID}.${scope}`;
18821             if (_textCache[textID]) return _textCache[textID];
18822             return _textCache[textID] = _t(textID, options);
18823           };
18824
18825
18826           _this.name = () => {
18827             if (_this.suggestion) {
18828               let path = presetID.split('/');
18829               path.pop();  // remove brand name
18830               // NOTE: insert an en-dash, not a hypen (to avoid conflict with fr - nl names in Brussels etc)
18831               return _this.originalName + ' – ' + _t('presets.presets.' + path.join('/') + '.name');
18832             }
18833             return _this.t('name', { 'default': _this.originalName });
18834           };
18835
18836
18837           _this.terms = () => _this.t('terms', { 'default': _this.originalTerms })
18838             .toLowerCase().trim().split(/\s*,+\s*/);
18839
18840
18841           _this.isFallback = () => {
18842             const tagCount = Object.keys(_this.tags).length;
18843             return tagCount === 0 || (tagCount === 1 && _this.tags.hasOwnProperty('area'));
18844           };
18845
18846
18847           _this.addable = function(val) {
18848             if (!arguments.length) return _addable;
18849             _addable = val;
18850             return _this;
18851           };
18852
18853
18854           _this.reference = (geom) => {
18855             // Lookup documentation on Wikidata...
18856             const qid = _this.tags.wikidata || _this.tags['brand:wikidata'] || _this.tags['operator:wikidata'];
18857             if (qid) {
18858               return { qid: qid };
18859             }
18860
18861             // Lookup documentation on OSM Wikibase...
18862             let key = _this.originalReference.key || Object.keys(utilObjectOmit(_this.tags, 'name'))[0];
18863             let value = _this.originalReference.value || _this.tags[key];
18864
18865             if (geom === 'relation' && key === 'type') {
18866               if (value in _this.tags) {
18867                 key = value;
18868                 value = _this.tags[key];
18869               } else {
18870                 return { rtype: value };
18871               }
18872             }
18873
18874             if (value === '*') {
18875               return { key: key };
18876             } else {
18877               return { key: key, value: value };
18878             }
18879           };
18880
18881
18882           _this.unsetTags = (tags, geometry, skipFieldDefaults) => {
18883             tags = utilObjectOmit(tags, Object.keys(_this.removeTags));
18884
18885             if (geometry && !skipFieldDefaults) {
18886               _this.fields().forEach(field => {
18887                 if (field.matchGeometry(geometry) && field.key && field.default === tags[field.key]) {
18888                   delete tags[field.key];
18889                 }
18890               });
18891             }
18892
18893             delete tags.area;
18894             return tags;
18895           };
18896
18897
18898           _this.setTags = (tags, geometry, skipFieldDefaults) => {
18899             const addTags = _this.addTags;
18900             tags = Object.assign({}, tags);   // shallow copy
18901
18902             for (let k in addTags) {
18903               if (addTags[k] === '*') {
18904                 tags[k] = 'yes';
18905               } else {
18906                 tags[k] = addTags[k];
18907               }
18908             }
18909
18910             // Add area=yes if necessary.
18911             // This is necessary if the geometry is already an area (e.g. user drew an area) AND any of:
18912             // 1. chosen preset could be either an area or a line (`barrier=city_wall`)
18913             // 2. chosen preset doesn't have a key in osmAreaKeys (`railway=station`)
18914             if (!addTags.hasOwnProperty('area')) {
18915               delete tags.area;
18916               if (geometry === 'area') {
18917                 let needsAreaTag = true;
18918                 if (_this.geometry.indexOf('line') === -1) {
18919                   for (let k in addTags) {
18920                     if (k in osmAreaKeys) {
18921                       needsAreaTag = false;
18922                       break;
18923                     }
18924                   }
18925                 }
18926                 if (needsAreaTag) {
18927                   tags.area = 'yes';
18928                 }
18929               }
18930             }
18931
18932             if (geometry && !skipFieldDefaults) {
18933               _this.fields().forEach(field => {
18934                 if (field.matchGeometry(geometry) && field.key && !tags[field.key] && field.default) {
18935                   tags[field.key] = field.default;
18936                 }
18937               });
18938             }
18939
18940             return tags;
18941           };
18942
18943
18944           // For a preset without fields, use the fields of the parent preset.
18945           // Replace {preset} placeholders with the fields of the specified presets.
18946           function resolve(which) {
18947             const fieldIDs = (which === 'fields' ? _this.originalFields : _this.originalMoreFields);
18948             let resolved = [];
18949
18950             fieldIDs.forEach(fieldID => {
18951               const match = fieldID.match(/\{(.*)\}/);
18952               if (match !== null) {    // a presetID wrapped in braces {}
18953                 resolved = resolved.concat(inheritFields(match[1], which));
18954               } else if (allFields[fieldID]) {    // a normal fieldID
18955                 resolved.push(allFields[fieldID]);
18956               } else {
18957                 console.log(`Cannot resolve "${fieldID}" found in ${_this.id}.${which}`);  // eslint-disable-line no-console
18958               }
18959             });
18960
18961             // no fields resolved, so use the parent's if possible
18962             if (!resolved.length) {
18963               const endIndex = _this.id.lastIndexOf('/');
18964               const parentID = endIndex && _this.id.substring(0, endIndex);
18965               if (parentID) {
18966                 resolved = inheritFields(parentID, which);
18967               }
18968             }
18969
18970             return utilArrayUniq(resolved);
18971
18972
18973             // returns an array of fields to inherit from the given presetID, if found
18974             function inheritFields(presetID, which) {
18975               const parent = allPresets[presetID];
18976               if (!parent) return [];
18977
18978               if (which === 'fields') {
18979                 return parent.fields().filter(shouldInherit);
18980               } else if (which === 'moreFields') {
18981                 return parent.moreFields();
18982               } else {
18983                 return [];
18984               }
18985             }
18986
18987
18988             // Skip `fields` for the keys which define the preset.
18989             // These are usually `typeCombo` fields like `shop=*`
18990             function shouldInherit(f) {
18991               if (f.key && _this.tags[f.key] !== undefined &&
18992                 // inherit anyway if multiple values are allowed or just a checkbox
18993                 f.type !== 'multiCombo' && f.type !== 'semiCombo' && f.type !== 'check'
18994               ) return false;
18995
18996               return true;
18997             }
18998           }
18999
19000
19001           return _this;
19002         }
19003
19004         let _mainPresetIndex = presetIndex(); // singleton
19005
19006         //
19007         // `presetIndex` wraps a `presetCollection`
19008         // with methods for loading new data and returning defaults
19009         //
19010         function presetIndex() {
19011           const dispatch$1 = dispatch('favoritePreset', 'recentsChange');
19012           const MAXRECENTS = 30;
19013
19014           // seed the preset lists with geometry fallbacks
19015           const POINT = presetPreset('point', { name: 'Point', tags: {}, geometry: ['point', 'vertex'], matchScore: 0.1 } );
19016           const LINE = presetPreset('line', { name: 'Line', tags: {}, geometry: ['line'], matchScore: 0.1 } );
19017           const AREA = presetPreset('area', { name: 'Area', tags: { area: 'yes' }, geometry: ['area'], matchScore: 0.1 } );
19018           const RELATION = presetPreset('relation', { name: 'Relation', tags: {}, geometry: ['relation'], matchScore: 0.1 } );
19019
19020           let _this = presetCollection([POINT, LINE, AREA, RELATION]);
19021           let _presets = { point: POINT, line: LINE, area: AREA, relation: RELATION };
19022
19023           let _defaults = {
19024             point: presetCollection([POINT]),
19025             vertex: presetCollection([POINT]),
19026             line: presetCollection([LINE]),
19027             area: presetCollection([AREA]),
19028             relation: presetCollection([RELATION])
19029           };
19030
19031           let _fields = {};
19032           let _categories = {};
19033           let _universal = [];
19034           let _addablePresetIDs = null;   // Set of preset IDs that the user can add
19035           let _recents;
19036           let _favorites;
19037
19038           // Index of presets by (geometry, tag key).
19039           let _geometryIndex = { point: {}, vertex: {}, line: {}, area: {}, relation: {} };
19040
19041           let _loadPromise;
19042
19043           _this.ensureLoaded = () => {
19044             if (_loadPromise) return _loadPromise;
19045
19046             return _loadPromise = Promise.all([
19047                 _mainFileFetcher.get('preset_categories'),
19048                 _mainFileFetcher.get('preset_defaults'),
19049                 _mainFileFetcher.get('preset_presets'),
19050                 _mainFileFetcher.get('preset_fields')
19051               ])
19052               .then(vals => {
19053                 _this.merge({
19054                   categories: vals[0],
19055                   defaults: vals[1],
19056                   presets: vals[2],
19057                   fields: vals[3]
19058                 });
19059                 osmSetAreaKeys(_this.areaKeys());
19060                 osmSetPointTags(_this.pointTags());
19061                 osmSetVertexTags(_this.vertexTags());
19062               });
19063           };
19064
19065
19066           _this.merge = (d) => {
19067             // Merge Fields
19068             if (d.fields) {
19069               Object.keys(d.fields).forEach(fieldID => {
19070                 const f = d.fields[fieldID];
19071                 if (f) {   // add or replace
19072                   _fields[fieldID] = presetField(fieldID, f);
19073                 } else {   // remove
19074                   delete _fields[fieldID];
19075                 }
19076               });
19077             }
19078
19079             // Merge Presets
19080             if (d.presets) {
19081               Object.keys(d.presets).forEach(presetID => {
19082                 const p = d.presets[presetID];
19083                 if (p) {   // add or replace
19084                   const isAddable = !_addablePresetIDs || _addablePresetIDs.has(presetID);
19085                   _presets[presetID] = presetPreset(presetID, p, isAddable, _fields, _presets);
19086                 } else {   // remove (but not if it's a fallback)
19087                   const existing = _presets[presetID];
19088                   if (existing && !existing.isFallback()) {
19089                     delete _presets[presetID];
19090                   }
19091                 }
19092               });
19093             }
19094
19095             // Need to rebuild _this.collection before loading categories
19096             _this.collection = Object.values(_presets).concat(Object.values(_categories));
19097
19098             // Merge Categories
19099             if (d.categories) {
19100               Object.keys(d.categories).forEach(categoryID => {
19101                 const c = d.categories[categoryID];
19102                 if (c) {   // add or replace
19103                   _categories[categoryID] = presetCategory(categoryID, c, _this);
19104                 } else {   // remove
19105                   delete _categories[categoryID];
19106                 }
19107               });
19108             }
19109
19110             // Rebuild _this.collection after loading categories
19111             _this.collection = Object.values(_presets).concat(Object.values(_categories));
19112
19113             // Merge Defaults
19114             if (d.defaults) {
19115               Object.keys(d.defaults).forEach(geometry => {
19116                 const def = d.defaults[geometry];
19117                 if (Array.isArray(def)) {   // add or replace
19118                   _defaults[geometry] = presetCollection(
19119                     def.map(id => _presets[id] || _categories[id]).filter(Boolean)
19120                   );
19121                 } else {   // remove
19122                   delete _defaults[geometry];
19123                 }
19124               });
19125             }
19126
19127             // Rebuild universal fields array
19128             _universal = Object.values(_fields).filter(field => field.universal);
19129
19130             // Reset all the preset fields - they'll need to be resolved again
19131             Object.values(_presets).forEach(preset => preset.resetFields());
19132
19133             // Rebuild geometry index
19134             _geometryIndex = { point: {}, vertex: {}, line: {}, area: {}, relation: {} };
19135             _this.collection.forEach(preset => {
19136               (preset.geometry || []).forEach(geometry => {
19137                 let g = _geometryIndex[geometry];
19138                 for (let key in preset.tags) {
19139                   (g[key] = g[key] || []).push(preset);
19140                 }
19141               });
19142             });
19143
19144             return _this;
19145           };
19146
19147
19148           _this.match = (entity, resolver) => {
19149             return resolver.transient(entity, 'presetMatch', () => {
19150               let geometry = entity.geometry(resolver);
19151               // Treat entities on addr:interpolation lines as points, not vertices - #3241
19152               if (geometry === 'vertex' && entity.isOnAddressLine(resolver)) {
19153                 geometry = 'point';
19154               }
19155               return _this.matchTags(entity.tags, geometry);
19156             });
19157           };
19158
19159
19160           _this.matchTags = (tags, geometry) => {
19161             const geometryMatches = _geometryIndex[geometry];
19162             let address;
19163             let best = -1;
19164             let match;
19165
19166             for (let k in tags) {
19167               // If any part of an address is present, allow fallback to "Address" preset - #4353
19168               if (/^addr:/.test(k) && geometryMatches['addr:*']) {
19169                 address = geometryMatches['addr:*'][0];
19170               }
19171
19172               const keyMatches = geometryMatches[k];
19173               if (!keyMatches) continue;
19174
19175               for (let i = 0; i < keyMatches.length; i++) {
19176                 const score = keyMatches[i].matchScore(tags);
19177                 if (score > best) {
19178                   best = score;
19179                   match = keyMatches[i];
19180                 }
19181               }
19182             }
19183
19184             if (address && (!match || match.isFallback())) {
19185               match = address;
19186             }
19187             return match || _this.fallback(geometry);
19188           };
19189
19190
19191           _this.allowsVertex = (entity, resolver) => {
19192             if (entity.type !== 'node') return false;
19193             if (Object.keys(entity.tags).length === 0) return true;
19194
19195             return resolver.transient(entity, 'vertexMatch', () => {
19196               // address lines allow vertices to act as standalone points
19197               if (entity.isOnAddressLine(resolver)) return true;
19198
19199               const geometries = osmNodeGeometriesForTags(entity.tags);
19200               if (geometries.vertex) return true;
19201               if (geometries.point) return false;
19202               // allow vertices for unspecified points
19203               return true;
19204             });
19205           };
19206
19207
19208           // Because of the open nature of tagging, iD will never have a complete
19209           // list of tags used in OSM, so we want it to have logic like "assume
19210           // that a closed way with an amenity tag is an area, unless the amenity
19211           // is one of these specific types". This function computes a structure
19212           // that allows testing of such conditions, based on the presets designated
19213           // as as supporting (or not supporting) the area geometry.
19214           //
19215           // The returned object L is a keeplist/discardlist of tags. A closed way
19216           // with a tag (k, v) is considered to be an area if `k in L && !(v in L[k])`
19217           // (see `Way#isArea()`). In other words, the keys of L form the keeplist,
19218           // and the subkeys form the discardlist.
19219           _this.areaKeys = () => {
19220             // The ignore list is for keys that imply lines. (We always add `area=yes` for exceptions)
19221             const ignore = ['barrier', 'highway', 'footway', 'railway', 'junction', 'type'];
19222             let areaKeys = {};
19223
19224             // ignore name-suggestion-index and deprecated presets
19225             const presets = _this.collection.filter(p => !p.suggestion && !p.replacement);
19226
19227             // keeplist
19228             presets.forEach(p => {
19229               let key;
19230               for (key in p.tags) break;  // pick the first tag
19231               if (!key) return;
19232               if (ignore.indexOf(key) !== -1) return;
19233
19234               if (p.geometry.indexOf('area') !== -1) {    // probably an area..
19235                 areaKeys[key] = areaKeys[key] || {};
19236               }
19237             });
19238
19239             // discardlist
19240             presets.forEach(p => {
19241               let key;
19242               for (key in p.addTags) {
19243                 // examine all addTags to get a better sense of what can be tagged on lines - #6800
19244                 const value = p.addTags[key];
19245                 if (key in areaKeys &&                    // probably an area...
19246                   p.geometry.indexOf('line') !== -1 &&    // but sometimes a line
19247                   value !== '*') {
19248                   areaKeys[key][value] = true;
19249                 }
19250               }
19251             });
19252
19253             return areaKeys;
19254           };
19255
19256
19257           _this.pointTags = () => {
19258             return _this.collection.reduce((pointTags, d) => {
19259               // ignore name-suggestion-index, deprecated, and generic presets
19260               if (d.suggestion || d.replacement || d.searchable === false) return pointTags;
19261
19262               // only care about the primary tag
19263               let key;
19264               for (key in d.tags) break;  // pick the first tag
19265               if (!key) return pointTags;
19266
19267               // if this can be a point
19268               if (d.geometry.indexOf('point') !== -1) {
19269                 pointTags[key] = pointTags[key] || {};
19270                 pointTags[key][d.tags[key]] = true;
19271               }
19272               return pointTags;
19273             }, {});
19274           };
19275
19276
19277           _this.vertexTags = () => {
19278             return _this.collection.reduce((vertexTags, d) => {
19279               // ignore name-suggestion-index, deprecated, and generic presets
19280               if (d.suggestion || d.replacement || d.searchable === false) return vertexTags;
19281
19282               // only care about the primary tag
19283               let key;
19284               for (key in d.tags) break;   // pick the first tag
19285               if (!key) return vertexTags;
19286
19287               // if this can be a vertex
19288               if (d.geometry.indexOf('vertex') !== -1) {
19289                 vertexTags[key] = vertexTags[key] || {};
19290                 vertexTags[key][d.tags[key]] = true;
19291               }
19292               return vertexTags;
19293             }, {});
19294           };
19295
19296
19297           _this.field = (id) => _fields[id];
19298
19299           _this.universal = () => _universal;
19300
19301
19302           _this.defaults = (geometry, n, startWithRecents) => {
19303             let recents = [];
19304             if (startWithRecents) {
19305               recents = _this.recent().matchGeometry(geometry).collection.slice(0, 4);
19306             }
19307             let defaults;
19308             if (_addablePresetIDs) {
19309               defaults = Array.from(_addablePresetIDs).map(function(id) {
19310                 var preset = _this.item(id);
19311                 if (preset && preset.matchGeometry(geometry)) return preset;
19312                 return null;
19313               }).filter(Boolean);
19314             } else {
19315               defaults = _defaults[geometry].collection.concat(_this.fallback(geometry));
19316             }
19317
19318             return presetCollection(
19319               utilArrayUniq(recents.concat(defaults)).slice(0, n - 1)
19320             );
19321           };
19322
19323           // pass a Set of addable preset ids
19324           _this.addablePresetIDs = function(val) {
19325             if (!arguments.length) return _addablePresetIDs;
19326
19327             // accept and convert arrays
19328             if (Array.isArray(val)) val = new Set(val);
19329
19330             _addablePresetIDs = val;
19331             if (_addablePresetIDs) {   // reset all presets
19332               _this.collection.forEach(p => {
19333                 // categories aren't addable
19334                 if (p.addable) p.addable(_addablePresetIDs.has(p.id));
19335               });
19336             } else {
19337               _this.collection.forEach(p => {
19338                 if (p.addable) p.addable(true);
19339               });
19340             }
19341
19342             return _this;
19343           };
19344
19345
19346           _this.recent = () => {
19347             return presetCollection(
19348               utilArrayUniq(_this.getRecents().map(d => d.preset))
19349             );
19350           };
19351
19352
19353           function RibbonItem(preset, source) {
19354             let item = {};
19355             item.preset = preset;
19356             item.source = source;
19357
19358             item.isFavorite = () => item.source === 'favorite';
19359             item.isRecent = () => item.source === 'recent';
19360             item.matches = (preset) => item.preset.id === preset.id;
19361             item.minified = () => ({ pID: item.preset.id });
19362
19363             return item;
19364           }
19365
19366
19367           function ribbonItemForMinified(d, source) {
19368             if (d && d.pID) {
19369               const preset = _this.item(d.pID);
19370               if (!preset) return null;
19371               return RibbonItem(preset, source);
19372             }
19373             return null;
19374           }
19375
19376
19377           _this.getGenericRibbonItems = () => {
19378             return ['point', 'line', 'area'].map(id => RibbonItem(_this.item(id), 'generic'));
19379           };
19380
19381
19382           _this.getAddable = () => {
19383               if (!_addablePresetIDs) return [];
19384
19385               return _addablePresetIDs.map((id) => {
19386                 const preset = _this.item(id);
19387                 if (preset) {
19388                   return RibbonItem(preset, 'addable');
19389                 }
19390               }).filter(Boolean);
19391           };
19392
19393
19394           function setRecents(items) {
19395             _recents = items;
19396             const minifiedItems = items.map(d => d.minified());
19397             corePreferences('preset_recents', JSON.stringify(minifiedItems));
19398             dispatch$1.call('recentsChange');
19399           }
19400
19401
19402           _this.getRecents = () => {
19403             if (!_recents) {
19404               // fetch from local storage
19405               _recents = (JSON.parse(corePreferences('preset_recents')) || [])
19406                 .reduce((acc, d) => {
19407                   let item = ribbonItemForMinified(d, 'recent');
19408                   if (item && item.preset.addable()) acc.push(item);
19409                   return acc;
19410                 }, []);
19411             }
19412             return _recents;
19413           };
19414
19415
19416           _this.addRecent = (preset, besidePreset, after) => {
19417             const recents = _this.getRecents();
19418
19419             const beforeItem = _this.recentMatching(besidePreset);
19420             let toIndex = recents.indexOf(beforeItem);
19421             if (after) toIndex += 1;
19422
19423             const newItem = RibbonItem(preset, 'recent');
19424             recents.splice(toIndex, 0, newItem);
19425             setRecents(recents);
19426           };
19427
19428
19429           _this.removeRecent = (preset) => {
19430             const item = _this.recentMatching(preset);
19431             if (item) {
19432               let items = _this.getRecents();
19433               items.splice(items.indexOf(item), 1);
19434               setRecents(items);
19435             }
19436           };
19437
19438
19439           _this.recentMatching = (preset) => {
19440             const items = _this.getRecents();
19441             for (let i in items) {
19442               if (items[i].matches(preset)) {
19443                 return items[i];
19444               }
19445             }
19446             return null;
19447           };
19448
19449
19450           _this.moveItem = (items, fromIndex, toIndex) => {
19451             if (fromIndex === toIndex ||
19452               fromIndex < 0 || toIndex < 0 ||
19453               fromIndex >= items.length || toIndex >= items.length
19454             ) return null;
19455
19456             items.splice(toIndex, 0, items.splice(fromIndex, 1)[0]);
19457             return items;
19458           };
19459
19460
19461           _this.moveRecent = (item, beforeItem) => {
19462             const recents = _this.getRecents();
19463             const fromIndex = recents.indexOf(item);
19464             const toIndex = recents.indexOf(beforeItem);
19465             const items = _this.moveItem(recents, fromIndex, toIndex);
19466             if (items) setRecents(items);
19467           };
19468
19469
19470           _this.setMostRecent = (preset) => {
19471             if (preset.searchable === false) return;
19472
19473             let items = _this.getRecents();
19474             let item = _this.recentMatching(preset);
19475             if (item) {
19476               items.splice(items.indexOf(item), 1);
19477             } else {
19478               item = RibbonItem(preset, 'recent');
19479             }
19480
19481             // remove the last recent (first in, first out)
19482             while (items.length >= MAXRECENTS) {
19483               items.pop();
19484             }
19485
19486             // prepend array
19487             items.unshift(item);
19488             setRecents(items);
19489           };
19490
19491           function setFavorites(items) {
19492             _favorites = items;
19493             const minifiedItems = items.map(d => d.minified());
19494             corePreferences('preset_favorites', JSON.stringify(minifiedItems));
19495
19496             // call update
19497             dispatch$1.call('favoritePreset');
19498           }
19499
19500           _this.addFavorite = (preset, besidePreset, after) => {
19501               const favorites = _this.getFavorites();
19502
19503               const beforeItem = _this.favoriteMatching(besidePreset);
19504               let toIndex = favorites.indexOf(beforeItem);
19505               if (after) toIndex += 1;
19506
19507               const newItem = RibbonItem(preset, 'favorite');
19508               favorites.splice(toIndex, 0, newItem);
19509               setFavorites(favorites);
19510           };
19511
19512           _this.toggleFavorite = (preset) => {
19513             const favs = _this.getFavorites();
19514             const favorite = _this.favoriteMatching(preset);
19515             if (favorite) {
19516               favs.splice(favs.indexOf(favorite), 1);
19517             } else {
19518               // only allow 10 favorites
19519               if (favs.length === 10) {
19520                   // remove the last favorite (last in, first out)
19521                   favs.pop();
19522               }
19523               // append array
19524               favs.push(RibbonItem(preset, 'favorite'));
19525             }
19526             setFavorites(favs);
19527           };
19528
19529
19530           _this.removeFavorite = (preset) => {
19531             const item = _this.favoriteMatching(preset);
19532             if (item) {
19533               const items = _this.getFavorites();
19534               items.splice(items.indexOf(item), 1);
19535               setFavorites(items);
19536             }
19537           };
19538
19539
19540           _this.getFavorites = () => {
19541             if (!_favorites) {
19542
19543               // fetch from local storage
19544               let rawFavorites = JSON.parse(corePreferences('preset_favorites'));
19545
19546               if (!rawFavorites) {
19547                 rawFavorites = [];
19548                 corePreferences('preset_favorites', JSON.stringify(rawFavorites));
19549               }
19550
19551               _favorites = rawFavorites.reduce((output, d) => {
19552                 const item = ribbonItemForMinified(d, 'favorite');
19553                 if (item && item.preset.addable()) output.push(item);
19554                 return output;
19555               }, []);
19556             }
19557             return _favorites;
19558           };
19559
19560
19561           _this.favoriteMatching = (preset) => {
19562             const favs = _this.getFavorites();
19563             for (let index in favs) {
19564               if (favs[index].matches(preset)) {
19565                 return favs[index];
19566               }
19567             }
19568             return null;
19569           };
19570
19571
19572           return utilRebind(_this, dispatch$1, 'on');
19573         }
19574
19575         function utilTagText(entity) {
19576             var obj = (entity && entity.tags) || {};
19577             return Object.keys(obj)
19578                 .map(function(k) { return k + '=' + obj[k]; })
19579                 .join(', ');
19580         }
19581
19582
19583         function utilTotalExtent(array, graph) {
19584             var extent = geoExtent();
19585             var val, entity;
19586             for (var i = 0; i < array.length; i++) {
19587                 val = array[i];
19588                 entity = typeof val === 'string' ? graph.hasEntity(val) : val;
19589                 if (entity) {
19590                     extent._extend(entity.extent(graph));
19591                 }
19592             }
19593             return extent;
19594         }
19595
19596
19597         function utilTagDiff(oldTags, newTags) {
19598             var tagDiff = [];
19599             var keys = utilArrayUnion(Object.keys(oldTags), Object.keys(newTags)).sort();
19600             keys.forEach(function(k) {
19601                 var oldVal = oldTags[k];
19602                 var newVal = newTags[k];
19603
19604                 if ((oldVal || oldVal === '') && (newVal === undefined || newVal !== oldVal)) {
19605                     tagDiff.push({
19606                         type: '-',
19607                         key: k,
19608                         oldVal: oldVal,
19609                         newVal: newVal,
19610                         display: '- ' + k + '=' + oldVal
19611                     });
19612                 }
19613                 if ((newVal || newVal === '') && (oldVal === undefined || newVal !== oldVal)) {
19614                     tagDiff.push({
19615                         type: '+',
19616                         key: k,
19617                         oldVal: oldVal,
19618                         newVal: newVal,
19619                         display: '+ ' + k + '=' + newVal
19620                     });
19621                 }
19622             });
19623             return tagDiff;
19624         }
19625
19626
19627         function utilEntitySelector(ids) {
19628             return ids.length ? '.' + ids.join(',.') : 'nothing';
19629         }
19630
19631
19632         // returns an selector to select entity ids for:
19633         //  - entityIDs passed in
19634         //  - shallow descendant entityIDs for any of those entities that are relations
19635         function utilEntityOrMemberSelector(ids, graph) {
19636             var seen = new Set(ids);
19637             ids.forEach(collectShallowDescendants);
19638             return utilEntitySelector(Array.from(seen));
19639
19640             function collectShallowDescendants(id) {
19641                 var entity = graph.hasEntity(id);
19642                 if (!entity || entity.type !== 'relation') return;
19643
19644                 entity.members
19645                     .map(function(member) { return member.id; })
19646                     .forEach(function(id) { seen.add(id); });
19647             }
19648         }
19649
19650
19651         // returns an selector to select entity ids for:
19652         //  - entityIDs passed in
19653         //  - deep descendant entityIDs for any of those entities that are relations
19654         function utilEntityOrDeepMemberSelector(ids, graph) {
19655             return utilEntitySelector(utilEntityAndDeepMemberIDs(ids, graph));
19656         }
19657
19658
19659         // returns an selector to select entity ids for:
19660         //  - entityIDs passed in
19661         //  - deep descendant entityIDs for any of those entities that are relations
19662         function utilEntityAndDeepMemberIDs(ids, graph) {
19663             var seen = new Set();
19664             ids.forEach(collectDeepDescendants);
19665             return Array.from(seen);
19666
19667             function collectDeepDescendants(id) {
19668                 if (seen.has(id)) return;
19669                 seen.add(id);
19670
19671                 var entity = graph.hasEntity(id);
19672                 if (!entity || entity.type !== 'relation') return;
19673
19674                 entity.members
19675                     .map(function(member) { return member.id; })
19676                     .forEach(collectDeepDescendants);   // recurse
19677             }
19678         }
19679
19680         // returns an selector to select entity ids for:
19681         //  - deep descendant entityIDs for any of those entities that are relations
19682         function utilDeepMemberSelector(ids, graph, skipMultipolgonMembers) {
19683             var idsSet = new Set(ids);
19684             var seen = new Set();
19685             var returners = new Set();
19686             ids.forEach(collectDeepDescendants);
19687             return utilEntitySelector(Array.from(returners));
19688
19689             function collectDeepDescendants(id) {
19690                 if (seen.has(id)) return;
19691                 seen.add(id);
19692
19693                 if (!idsSet.has(id)) {
19694                     returners.add(id);
19695                 }
19696
19697                 var entity = graph.hasEntity(id);
19698                 if (!entity || entity.type !== 'relation') return;
19699                 if (skipMultipolgonMembers && entity.isMultipolygon()) return;
19700                 entity.members
19701                     .map(function(member) { return member.id; })
19702                     .forEach(collectDeepDescendants);   // recurse
19703             }
19704         }
19705
19706
19707         // Adds or removes highlight styling for the specified entities
19708         function utilHighlightEntities(ids, highlighted, context) {
19709             context.surface()
19710                 .selectAll(utilEntityOrDeepMemberSelector(ids, context.graph()))
19711                 .classed('highlighted', highlighted);
19712         }
19713
19714
19715         // returns an Array that is the union of:
19716         //  - nodes for any nodeIDs passed in
19717         //  - child nodes of any wayIDs passed in
19718         //  - descendant member and child nodes of relationIDs passed in
19719         function utilGetAllNodes(ids, graph) {
19720             var seen = new Set();
19721             var nodes = new Set();
19722
19723             ids.forEach(collectNodes);
19724             return Array.from(nodes);
19725
19726             function collectNodes(id) {
19727                 if (seen.has(id)) return;
19728                 seen.add(id);
19729
19730                 var entity = graph.hasEntity(id);
19731                 if (!entity) return;
19732
19733                 if (entity.type === 'node') {
19734                     nodes.add(entity);
19735                 } else if (entity.type === 'way') {
19736                     entity.nodes.forEach(collectNodes);
19737                 } else {
19738                     entity.members
19739                         .map(function(member) { return member.id; })
19740                         .forEach(collectNodes);   // recurse
19741                 }
19742             }
19743         }
19744
19745
19746         function utilDisplayName(entity) {
19747             var localizedNameKey = 'name:' + _mainLocalizer.languageCode().toLowerCase();
19748             var name = entity.tags[localizedNameKey] || entity.tags.name || '';
19749             var network = entity.tags.cycle_network || entity.tags.network;
19750
19751             if (!name && entity.tags.ref) {
19752                 name = entity.tags.ref;
19753                 if (network) {
19754                     name = network + ' ' + name;
19755                 }
19756             }
19757
19758             return name;
19759         }
19760
19761
19762         function utilDisplayNameForPath(entity) {
19763             var name = utilDisplayName(entity);
19764             var isFirefox = utilDetect().browser.toLowerCase().indexOf('firefox') > -1;
19765
19766             if (!isFirefox && name && rtlRegex.test(name)) {
19767                 name = fixRTLTextForSvg(name);
19768             }
19769
19770             return name;
19771         }
19772
19773
19774         function utilDisplayType(id) {
19775             return {
19776                 n: _t('inspector.node'),
19777                 w: _t('inspector.way'),
19778                 r: _t('inspector.relation')
19779             }[id.charAt(0)];
19780         }
19781
19782
19783         function utilDisplayLabel(entity, graph) {
19784             var displayName = utilDisplayName(entity);
19785             if (displayName) {
19786                 // use the display name if there is one
19787                 return displayName;
19788             }
19789             var preset = _mainPresetIndex.match(entity, graph);
19790             if (preset && preset.name()) {
19791                 // use the preset name if there is a match
19792                 return preset.name();
19793             }
19794             // fallback to the display type (node/way/relation)
19795             return utilDisplayType(entity.id);
19796         }
19797
19798
19799         function utilEntityRoot(entityType) {
19800             return {
19801                 node: 'n',
19802                 way: 'w',
19803                 relation: 'r'
19804             }[entityType];
19805         }
19806
19807
19808         // Returns a single object containing the tags of all the given entities.
19809         // Example:
19810         // {
19811         //   highway: 'service',
19812         //   service: 'parking_aisle'
19813         // }
19814         //           +
19815         // {
19816         //   highway: 'service',
19817         //   service: 'driveway',
19818         //   width: '3'
19819         // }
19820         //           =
19821         // {
19822         //   highway: 'service',
19823         //   service: [ 'driveway', 'parking_aisle' ],
19824         //   width: [ '3', undefined ]
19825         // }
19826         function utilCombinedTags(entityIDs, graph) {
19827
19828             var tags = {};
19829             var tagCounts = {};
19830             var allKeys = new Set();
19831
19832             var entities = entityIDs.map(function(entityID) {
19833                 return graph.hasEntity(entityID);
19834             }).filter(Boolean);
19835
19836             // gather the aggregate keys
19837             entities.forEach(function(entity) {
19838                 var keys = Object.keys(entity.tags).filter(Boolean);
19839                 keys.forEach(function(key) {
19840                     allKeys.add(key);
19841                 });
19842             });
19843
19844             entities.forEach(function(entity) {
19845
19846                 allKeys.forEach(function(key) {
19847
19848                     var value = entity.tags[key]; // purposely allow `undefined`
19849
19850                     if (!tags.hasOwnProperty(key)) {
19851                         // first value, set as raw
19852                         tags[key] = value;
19853                     } else {
19854                         if (!Array.isArray(tags[key])) {
19855                             if (tags[key] !== value) {
19856                                 // first alternate value, replace single value with array
19857                                 tags[key] = [tags[key], value];
19858                             }
19859                         } else { // type is array
19860                             if (tags[key].indexOf(value) === -1) {
19861                                 // subsequent alternate value, add to array
19862                                 tags[key].push(value);
19863                             }
19864                         }
19865                     }
19866
19867                     var tagHash = key + '=' + value;
19868                     if (!tagCounts[tagHash]) tagCounts[tagHash] = 0;
19869                     tagCounts[tagHash] += 1;
19870                 });
19871             });
19872
19873             for (var key in tags) {
19874                 if (!Array.isArray(tags[key])) continue;
19875
19876                 // sort values by frequency then alphabetically
19877                 tags[key] = tags[key].sort(function(val1, val2) {
19878                     var key = key; // capture
19879                     var count2 = tagCounts[key + '=' + val2];
19880                     var count1 = tagCounts[key + '=' + val1];
19881                     if (count2 !== count1) {
19882                         return count2 - count1;
19883                     }
19884                     if (val2 && val1) {
19885                         return val1.localeCompare(val2);
19886                     }
19887                     return val1 ? 1 : -1;
19888                 });
19889             }
19890
19891             return tags;
19892         }
19893
19894
19895         function utilStringQs(str) {
19896             var i = 0;  // advance past any leading '?' or '#' characters
19897             while (i < str.length && (str[i] === '?' || str[i] === '#')) i++;
19898             str = str.slice(i);
19899
19900             return str.split('&').reduce(function(obj, pair){
19901                 var parts = pair.split('=');
19902                 if (parts.length === 2) {
19903                     obj[parts[0]] = (null === parts[1]) ? '' : decodeURIComponent(parts[1]);
19904                 }
19905                 return obj;
19906             }, {});
19907         }
19908
19909
19910         function utilQsString(obj, noencode) {
19911             // encode everything except special characters used in certain hash parameters:
19912             // "/" in map states, ":", ",", {" and "}" in background
19913             function softEncode(s) {
19914                 return encodeURIComponent(s).replace(/(%2F|%3A|%2C|%7B|%7D)/g, decodeURIComponent);
19915             }
19916
19917             return Object.keys(obj).sort().map(function(key) {
19918                 return encodeURIComponent(key) + '=' + (
19919                     noencode ? softEncode(obj[key]) : encodeURIComponent(obj[key]));
19920             }).join('&');
19921         }
19922
19923
19924         function utilPrefixDOMProperty(property) {
19925             var prefixes = ['webkit', 'ms', 'moz', 'o'];
19926             var i = -1;
19927             var n = prefixes.length;
19928             var s = document.body;
19929
19930             if (property in s)
19931                 return property;
19932
19933             property = property.substr(0, 1).toUpperCase() + property.substr(1);
19934
19935             while (++i < n) {
19936                 if (prefixes[i] + property in s) {
19937                     return prefixes[i] + property;
19938                 }
19939             }
19940
19941             return false;
19942         }
19943
19944
19945         function utilPrefixCSSProperty(property) {
19946             var prefixes = ['webkit', 'ms', 'Moz', 'O'];
19947             var i = -1;
19948             var n = prefixes.length;
19949             var s = document.body.style;
19950
19951             if (property.toLowerCase() in s) {
19952                 return property.toLowerCase();
19953             }
19954
19955             while (++i < n) {
19956                 if (prefixes[i] + property in s) {
19957                     return '-' + prefixes[i].toLowerCase() + property.replace(/([A-Z])/g, '-$1').toLowerCase();
19958                 }
19959             }
19960
19961             return false;
19962         }
19963
19964
19965         var transformProperty;
19966         function utilSetTransform(el, x, y, scale) {
19967             var prop = transformProperty = transformProperty || utilPrefixCSSProperty('Transform');
19968             var translate = utilDetect().opera ? 'translate('   + x + 'px,' + y + 'px)'
19969                 : 'translate3d(' + x + 'px,' + y + 'px,0)';
19970             return el.style(prop, translate + (scale ? ' scale(' + scale + ')' : ''));
19971         }
19972
19973
19974         // Calculates Levenshtein distance between two strings
19975         // see:  https://en.wikipedia.org/wiki/Levenshtein_distance
19976         // first converts the strings to lowercase and replaces diacritic marks with ascii equivalents.
19977         function utilEditDistance(a, b) {
19978             a = remove$1(a.toLowerCase());
19979             b = remove$1(b.toLowerCase());
19980             if (a.length === 0) return b.length;
19981             if (b.length === 0) return a.length;
19982             var matrix = [];
19983             for (var i = 0; i <= b.length; i++) { matrix[i] = [i]; }
19984             for (var j = 0; j <= a.length; j++) { matrix[0][j] = j; }
19985             for (i = 1; i <= b.length; i++) {
19986                 for (j = 1; j <= a.length; j++) {
19987                     if (b.charAt(i-1) === a.charAt(j-1)) {
19988                         matrix[i][j] = matrix[i-1][j-1];
19989                     } else {
19990                         matrix[i][j] = Math.min(matrix[i-1][j-1] + 1, // substitution
19991                             Math.min(matrix[i][j-1] + 1, // insertion
19992                             matrix[i-1][j] + 1)); // deletion
19993                     }
19994                 }
19995             }
19996             return matrix[b.length][a.length];
19997         }
19998
19999
20000         // a d3.mouse-alike which
20001         // 1. Only works on HTML elements, not SVG
20002         // 2. Does not cause style recalculation
20003         function utilFastMouse(container) {
20004             var rect = container.getBoundingClientRect();
20005             var rectLeft = rect.left;
20006             var rectTop = rect.top;
20007             var clientLeft = +container.clientLeft;
20008             var clientTop = +container.clientTop;
20009             return function(e) {
20010                 return [
20011                     e.clientX - rectLeft - clientLeft,
20012                     e.clientY - rectTop - clientTop];
20013             };
20014         }
20015
20016
20017         function utilAsyncMap(inputs, func, callback) {
20018             var remaining = inputs.length;
20019             var results = [];
20020             var errors = [];
20021
20022             inputs.forEach(function(d, i) {
20023                 func(d, function done(err, data) {
20024                     errors[i] = err;
20025                     results[i] = data;
20026                     remaining--;
20027                     if (!remaining) callback(errors, results);
20028                 });
20029             });
20030         }
20031
20032
20033         // wraps an index to an interval [0..length-1]
20034         function utilWrap(index, length) {
20035             if (index < 0) {
20036                 index += Math.ceil(-index/length)*length;
20037             }
20038             return index % length;
20039         }
20040
20041
20042         /**
20043          * a replacement for functor
20044          *
20045          * @param {*} value any value
20046          * @returns {Function} a function that returns that value or the value if it's a function
20047          */
20048         function utilFunctor(value) {
20049             if (typeof value === 'function') return value;
20050             return function() {
20051                 return value;
20052             };
20053         }
20054
20055
20056         function utilNoAuto(selection) {
20057             var isText = (selection.size() && selection.node().tagName.toLowerCase() === 'textarea');
20058
20059             return selection
20060                 // assign 'new-password' even for non-password fields to prevent browsers (Chrome) ignoring 'off'
20061                 .attr('autocomplete', 'new-password')
20062                 .attr('autocorrect', 'off')
20063                 .attr('autocapitalize', 'off')
20064                 .attr('spellcheck', isText ? 'true' : 'false');
20065         }
20066
20067
20068         // https://stackoverflow.com/questions/194846/is-there-any-kind-of-hash-code-function-in-javascript
20069         // https://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/
20070         function utilHashcode(str) {
20071             var hash = 0;
20072             if (str.length === 0) {
20073                 return hash;
20074             }
20075             for (var i = 0; i < str.length; i++) {
20076                 var char = str.charCodeAt(i);
20077                 hash = ((hash << 5) - hash) + char;
20078                 hash = hash & hash; // Convert to 32bit integer
20079             }
20080             return hash;
20081         }
20082
20083         // Returns version of `str` with all runs of special characters replaced by `_`;
20084         // suitable for HTML ids, classes, selectors, etc.
20085         function utilSafeClassName(str) {
20086             return str.toLowerCase().replace(/[^a-z0-9]+/g, '_');
20087         }
20088
20089         // Returns string based on `val` that is highly unlikely to collide with an id
20090         // used previously or that's present elsewhere in the document. Useful for preventing
20091         // browser-provided autofills or when embedding iD on pages with unknown elements.
20092         function utilUniqueDomId(val) {
20093             return 'ideditor-' + utilSafeClassName(val.toString()) + '-' + new Date().getTime().toString();
20094         }
20095
20096         // Returns the length of `str` in unicode characters. This can be less than
20097         // `String.length()` since a single unicode character can be composed of multiple
20098         // JavaScript UTF-16 code units.
20099         function utilUnicodeCharsCount(str) {
20100             // Native ES2015 implementations of `Array.from` split strings into unicode characters
20101             return Array.from(str).length;
20102         }
20103
20104         // Returns a new string representing `str` cut from its start to `limit` length
20105         // in unicode characters. Note that this runs the risk of splitting graphemes.
20106         function utilUnicodeCharsTruncated(str, limit) {
20107             return Array.from(str).slice(0, limit).join('');
20108         }
20109
20110         function osmEntity(attrs) {
20111             // For prototypal inheritance.
20112             if (this instanceof osmEntity) return;
20113
20114             // Create the appropriate subtype.
20115             if (attrs && attrs.type) {
20116                 return osmEntity[attrs.type].apply(this, arguments);
20117             } else if (attrs && attrs.id) {
20118                 return osmEntity[osmEntity.id.type(attrs.id)].apply(this, arguments);
20119             }
20120
20121             // Initialize a generic Entity (used only in tests).
20122             return (new osmEntity()).initialize(arguments);
20123         }
20124
20125
20126         osmEntity.id = function(type) {
20127             return osmEntity.id.fromOSM(type, osmEntity.id.next[type]--);
20128         };
20129
20130
20131         osmEntity.id.next = {
20132             changeset: -1, node: -1, way: -1, relation: -1
20133         };
20134
20135
20136         osmEntity.id.fromOSM = function(type, id) {
20137             return type[0] + id;
20138         };
20139
20140
20141         osmEntity.id.toOSM = function(id) {
20142             return id.slice(1);
20143         };
20144
20145
20146         osmEntity.id.type = function(id) {
20147             return { 'c': 'changeset', 'n': 'node', 'w': 'way', 'r': 'relation' }[id[0]];
20148         };
20149
20150
20151         // A function suitable for use as the second argument to d3.selection#data().
20152         osmEntity.key = function(entity) {
20153             return entity.id + 'v' + (entity.v || 0);
20154         };
20155
20156         var _deprecatedTagValuesByKey;
20157
20158         osmEntity.deprecatedTagValuesByKey = function(dataDeprecated) {
20159             if (!_deprecatedTagValuesByKey) {
20160                 _deprecatedTagValuesByKey = {};
20161                 dataDeprecated.forEach(function(d) {
20162                     var oldKeys = Object.keys(d.old);
20163                     if (oldKeys.length === 1) {
20164                         var oldKey = oldKeys[0];
20165                         var oldValue = d.old[oldKey];
20166                         if (oldValue !== '*') {
20167                             if (!_deprecatedTagValuesByKey[oldKey]) {
20168                                 _deprecatedTagValuesByKey[oldKey] = [oldValue];
20169                             } else {
20170                                 _deprecatedTagValuesByKey[oldKey].push(oldValue);
20171                             }
20172                         }
20173                     }
20174                 });
20175             }
20176             return _deprecatedTagValuesByKey;
20177         };
20178
20179
20180         osmEntity.prototype = {
20181
20182             tags: {},
20183
20184
20185             initialize: function(sources) {
20186                 for (var i = 0; i < sources.length; ++i) {
20187                     var source = sources[i];
20188                     for (var prop in source) {
20189                         if (Object.prototype.hasOwnProperty.call(source, prop)) {
20190                             if (source[prop] === undefined) {
20191                                 delete this[prop];
20192                             } else {
20193                                 this[prop] = source[prop];
20194                             }
20195                         }
20196                     }
20197                 }
20198
20199                 if (!this.id && this.type) {
20200                     this.id = osmEntity.id(this.type);
20201                 }
20202                 if (!this.hasOwnProperty('visible')) {
20203                     this.visible = true;
20204                 }
20205
20206                 return this;
20207             },
20208
20209
20210             copy: function(resolver, copies) {
20211                 if (copies[this.id])
20212                     return copies[this.id];
20213
20214                 var copy = osmEntity(this, { id: undefined, user: undefined, version: undefined });
20215                 copies[this.id] = copy;
20216
20217                 return copy;
20218             },
20219
20220
20221             osmId: function() {
20222                 return osmEntity.id.toOSM(this.id);
20223             },
20224
20225
20226             isNew: function() {
20227                 return this.osmId() < 0;
20228             },
20229
20230
20231             update: function(attrs) {
20232                 return osmEntity(this, attrs, { v: 1 + (this.v || 0) });
20233             },
20234
20235
20236             mergeTags: function(tags) {
20237                 var merged = Object.assign({}, this.tags);   // shallow copy
20238                 var changed = false;
20239                 for (var k in tags) {
20240                     var t1 = merged[k];
20241                     var t2 = tags[k];
20242                     if (!t1) {
20243                         changed = true;
20244                         merged[k] = t2;
20245                     } else if (t1 !== t2) {
20246                         changed = true;
20247                         merged[k] = utilUnicodeCharsTruncated(
20248                             utilArrayUnion(t1.split(/;\s*/), t2.split(/;\s*/)).join(';'),
20249                             255 // avoid exceeding character limit; see also services/osm.js -> maxCharsForTagValue()
20250                         );
20251                     }
20252                 }
20253                 return changed ? this.update({ tags: merged }) : this;
20254             },
20255
20256
20257             intersects: function(extent, resolver) {
20258                 return this.extent(resolver).intersects(extent);
20259             },
20260
20261
20262             hasNonGeometryTags: function() {
20263                 return Object.keys(this.tags).some(function(k) { return k !== 'area'; });
20264             },
20265
20266             hasParentRelations: function(resolver) {
20267                 return resolver.parentRelations(this).length > 0;
20268             },
20269
20270             hasInterestingTags: function() {
20271                 return Object.keys(this.tags).some(osmIsInterestingTag);
20272             },
20273
20274             hasWikidata: function() {
20275                 return !!this.tags.wikidata || !!this.tags['brand:wikidata'];
20276             },
20277
20278             isHighwayIntersection: function() {
20279                 return false;
20280             },
20281
20282             isDegenerate: function() {
20283                 return true;
20284             },
20285
20286             deprecatedTags: function(dataDeprecated) {
20287                 var tags = this.tags;
20288
20289                 // if there are no tags, none can be deprecated
20290                 if (Object.keys(tags).length === 0) return [];
20291
20292                 var deprecated = [];
20293                 dataDeprecated.forEach(function(d) {
20294                     var oldKeys = Object.keys(d.old);
20295                     var matchesDeprecatedTags = oldKeys.every(function(oldKey) {
20296                         if (!tags[oldKey]) return false;
20297                         if (d.old[oldKey] === '*') return true;
20298
20299                         var vals = tags[oldKey].split(';').filter(Boolean);
20300                         if (vals.length === 0) {
20301                             return false;
20302                         } else if (vals.length > 1) {
20303                             return vals.indexOf(d.old[oldKey]) !== -1;
20304                         } else {
20305                             if (tags[oldKey] === d.old[oldKey]) {
20306                                 if (d.replace && d.old[oldKey] === d.replace[oldKey]) {
20307                                     var replaceKeys = Object.keys(d.replace);
20308                                     return !replaceKeys.every(function(replaceKey) {
20309                                         return tags[replaceKey] === d.replace[replaceKey];
20310                                     });
20311                                 } else {
20312                                     return true;
20313                                 }
20314                             }
20315                         }
20316                         return false;
20317                     });
20318                     if (matchesDeprecatedTags) {
20319                         deprecated.push(d);
20320                     }
20321                 });
20322
20323                 return deprecated;
20324             }
20325         };
20326
20327         function osmLanes(entity) {
20328             if (entity.type !== 'way') return null;
20329             if (!entity.tags.highway) return null;
20330
20331             var tags = entity.tags;
20332             var isOneWay = entity.isOneWay();
20333             var laneCount = getLaneCount(tags, isOneWay);
20334             var maxspeed = parseMaxspeed(tags);
20335
20336             var laneDirections = parseLaneDirections(tags, isOneWay, laneCount);
20337             var forward = laneDirections.forward;
20338             var backward = laneDirections.backward;
20339             var bothways = laneDirections.bothways;
20340
20341             // parse the piped string 'x|y|z' format
20342             var turnLanes = {};
20343             turnLanes.unspecified = parseTurnLanes(tags['turn:lanes']);
20344             turnLanes.forward = parseTurnLanes(tags['turn:lanes:forward']);
20345             turnLanes.backward = parseTurnLanes(tags['turn:lanes:backward']);
20346
20347             var maxspeedLanes = {};
20348             maxspeedLanes.unspecified = parseMaxspeedLanes(tags['maxspeed:lanes'], maxspeed);
20349             maxspeedLanes.forward = parseMaxspeedLanes(tags['maxspeed:lanes:forward'], maxspeed);
20350             maxspeedLanes.backward = parseMaxspeedLanes(tags['maxspeed:lanes:backward'], maxspeed);
20351
20352             var psvLanes = {};
20353             psvLanes.unspecified = parseMiscLanes(tags['psv:lanes']);
20354             psvLanes.forward = parseMiscLanes(tags['psv:lanes:forward']);
20355             psvLanes.backward = parseMiscLanes(tags['psv:lanes:backward']);
20356
20357             var busLanes = {};
20358             busLanes.unspecified = parseMiscLanes(tags['bus:lanes']);
20359             busLanes.forward = parseMiscLanes(tags['bus:lanes:forward']);
20360             busLanes.backward = parseMiscLanes(tags['bus:lanes:backward']);
20361
20362             var taxiLanes = {};
20363             taxiLanes.unspecified = parseMiscLanes(tags['taxi:lanes']);
20364             taxiLanes.forward = parseMiscLanes(tags['taxi:lanes:forward']);
20365             taxiLanes.backward = parseMiscLanes(tags['taxi:lanes:backward']);
20366
20367             var hovLanes = {};
20368             hovLanes.unspecified = parseMiscLanes(tags['hov:lanes']);
20369             hovLanes.forward = parseMiscLanes(tags['hov:lanes:forward']);
20370             hovLanes.backward = parseMiscLanes(tags['hov:lanes:backward']);
20371
20372             var hgvLanes = {};
20373             hgvLanes.unspecified = parseMiscLanes(tags['hgv:lanes']);
20374             hgvLanes.forward = parseMiscLanes(tags['hgv:lanes:forward']);
20375             hgvLanes.backward = parseMiscLanes(tags['hgv:lanes:backward']);
20376
20377             var bicyclewayLanes = {};
20378             bicyclewayLanes.unspecified = parseBicycleWay(tags['bicycleway:lanes']);
20379             bicyclewayLanes.forward = parseBicycleWay(tags['bicycleway:lanes:forward']);
20380             bicyclewayLanes.backward = parseBicycleWay(tags['bicycleway:lanes:backward']);
20381
20382             var lanesObj = {
20383                 forward: [],
20384                 backward: [],
20385                 unspecified: []
20386             };
20387
20388             // map forward/backward/unspecified of each lane type to lanesObj
20389             mapToLanesObj(lanesObj, turnLanes, 'turnLane');
20390             mapToLanesObj(lanesObj, maxspeedLanes, 'maxspeed');
20391             mapToLanesObj(lanesObj, psvLanes, 'psv');
20392             mapToLanesObj(lanesObj, busLanes, 'bus');
20393             mapToLanesObj(lanesObj, taxiLanes, 'taxi');
20394             mapToLanesObj(lanesObj, hovLanes, 'hov');
20395             mapToLanesObj(lanesObj, hgvLanes, 'hgv');
20396             mapToLanesObj(lanesObj, bicyclewayLanes, 'bicycleway');
20397
20398             return {
20399                 metadata: {
20400                     count: laneCount,
20401                     oneway: isOneWay,
20402                     forward: forward,
20403                     backward: backward,
20404                     bothways: bothways,
20405                     turnLanes: turnLanes,
20406                     maxspeed: maxspeed,
20407                     maxspeedLanes: maxspeedLanes,
20408                     psvLanes: psvLanes,
20409                     busLanes: busLanes,
20410                     taxiLanes: taxiLanes,
20411                     hovLanes: hovLanes,
20412                     hgvLanes: hgvLanes,
20413                     bicyclewayLanes: bicyclewayLanes
20414                 },
20415                 lanes: lanesObj
20416             };
20417         }
20418
20419
20420         function getLaneCount(tags, isOneWay) {
20421             var count;
20422             if (tags.lanes) {
20423                 count = parseInt(tags.lanes, 10);
20424                 if (count > 0) {
20425                     return count;
20426                 }
20427             }
20428
20429
20430             switch (tags.highway) {
20431                 case 'trunk':
20432                 case 'motorway':
20433                     count = isOneWay ? 2 : 4;
20434                     break;
20435                 default:
20436                     count = isOneWay ? 1 : 2;
20437                     break;
20438             }
20439
20440             return count;
20441         }
20442
20443
20444         function parseMaxspeed(tags) {
20445             var maxspeed = tags.maxspeed;
20446             if (!maxspeed) return;
20447
20448             var maxspeedRegex = /^([0-9][\.0-9]+?)(?:[ ]?(?:km\/h|kmh|kph|mph|knots))?$/;
20449             if (!maxspeedRegex.test(maxspeed)) return;
20450
20451             return parseInt(maxspeed, 10);
20452         }
20453
20454
20455         function parseLaneDirections(tags, isOneWay, laneCount) {
20456             var forward = parseInt(tags['lanes:forward'], 10);
20457             var backward = parseInt(tags['lanes:backward'], 10);
20458             var bothways = parseInt(tags['lanes:both_ways'], 10) > 0 ? 1 : 0;
20459
20460             if (parseInt(tags.oneway, 10) === -1) {
20461                 forward = 0;
20462                 bothways = 0;
20463                 backward = laneCount;
20464             }
20465             else if (isOneWay) {
20466                 forward = laneCount;
20467                 bothways = 0;
20468                 backward = 0;
20469             }
20470             else if (isNaN(forward) && isNaN(backward)) {
20471                 backward = Math.floor((laneCount - bothways) / 2);
20472                 forward = laneCount - bothways - backward;
20473             }
20474             else if (isNaN(forward)) {
20475                 if (backward > laneCount - bothways) {
20476                     backward = laneCount - bothways;
20477                 }
20478                 forward = laneCount - bothways - backward;
20479             }
20480             else if (isNaN(backward)) {
20481                 if (forward > laneCount - bothways) {
20482                     forward = laneCount - bothways;
20483                 }
20484                 backward = laneCount - bothways - forward;
20485             }
20486             return {
20487                 forward: forward,
20488                 backward: backward,
20489                 bothways: bothways
20490             };
20491         }
20492
20493
20494         function parseTurnLanes(tag){
20495             if (!tag) return;
20496
20497             var validValues = [
20498                 'left', 'slight_left', 'sharp_left', 'through', 'right', 'slight_right',
20499                 'sharp_right', 'reverse', 'merge_to_left', 'merge_to_right', 'none'
20500             ];
20501
20502             return tag.split('|')
20503                 .map(function (s) {
20504                     if (s === '') s = 'none';
20505                     return s.split(';')
20506                         .map(function (d) {
20507                             return validValues.indexOf(d) === -1 ? 'unknown': d;
20508                         });
20509                 });
20510         }
20511
20512
20513         function parseMaxspeedLanes(tag, maxspeed) {
20514             if (!tag) return;
20515
20516             return tag.split('|')
20517                 .map(function (s) {
20518                     if (s === 'none') return s;
20519                     var m = parseInt(s, 10);
20520                     if (s === '' || m === maxspeed) return null;
20521                     return isNaN(m) ? 'unknown': m;
20522                 });
20523         }
20524
20525
20526         function parseMiscLanes(tag) {
20527             if (!tag) return;
20528
20529             var validValues = [
20530                 'yes', 'no', 'designated'
20531             ];
20532
20533             return tag.split('|')
20534                 .map(function (s) {
20535                     if (s === '') s = 'no';
20536                     return validValues.indexOf(s) === -1 ? 'unknown': s;
20537                 });
20538         }
20539
20540
20541         function parseBicycleWay(tag) {
20542             if (!tag) return;
20543
20544             var validValues = [
20545                 'yes', 'no', 'designated', 'lane'
20546             ];
20547
20548             return tag.split('|')
20549                 .map(function (s) {
20550                     if (s === '') s = 'no';
20551                     return validValues.indexOf(s) === -1 ? 'unknown': s;
20552                 });
20553         }
20554
20555
20556         function mapToLanesObj(lanesObj, data, key) {
20557             if (data.forward) data.forward.forEach(function(l, i) {
20558                 if (!lanesObj.forward[i]) lanesObj.forward[i] = {};
20559                 lanesObj.forward[i][key] = l;
20560             });
20561             if (data.backward) data.backward.forEach(function(l, i) {
20562                 if (!lanesObj.backward[i]) lanesObj.backward[i] = {};
20563                 lanesObj.backward[i][key] = l;
20564             });
20565             if (data.unspecified) data.unspecified.forEach(function(l, i) {
20566                 if (!lanesObj.unspecified[i]) lanesObj.unspecified[i] = {};
20567                 lanesObj.unspecified[i][key] = l;
20568             });
20569         }
20570
20571         function osmWay() {
20572             if (!(this instanceof osmWay)) {
20573                 return (new osmWay()).initialize(arguments);
20574             } else if (arguments.length) {
20575                 this.initialize(arguments);
20576             }
20577         }
20578
20579
20580         osmEntity.way = osmWay;
20581
20582         osmWay.prototype = Object.create(osmEntity.prototype);
20583
20584
20585         Object.assign(osmWay.prototype, {
20586             type: 'way',
20587             nodes: [],
20588
20589
20590             copy: function(resolver, copies) {
20591                 if (copies[this.id]) return copies[this.id];
20592
20593                 var copy = osmEntity.prototype.copy.call(this, resolver, copies);
20594
20595                 var nodes = this.nodes.map(function(id) {
20596                     return resolver.entity(id).copy(resolver, copies).id;
20597                 });
20598
20599                 copy = copy.update({ nodes: nodes });
20600                 copies[this.id] = copy;
20601
20602                 return copy;
20603             },
20604
20605
20606             extent: function(resolver) {
20607                 return resolver.transient(this, 'extent', function() {
20608                     var extent = geoExtent();
20609                     for (var i = 0; i < this.nodes.length; i++) {
20610                         var node = resolver.hasEntity(this.nodes[i]);
20611                         if (node) {
20612                             extent._extend(node.extent());
20613                         }
20614                     }
20615                     return extent;
20616                 });
20617             },
20618
20619
20620             first: function() {
20621                 return this.nodes[0];
20622             },
20623
20624
20625             last: function() {
20626                 return this.nodes[this.nodes.length - 1];
20627             },
20628
20629
20630             contains: function(node) {
20631                 return this.nodes.indexOf(node) >= 0;
20632             },
20633
20634
20635             affix: function(node) {
20636                 if (this.nodes[0] === node) return 'prefix';
20637                 if (this.nodes[this.nodes.length - 1] === node) return 'suffix';
20638             },
20639
20640
20641             layer: function() {
20642                 // explicit layer tag, clamp between -10, 10..
20643                 if (isFinite(this.tags.layer)) {
20644                     return Math.max(-10, Math.min(+(this.tags.layer), 10));
20645                 }
20646
20647                 // implied layer tag..
20648                 if (this.tags.covered === 'yes') return -1;
20649                 if (this.tags.location === 'overground') return 1;
20650                 if (this.tags.location === 'underground') return -1;
20651                 if (this.tags.location === 'underwater') return -10;
20652
20653                 if (this.tags.power === 'line') return 10;
20654                 if (this.tags.power === 'minor_line') return 10;
20655                 if (this.tags.aerialway) return 10;
20656                 if (this.tags.bridge) return 1;
20657                 if (this.tags.cutting) return -1;
20658                 if (this.tags.tunnel) return -1;
20659                 if (this.tags.waterway) return -1;
20660                 if (this.tags.man_made === 'pipeline') return -10;
20661                 if (this.tags.boundary) return -10;
20662                 return 0;
20663             },
20664
20665
20666             // the approximate width of the line based on its tags except its `width` tag
20667             impliedLineWidthMeters: function() {
20668                 var averageWidths = {
20669                     highway: { // width is for single lane
20670                         motorway: 5, motorway_link: 5, trunk: 4.5, trunk_link: 4.5,
20671                         primary: 4, secondary: 4, tertiary: 4,
20672                         primary_link: 4, secondary_link: 4, tertiary_link: 4,
20673                         unclassified: 4, road: 4, living_street: 4, bus_guideway: 4, pedestrian: 4,
20674                         residential: 3.5, service: 3.5, track: 3, cycleway: 2.5,
20675                         bridleway: 2, corridor: 2, steps: 2, path: 1.5, footway: 1.5
20676                     },
20677                     railway: { // width includes ties and rail bed, not just track gauge
20678                         rail: 2.5, light_rail: 2.5, tram: 2.5, subway: 2.5,
20679                         monorail: 2.5, funicular: 2.5, disused: 2.5, preserved: 2.5,
20680                         miniature: 1.5, narrow_gauge: 1.5
20681                     },
20682                     waterway: {
20683                         river: 50, canal: 25, stream: 5, tidal_channel: 5, fish_pass: 2.5, drain: 2.5, ditch: 1.5
20684                     }
20685                 };
20686                 for (var key in averageWidths) {
20687                     if (this.tags[key] && averageWidths[key][this.tags[key]]) {
20688                         var width = averageWidths[key][this.tags[key]];
20689                         if (key === 'highway') {
20690                             var laneCount = this.tags.lanes && parseInt(this.tags.lanes, 10);
20691                             if (!laneCount) laneCount = this.isOneWay() ? 1 : 2;
20692
20693                             return width * laneCount;
20694                         }
20695                         return width;
20696                     }
20697                 }
20698                 return null;
20699             },
20700
20701
20702             isOneWay: function() {
20703                 // explicit oneway tag..
20704                 var values = {
20705                     'yes': true,
20706                     '1': true,
20707                     '-1': true,
20708                     'reversible': true,
20709                     'alternating': true,
20710                     'no': false,
20711                     '0': false
20712                 };
20713                 if (values[this.tags.oneway] !== undefined) {
20714                     return values[this.tags.oneway];
20715                 }
20716
20717                 // implied oneway tag..
20718                 for (var key in this.tags) {
20719                     if (key in osmOneWayTags && (this.tags[key] in osmOneWayTags[key]))
20720                         return true;
20721                 }
20722                 return false;
20723             },
20724
20725             // Some identifier for tag that implies that this way is "sided",
20726             // i.e. the right side is the 'inside' (e.g. the right side of a
20727             // natural=cliff is lower).
20728             sidednessIdentifier: function() {
20729                 for (var key in this.tags) {
20730                     var value = this.tags[key];
20731                     if (key in osmRightSideIsInsideTags && (value in osmRightSideIsInsideTags[key])) {
20732                         if (osmRightSideIsInsideTags[key][value] === true) {
20733                             return key;
20734                         } else {
20735                             // if the map's value is something other than a
20736                             // literal true, we should use it so we can
20737                             // special case some keys (e.g. natural=coastline
20738                             // is handled differently to other naturals).
20739                             return osmRightSideIsInsideTags[key][value];
20740                         }
20741                     }
20742                 }
20743
20744                 return null;
20745             },
20746
20747             isSided: function() {
20748                 if (this.tags.two_sided === 'yes') {
20749                     return false;
20750                 }
20751
20752                 return this.sidednessIdentifier() !== null;
20753             },
20754
20755             lanes: function() {
20756                 return osmLanes(this);
20757             },
20758
20759
20760             isClosed: function() {
20761                 return this.nodes.length > 1 && this.first() === this.last();
20762             },
20763
20764
20765             isConvex: function(resolver) {
20766                 if (!this.isClosed() || this.isDegenerate()) return null;
20767
20768                 var nodes = utilArrayUniq(resolver.childNodes(this));
20769                 var coords = nodes.map(function(n) { return n.loc; });
20770                 var curr = 0;
20771                 var prev = 0;
20772
20773                 for (var i = 0; i < coords.length; i++) {
20774                     var o = coords[(i+1) % coords.length];
20775                     var a = coords[i];
20776                     var b = coords[(i+2) % coords.length];
20777                     var res = geoVecCross(a, b, o);
20778
20779                     curr = (res > 0) ? 1 : (res < 0) ? -1 : 0;
20780                     if (curr === 0) {
20781                         continue;
20782                     } else if (prev && curr !== prev) {
20783                         return false;
20784                     }
20785                     prev = curr;
20786                 }
20787                 return true;
20788             },
20789
20790             // returns an object with the tag that implies this is an area, if any
20791             tagSuggestingArea: function() {
20792                 return osmTagSuggestingArea(this.tags);
20793             },
20794
20795             isArea: function() {
20796                 if (this.tags.area === 'yes')
20797                     return true;
20798                 if (!this.isClosed() || this.tags.area === 'no')
20799                     return false;
20800                 return this.tagSuggestingArea() !== null;
20801             },
20802
20803
20804             isDegenerate: function() {
20805                 return (new Set(this.nodes).size < (this.isArea() ? 3 : 2));
20806             },
20807
20808
20809             areAdjacent: function(n1, n2) {
20810                 for (var i = 0; i < this.nodes.length; i++) {
20811                     if (this.nodes[i] === n1) {
20812                         if (this.nodes[i - 1] === n2) return true;
20813                         if (this.nodes[i + 1] === n2) return true;
20814                     }
20815                 }
20816                 return false;
20817             },
20818
20819
20820             geometry: function(graph) {
20821                 return graph.transient(this, 'geometry', function() {
20822                     return this.isArea() ? 'area' : 'line';
20823                 });
20824             },
20825
20826
20827             // returns an array of objects representing the segments between the nodes in this way
20828             segments: function(graph) {
20829
20830                 function segmentExtent(graph) {
20831                     var n1 = graph.hasEntity(this.nodes[0]);
20832                     var n2 = graph.hasEntity(this.nodes[1]);
20833                     return n1 && n2 && geoExtent([
20834                         [
20835                             Math.min(n1.loc[0], n2.loc[0]),
20836                             Math.min(n1.loc[1], n2.loc[1])
20837                         ],
20838                         [
20839                             Math.max(n1.loc[0], n2.loc[0]),
20840                             Math.max(n1.loc[1], n2.loc[1])
20841                         ]
20842                     ]);
20843                 }
20844
20845                 return graph.transient(this, 'segments', function() {
20846                     var segments = [];
20847                     for (var i = 0; i < this.nodes.length - 1; i++) {
20848                         segments.push({
20849                             id: this.id + '-' + i,
20850                             wayId: this.id,
20851                             index: i,
20852                             nodes: [this.nodes[i], this.nodes[i + 1]],
20853                             extent: segmentExtent
20854                         });
20855                     }
20856                     return segments;
20857                 });
20858             },
20859
20860
20861             // If this way is not closed, append the beginning node to the end of the nodelist to close it.
20862             close: function() {
20863                 if (this.isClosed() || !this.nodes.length) return this;
20864
20865                 var nodes = this.nodes.slice();
20866                 nodes = nodes.filter(noRepeatNodes);
20867                 nodes.push(nodes[0]);
20868                 return this.update({ nodes: nodes });
20869             },
20870
20871
20872             // If this way is closed, remove any connector nodes from the end of the nodelist to unclose it.
20873             unclose: function() {
20874                 if (!this.isClosed()) return this;
20875
20876                 var nodes = this.nodes.slice();
20877                 var connector = this.first();
20878                 var i = nodes.length - 1;
20879
20880                 // remove trailing connectors..
20881                 while (i > 0 && nodes.length > 1 && nodes[i] === connector) {
20882                     nodes.splice(i, 1);
20883                     i = nodes.length - 1;
20884                 }
20885
20886                 nodes = nodes.filter(noRepeatNodes);
20887                 return this.update({ nodes: nodes });
20888             },
20889
20890
20891             // Adds a node (id) in front of the node which is currently at position index.
20892             // If index is undefined, the node will be added to the end of the way for linear ways,
20893             //   or just before the final connecting node for circular ways.
20894             // Consecutive duplicates are eliminated including existing ones.
20895             // Circularity is always preserved when adding a node.
20896             addNode: function(id, index) {
20897                 var nodes = this.nodes.slice();
20898                 var isClosed = this.isClosed();
20899                 var max = isClosed ? nodes.length - 1 : nodes.length;
20900
20901                 if (index === undefined) {
20902                     index = max;
20903                 }
20904
20905                 if (index < 0 || index > max) {
20906                     throw new RangeError('index ' + index + ' out of range 0..' + max);
20907                 }
20908
20909                 // If this is a closed way, remove all connector nodes except the first one
20910                 // (there may be duplicates) and adjust index if necessary..
20911                 if (isClosed) {
20912                     var connector = this.first();
20913
20914                     // leading connectors..
20915                     var i = 1;
20916                     while (i < nodes.length && nodes.length > 2 && nodes[i] === connector) {
20917                         nodes.splice(i, 1);
20918                         if (index > i) index--;
20919                     }
20920
20921                     // trailing connectors..
20922                     i = nodes.length - 1;
20923                     while (i > 0 && nodes.length > 1 && nodes[i] === connector) {
20924                         nodes.splice(i, 1);
20925                         if (index > i) index--;
20926                         i = nodes.length - 1;
20927                     }
20928                 }
20929
20930                 nodes.splice(index, 0, id);
20931                 nodes = nodes.filter(noRepeatNodes);
20932
20933                 // If the way was closed before, append a connector node to keep it closed..
20934                 if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
20935                     nodes.push(nodes[0]);
20936                 }
20937
20938                 return this.update({ nodes: nodes });
20939             },
20940
20941
20942             // Replaces the node which is currently at position index with the given node (id).
20943             // Consecutive duplicates are eliminated including existing ones.
20944             // Circularity is preserved when updating a node.
20945             updateNode: function(id, index) {
20946                 var nodes = this.nodes.slice();
20947                 var isClosed = this.isClosed();
20948                 var max = nodes.length - 1;
20949
20950                 if (index === undefined || index < 0 || index > max) {
20951                     throw new RangeError('index ' + index + ' out of range 0..' + max);
20952                 }
20953
20954                 // If this is a closed way, remove all connector nodes except the first one
20955                 // (there may be duplicates) and adjust index if necessary..
20956                 if (isClosed) {
20957                     var connector = this.first();
20958
20959                     // leading connectors..
20960                     var i = 1;
20961                     while (i < nodes.length && nodes.length > 2 && nodes[i] === connector) {
20962                         nodes.splice(i, 1);
20963                         if (index > i) index--;
20964                     }
20965
20966                     // trailing connectors..
20967                     i = nodes.length - 1;
20968                     while (i > 0 && nodes.length > 1 && nodes[i] === connector) {
20969                         nodes.splice(i, 1);
20970                         if (index === i) index = 0;  // update leading connector instead
20971                         i = nodes.length - 1;
20972                     }
20973                 }
20974
20975                 nodes.splice(index, 1, id);
20976                 nodes = nodes.filter(noRepeatNodes);
20977
20978                 // If the way was closed before, append a connector node to keep it closed..
20979                 if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
20980                     nodes.push(nodes[0]);
20981                 }
20982
20983                 return this.update({nodes: nodes});
20984             },
20985
20986
20987             // Replaces each occurrence of node id needle with replacement.
20988             // Consecutive duplicates are eliminated including existing ones.
20989             // Circularity is preserved.
20990             replaceNode: function(needleID, replacementID) {
20991                 var nodes = this.nodes.slice();
20992                 var isClosed = this.isClosed();
20993
20994                 for (var i = 0; i < nodes.length; i++) {
20995                     if (nodes[i] === needleID) {
20996                         nodes[i] = replacementID;
20997                     }
20998                 }
20999
21000                 nodes = nodes.filter(noRepeatNodes);
21001
21002                 // If the way was closed before, append a connector node to keep it closed..
21003                 if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
21004                     nodes.push(nodes[0]);
21005                 }
21006
21007                 return this.update({nodes: nodes});
21008             },
21009
21010
21011             // Removes each occurrence of node id.
21012             // Consecutive duplicates are eliminated including existing ones.
21013             // Circularity is preserved.
21014             removeNode: function(id) {
21015                 var nodes = this.nodes.slice();
21016                 var isClosed = this.isClosed();
21017
21018                 nodes = nodes
21019                     .filter(function(node) { return node !== id; })
21020                     .filter(noRepeatNodes);
21021
21022                 // If the way was closed before, append a connector node to keep it closed..
21023                 if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
21024                     nodes.push(nodes[0]);
21025                 }
21026
21027                 return this.update({nodes: nodes});
21028             },
21029
21030
21031             asJXON: function(changeset_id) {
21032                 var r = {
21033                     way: {
21034                         '@id': this.osmId(),
21035                         '@version': this.version || 0,
21036                         nd: this.nodes.map(function(id) {
21037                             return { keyAttributes: { ref: osmEntity.id.toOSM(id) } };
21038                         }, this),
21039                         tag: Object.keys(this.tags).map(function(k) {
21040                             return { keyAttributes: { k: k, v: this.tags[k] } };
21041                         }, this)
21042                     }
21043                 };
21044                 if (changeset_id) {
21045                     r.way['@changeset'] = changeset_id;
21046                 }
21047                 return r;
21048             },
21049
21050
21051             asGeoJSON: function(resolver) {
21052                 return resolver.transient(this, 'GeoJSON', function() {
21053                     var coordinates = resolver.childNodes(this)
21054                         .map(function(n) { return n.loc; });
21055
21056                     if (this.isArea() && this.isClosed()) {
21057                         return {
21058                             type: 'Polygon',
21059                             coordinates: [coordinates]
21060                         };
21061                     } else {
21062                         return {
21063                             type: 'LineString',
21064                             coordinates: coordinates
21065                         };
21066                     }
21067                 });
21068             },
21069
21070
21071             area: function(resolver) {
21072                 return resolver.transient(this, 'area', function() {
21073                     var nodes = resolver.childNodes(this);
21074
21075                     var json = {
21076                         type: 'Polygon',
21077                         coordinates: [ nodes.map(function(n) { return n.loc; }) ]
21078                     };
21079
21080                     if (!this.isClosed() && nodes.length) {
21081                         json.coordinates[0].push(nodes[0].loc);
21082                     }
21083
21084                     var area = d3_geoArea(json);
21085
21086                     // Heuristic for detecting counterclockwise winding order. Assumes
21087                     // that OpenStreetMap polygons are not hemisphere-spanning.
21088                     if (area > 2 * Math.PI) {
21089                         json.coordinates[0] = json.coordinates[0].reverse();
21090                         area = d3_geoArea(json);
21091                     }
21092
21093                     return isNaN(area) ? 0 : area;
21094                 });
21095             }
21096         });
21097
21098
21099         // Filter function to eliminate consecutive duplicates.
21100         function noRepeatNodes(node, i, arr) {
21101             return i === 0 || node !== arr[i - 1];
21102         }
21103
21104         // "Old" multipolyons, previously known as "simple" multipolygons, are as follows:
21105         //
21106         // 1. Relation tagged with `type=multipolygon` and no interesting tags.
21107         // 2. One and only one member with the `outer` role. Must be a way with interesting tags.
21108         // 3. No members without a role.
21109         //
21110         // Old multipolygons are no longer recommended but are still rendered as areas by iD.
21111
21112         function osmOldMultipolygonOuterMemberOfRelation(entity, graph) {
21113             if (entity.type !== 'relation' ||
21114                 !entity.isMultipolygon()
21115                 || Object.keys(entity.tags).filter(osmIsInterestingTag).length > 1) {
21116                 return false;
21117             }
21118
21119             var outerMember;
21120             for (var memberIndex in entity.members) {
21121                 var member = entity.members[memberIndex];
21122                 if (!member.role || member.role === 'outer') {
21123                     if (outerMember) return false;
21124                     if (member.type !== 'way') return false;
21125                     if (!graph.hasEntity(member.id)) return false;
21126
21127                     outerMember = graph.entity(member.id);
21128
21129                     if (Object.keys(outerMember.tags).filter(osmIsInterestingTag).length === 0) {
21130                         return false;
21131                     }
21132                 }
21133             }
21134
21135             return outerMember;
21136         }
21137
21138         // For fixing up rendering of multipolygons with tags on the outer member.
21139         // https://github.com/openstreetmap/iD/issues/613
21140         function osmIsOldMultipolygonOuterMember(entity, graph) {
21141             if (entity.type !== 'way' || Object.keys(entity.tags).filter(osmIsInterestingTag).length === 0)
21142                 return false;
21143
21144             var parents = graph.parentRelations(entity);
21145             if (parents.length !== 1)
21146                 return false;
21147
21148             var parent = parents[0];
21149             if (!parent.isMultipolygon() || Object.keys(parent.tags).filter(osmIsInterestingTag).length > 1)
21150                 return false;
21151
21152             var members = parent.members, member;
21153             for (var i = 0; i < members.length; i++) {
21154                 member = members[i];
21155                 if (member.id === entity.id && member.role && member.role !== 'outer')
21156                     return false; // Not outer member
21157                 if (member.id !== entity.id && (!member.role || member.role === 'outer'))
21158                     return false; // Not a simple multipolygon
21159             }
21160
21161             return parent;
21162         }
21163
21164
21165         function osmOldMultipolygonOuterMember(entity, graph) {
21166             if (entity.type !== 'way')
21167                 return false;
21168
21169             var parents = graph.parentRelations(entity);
21170             if (parents.length !== 1)
21171                 return false;
21172
21173             var parent = parents[0];
21174             if (!parent.isMultipolygon() || Object.keys(parent.tags).filter(osmIsInterestingTag).length > 1)
21175                 return false;
21176
21177             var members = parent.members, member, outerMember;
21178             for (var i = 0; i < members.length; i++) {
21179                 member = members[i];
21180                 if (!member.role || member.role === 'outer') {
21181                     if (outerMember)
21182                         return false; // Not a simple multipolygon
21183                     outerMember = member;
21184                 }
21185             }
21186
21187             if (!outerMember)
21188                 return false;
21189
21190             var outerEntity = graph.hasEntity(outerMember.id);
21191             if (!outerEntity || !Object.keys(outerEntity.tags).filter(osmIsInterestingTag).length)
21192                 return false;
21193
21194             return outerEntity;
21195         }
21196
21197
21198         // Join `toJoin` array into sequences of connecting ways.
21199
21200         // Segments which share identical start/end nodes will, as much as possible,
21201         // be connected with each other.
21202         //
21203         // The return value is a nested array. Each constituent array contains elements
21204         // of `toJoin` which have been determined to connect.
21205         //
21206         // Each consitituent array also has a `nodes` property whose value is an
21207         // ordered array of member nodes, with appropriate order reversal and
21208         // start/end coordinate de-duplication.
21209         //
21210         // Members of `toJoin` must have, at minimum, `type` and `id` properties.
21211         // Thus either an array of `osmWay`s or a relation member array may be used.
21212         //
21213         // If an member is an `osmWay`, its tags and childnodes may be reversed via
21214         // `actionReverse` in the output.
21215         //
21216         // The returned sequences array also has an `actions` array property, containing
21217         // any reversal actions that should be applied to the graph, should the calling
21218         // code attempt to actually join the given ways.
21219         //
21220         // Incomplete members (those for which `graph.hasEntity(element.id)` returns
21221         // false) and non-way members are ignored.
21222         //
21223         function osmJoinWays(toJoin, graph) {
21224             function resolve(member) {
21225                 return graph.childNodes(graph.entity(member.id));
21226             }
21227
21228             function reverse(item) {
21229                 var action = actionReverse(item.id, { reverseOneway: true });
21230                 sequences.actions.push(action);
21231                 return (item instanceof osmWay) ? action(graph).entity(item.id) : item;
21232             }
21233
21234             // make a copy containing only the items to join
21235             toJoin = toJoin.filter(function(member) {
21236                 return member.type === 'way' && graph.hasEntity(member.id);
21237             });
21238
21239             // Are the things we are joining relation members or `osmWays`?
21240             // If `osmWays`, skip the "prefer a forward path" code below (see #4872)
21241             var i;
21242             var joinAsMembers = true;
21243             for (i = 0; i < toJoin.length; i++) {
21244                 if (toJoin[i] instanceof osmWay) {
21245                     joinAsMembers = false;
21246                     break;
21247                 }
21248             }
21249
21250             var sequences = [];
21251             sequences.actions = [];
21252
21253             while (toJoin.length) {
21254                 // start a new sequence
21255                 var item = toJoin.shift();
21256                 var currWays = [item];
21257                 var currNodes = resolve(item).slice();
21258                 var doneSequence = false;
21259
21260                 // add to it
21261                 while (toJoin.length && !doneSequence) {
21262                     var start = currNodes[0];
21263                     var end = currNodes[currNodes.length - 1];
21264                     var fn = null;
21265                     var nodes = null;
21266
21267                     // Find the next way/member to join.
21268                     for (i = 0; i < toJoin.length; i++) {
21269                         item = toJoin[i];
21270                         nodes = resolve(item);
21271
21272                         // (for member ordering only, not way ordering - see #4872)
21273                         // Strongly prefer to generate a forward path that preserves the order
21274                         // of the members array. For multipolygons and most relations, member
21275                         // order does not matter - but for routes, it does. (see #4589)
21276                         // If we started this sequence backwards (i.e. next member way attaches to
21277                         // the start node and not the end node), reverse the initial way before continuing.
21278                         if (joinAsMembers && currWays.length === 1 && nodes[0] !== end && nodes[nodes.length - 1] !== end &&
21279                             (nodes[nodes.length - 1] === start || nodes[0] === start)
21280                         ) {
21281                             currWays[0] = reverse(currWays[0]);
21282                             currNodes.reverse();
21283                             start = currNodes[0];
21284                             end = currNodes[currNodes.length - 1];
21285                         }
21286
21287                         if (nodes[0] === end) {
21288                             fn = currNodes.push;               // join to end
21289                             nodes = nodes.slice(1);
21290                             break;
21291                         } else if (nodes[nodes.length - 1] === end) {
21292                             fn = currNodes.push;               // join to end
21293                             nodes = nodes.slice(0, -1).reverse();
21294                             item = reverse(item);
21295                             break;
21296                         } else if (nodes[nodes.length - 1] === start) {
21297                             fn = currNodes.unshift;            // join to beginning
21298                             nodes = nodes.slice(0, -1);
21299                             break;
21300                         } else if (nodes[0] === start) {
21301                             fn = currNodes.unshift;            // join to beginning
21302                             nodes = nodes.slice(1).reverse();
21303                             item = reverse(item);
21304                             break;
21305                         } else {
21306                             fn = nodes = null;
21307                         }
21308                     }
21309
21310                     if (!nodes) {     // couldn't find a joinable way/member
21311                         doneSequence = true;
21312                         break;
21313                     }
21314
21315                     fn.apply(currWays, [item]);
21316                     fn.apply(currNodes, nodes);
21317
21318                     toJoin.splice(i, 1);
21319                 }
21320
21321                 currWays.nodes = currNodes;
21322                 sequences.push(currWays);
21323             }
21324
21325             return sequences;
21326         }
21327
21328         function actionAddMember(relationId, member, memberIndex, insertPair) {
21329
21330             return function action(graph) {
21331                 var relation = graph.entity(relationId);
21332
21333                 // There are some special rules for Public Transport v2 routes.
21334                 var isPTv2 = /stop|platform/.test(member.role);
21335
21336                 if ((isNaN(memberIndex) || insertPair) && member.type === 'way' && !isPTv2) {
21337                     // Try to perform sensible inserts based on how the ways join together
21338                     graph = addWayMember(relation, graph);
21339                 } else {
21340                     // see https://wiki.openstreetmap.org/wiki/Public_transport#Service_routes
21341                     // Stops and Platforms for PTv2 should be ordered first.
21342                     // hack: We do not currently have the ability to place them in the exactly correct order.
21343                     if (isPTv2 && isNaN(memberIndex)) {
21344                         memberIndex = 0;
21345                     }
21346
21347                     graph = graph.replace(relation.addMember(member, memberIndex));
21348                 }
21349
21350                 return graph;
21351             };
21352
21353
21354             // Add a way member into the relation "wherever it makes sense".
21355             // In this situation we were not supplied a memberIndex.
21356             function addWayMember(relation, graph) {
21357                 var groups, tempWay, item, i, j, k;
21358
21359                 // remove PTv2 stops and platforms before doing anything.
21360                 var PTv2members = [];
21361                 var members = [];
21362                 for (i = 0; i < relation.members.length; i++) {
21363                     var m = relation.members[i];
21364                     if (/stop|platform/.test(m.role)) {
21365                         PTv2members.push(m);
21366                     } else {
21367                         members.push(m);
21368                     }
21369                 }
21370                 relation = relation.update({ members: members });
21371
21372
21373                 if (insertPair) {
21374                     // We're adding a member that must stay paired with an existing member.
21375                     // (This feature is used by `actionSplit`)
21376                     //
21377                     // This is tricky because the members may exist multiple times in the
21378                     // member list, and with different A-B/B-A ordering and different roles.
21379                     // (e.g. a bus route that loops out and back - #4589).
21380                     //
21381                     // Replace the existing member with a temporary way,
21382                     // so that `osmJoinWays` can treat the pair like a single way.
21383                     tempWay = osmWay({ id: 'wTemp', nodes: insertPair.nodes });
21384                     graph = graph.replace(tempWay);
21385                     var tempMember = { id: tempWay.id, type: 'way', role: member.role };
21386                     var tempRelation = relation.replaceMember({id: insertPair.originalID}, tempMember, true);
21387                     groups = utilArrayGroupBy(tempRelation.members, 'type');
21388                     groups.way = groups.way || [];
21389
21390                 } else {
21391                     // Add the member anywhere, one time. Just push and let `osmJoinWays` decide where to put it.
21392                     groups = utilArrayGroupBy(relation.members, 'type');
21393                     groups.way = groups.way || [];
21394                     groups.way.push(member);
21395                 }
21396
21397                 members = withIndex(groups.way);
21398                 var joined = osmJoinWays(members, graph);
21399
21400                 // `joined` might not contain all of the way members,
21401                 // But will contain only the completed (downloaded) members
21402                 for (i = 0; i < joined.length; i++) {
21403                     var segment = joined[i];
21404                     var nodes = segment.nodes.slice();
21405                     var startIndex = segment[0].index;
21406
21407                     // j = array index in `members` where this segment starts
21408                     for (j = 0; j < members.length; j++) {
21409                         if (members[j].index === startIndex) {
21410                             break;
21411                         }
21412                     }
21413
21414                     // k = each member in segment
21415                     for (k = 0; k < segment.length; k++) {
21416                         item = segment[k];
21417                         var way = graph.entity(item.id);
21418
21419                         // If this is a paired item, generate members in correct order and role
21420                         if (tempWay && item.id === tempWay.id) {
21421                             if (nodes[0].id === insertPair.nodes[0]) {
21422                                 item.pair = [
21423                                     { id: insertPair.originalID, type: 'way', role: item.role },
21424                                     { id: insertPair.insertedID, type: 'way', role: item.role }
21425                                 ];
21426                             } else {
21427                                 item.pair = [
21428                                     { id: insertPair.insertedID, type: 'way', role: item.role },
21429                                     { id: insertPair.originalID, type: 'way', role: item.role }
21430                                 ];
21431                             }
21432                         }
21433
21434                         // reorder `members` if necessary
21435                         if (k > 0) {
21436                             if (j+k >= members.length || item.index !== members[j+k].index) {
21437                                 moveMember(members, item.index, j+k);
21438                             }
21439                         }
21440
21441                         nodes.splice(0, way.nodes.length - 1);
21442                     }
21443                 }
21444
21445                 if (tempWay) {
21446                     graph = graph.remove(tempWay);
21447                 }
21448
21449                 // Final pass: skip dead items, split pairs, remove index properties
21450                 var wayMembers = [];
21451                 for (i = 0; i < members.length; i++) {
21452                     item = members[i];
21453                     if (item.index === -1) continue;
21454
21455                     if (item.pair) {
21456                         wayMembers.push(item.pair[0]);
21457                         wayMembers.push(item.pair[1]);
21458                     } else {
21459                         wayMembers.push(utilObjectOmit(item, ['index']));
21460                     }
21461                 }
21462
21463                 // Put stops and platforms first, then nodes, ways, relations
21464                 // This is recommended for Public Transport v2 routes:
21465                 // see https://wiki.openstreetmap.org/wiki/Public_transport#Service_routes
21466                 var newMembers = PTv2members.concat( (groups.node || []), wayMembers, (groups.relation || []) );
21467
21468                 return graph.replace(relation.update({ members: newMembers }));
21469
21470
21471                 // `moveMember()` changes the `members` array in place by splicing
21472                 // the item with `.index = findIndex` to where it belongs,
21473                 // and marking the old position as "dead" with `.index = -1`
21474                 //
21475                 // j=5, k=0                jk
21476                 // segment                 5 4 7 6
21477                 // members       0 1 2 3 4 5 6 7 8 9        keep 5 in j+k
21478                 //
21479                 // j=5, k=1                j k
21480                 // segment                 5 4 7 6
21481                 // members       0 1 2 3 4 5 6 7 8 9        move 4 to j+k
21482                 // members       0 1 2 3 x 5 4 6 7 8 9      moved
21483                 //
21484                 // j=5, k=2                j   k
21485                 // segment                 5 4 7 6
21486                 // members       0 1 2 3 x 5 4 6 7 8 9      move 7 to j+k
21487                 // members       0 1 2 3 x 5 4 7 6 x 8 9    moved
21488                 //
21489                 // j=5, k=3                j     k
21490                 // segment                 5 4 7 6
21491                 // members       0 1 2 3 x 5 4 7 6 x 8 9    keep 6 in j+k
21492                 //
21493                 function moveMember(arr, findIndex, toIndex) {
21494                     for (var i = 0; i < arr.length; i++) {
21495                         if (arr[i].index === findIndex) {
21496                             break;
21497                         }
21498                     }
21499
21500                     var item = Object.assign({}, arr[i]);   // shallow copy
21501                     arr[i].index = -1;   // mark as dead
21502                     item.index = toIndex;
21503                     arr.splice(toIndex, 0, item);
21504                 }
21505
21506
21507                 // This is the same as `Relation.indexedMembers`,
21508                 // Except we don't want to index all the members, only the ways
21509                 function withIndex(arr) {
21510                     var result = new Array(arr.length);
21511                     for (var i = 0; i < arr.length; i++) {
21512                         result[i] = Object.assign({}, arr[i]);   // shallow copy
21513                         result[i].index = i;
21514                     }
21515                     return result;
21516                 }
21517             }
21518
21519         }
21520
21521         function actionAddMidpoint(midpoint, node) {
21522             return function(graph) {
21523                 graph = graph.replace(node.move(midpoint.loc));
21524
21525                 var parents = utilArrayIntersection(
21526                     graph.parentWays(graph.entity(midpoint.edge[0])),
21527                     graph.parentWays(graph.entity(midpoint.edge[1]))
21528                 );
21529
21530                 parents.forEach(function(way) {
21531                     for (var i = 0; i < way.nodes.length - 1; i++) {
21532                         if (geoEdgeEqual([way.nodes[i], way.nodes[i + 1]], midpoint.edge)) {
21533                             graph = graph.replace(graph.entity(way.id).addNode(node.id, i + 1));
21534
21535                             // Add only one midpoint on doubled-back segments,
21536                             // turning them into self-intersections.
21537                             return;
21538                         }
21539                     }
21540                 });
21541
21542                 return graph;
21543             };
21544         }
21545
21546         // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/AddNodeToWayAction.as
21547         function actionAddVertex(wayId, nodeId, index) {
21548             return function(graph) {
21549                 return graph.replace(graph.entity(wayId).addNode(nodeId, index));
21550             };
21551         }
21552
21553         function actionChangeMember(relationId, member, memberIndex) {
21554             return function(graph) {
21555                 return graph.replace(graph.entity(relationId).updateMember(member, memberIndex));
21556             };
21557         }
21558
21559         function actionChangePreset(entityID, oldPreset, newPreset, skipFieldDefaults) {
21560             return function action(graph) {
21561                 var entity = graph.entity(entityID);
21562                 var geometry = entity.geometry(graph);
21563                 var tags = entity.tags;
21564
21565                 if (oldPreset) tags = oldPreset.unsetTags(tags, geometry);
21566                 if (newPreset) tags = newPreset.setTags(tags, geometry, skipFieldDefaults);
21567
21568                 return graph.replace(entity.update({tags: tags}));
21569             };
21570         }
21571
21572         function actionChangeTags(entityId, tags) {
21573             return function(graph) {
21574                 var entity = graph.entity(entityId);
21575                 return graph.replace(entity.update({tags: tags}));
21576             };
21577         }
21578
21579         function osmNode() {
21580             if (!(this instanceof osmNode)) {
21581                 return (new osmNode()).initialize(arguments);
21582             } else if (arguments.length) {
21583                 this.initialize(arguments);
21584             }
21585         }
21586
21587         osmEntity.node = osmNode;
21588
21589         osmNode.prototype = Object.create(osmEntity.prototype);
21590
21591         Object.assign(osmNode.prototype, {
21592             type: 'node',
21593             loc: [9999, 9999],
21594
21595             extent: function() {
21596                 return new geoExtent(this.loc);
21597             },
21598
21599
21600             geometry: function(graph) {
21601                 return graph.transient(this, 'geometry', function() {
21602                     return graph.isPoi(this) ? 'point' : 'vertex';
21603                 });
21604             },
21605
21606
21607             move: function(loc) {
21608                 return this.update({loc: loc});
21609             },
21610
21611
21612             isDegenerate: function() {
21613                 return !(
21614                     Array.isArray(this.loc) && this.loc.length === 2 &&
21615                     this.loc[0] >= -180 && this.loc[0] <= 180 &&
21616                     this.loc[1] >= -90 && this.loc[1] <= 90
21617                 );
21618             },
21619
21620
21621             // Inspect tags and geometry to determine which direction(s) this node/vertex points
21622             directions: function(resolver, projection) {
21623                 var val;
21624                 var i;
21625
21626                 // which tag to use?
21627                 if (this.isHighwayIntersection(resolver) && (this.tags.stop || '').toLowerCase() === 'all') {
21628                     // all-way stop tag on a highway intersection
21629                     val = 'all';
21630                 } else {
21631                     // generic direction tag
21632                     val = (this.tags.direction || '').toLowerCase();
21633
21634                     // better suffix-style direction tag
21635                     var re = /:direction$/i;
21636                     var keys = Object.keys(this.tags);
21637                     for (i = 0; i < keys.length; i++) {
21638                         if (re.test(keys[i])) {
21639                             val = this.tags[keys[i]].toLowerCase();
21640                             break;
21641                         }
21642                     }
21643                 }
21644
21645                 if (val === '') return [];
21646
21647                 var cardinal = {
21648                     north: 0,               n: 0,
21649                     northnortheast: 22,     nne: 22,
21650                     northeast: 45,          ne: 45,
21651                     eastnortheast: 67,      ene: 67,
21652                     east: 90,               e: 90,
21653                     eastsoutheast: 112,     ese: 112,
21654                     southeast: 135,         se: 135,
21655                     southsoutheast: 157,    sse: 157,
21656                     south: 180,             s: 180,
21657                     southsouthwest: 202,    ssw: 202,
21658                     southwest: 225,         sw: 225,
21659                     westsouthwest: 247,     wsw: 247,
21660                     west: 270,              w: 270,
21661                     westnorthwest: 292,     wnw: 292,
21662                     northwest: 315,         nw: 315,
21663                     northnorthwest: 337,    nnw: 337
21664                 };
21665
21666
21667                 var values = val.split(';');
21668                 var results = [];
21669
21670                 values.forEach(function(v) {
21671                     // swap cardinal for numeric directions
21672                     if (cardinal[v] !== undefined) {
21673                         v = cardinal[v];
21674                     }
21675
21676                     // numeric direction - just add to results
21677                     if (v !== '' && !isNaN(+v)) {
21678                         results.push(+v);
21679                         return;
21680                     }
21681
21682                     // string direction - inspect parent ways
21683                     var lookBackward =
21684                         (this.tags['traffic_sign:backward'] || v === 'backward' || v === 'both' || v === 'all');
21685                     var lookForward =
21686                         (this.tags['traffic_sign:forward'] || v === 'forward' || v === 'both' || v === 'all');
21687
21688                     if (!lookForward && !lookBackward) return;
21689
21690                     var nodeIds = {};
21691                     resolver.parentWays(this).forEach(function(parent) {
21692                         var nodes = parent.nodes;
21693                         for (i = 0; i < nodes.length; i++) {
21694                             if (nodes[i] === this.id) {  // match current entity
21695                                 if (lookForward && i > 0) {
21696                                     nodeIds[nodes[i - 1]] = true;  // look back to prev node
21697                                 }
21698                                 if (lookBackward && i < nodes.length - 1) {
21699                                     nodeIds[nodes[i + 1]] = true;  // look ahead to next node
21700                                 }
21701                             }
21702                         }
21703                     }, this);
21704
21705                     Object.keys(nodeIds).forEach(function(nodeId) {
21706                         // +90 because geoAngle returns angle from X axis, not Y (north)
21707                         results.push(
21708                             (geoAngle(this, resolver.entity(nodeId), projection) * (180 / Math.PI)) + 90
21709                         );
21710                     }, this);
21711
21712                 }, this);
21713
21714                 return utilArrayUniq(results);
21715             },
21716
21717
21718             isEndpoint: function(resolver) {
21719                 return resolver.transient(this, 'isEndpoint', function() {
21720                     var id = this.id;
21721                     return resolver.parentWays(this).filter(function(parent) {
21722                         return !parent.isClosed() && !!parent.affix(id);
21723                     }).length > 0;
21724                 });
21725             },
21726
21727
21728             isConnected: function(resolver) {
21729                 return resolver.transient(this, 'isConnected', function() {
21730                     var parents = resolver.parentWays(this);
21731
21732                     if (parents.length > 1) {
21733                         // vertex is connected to multiple parent ways
21734                         for (var i in parents) {
21735                             if (parents[i].geometry(resolver) === 'line' &&
21736                                 parents[i].hasInterestingTags()) return true;
21737                         }
21738                     } else if (parents.length === 1) {
21739                         var way = parents[0];
21740                         var nodes = way.nodes.slice();
21741                         if (way.isClosed()) { nodes.pop(); }  // ignore connecting node if closed
21742
21743                         // return true if vertex appears multiple times (way is self intersecting)
21744                         return nodes.indexOf(this.id) !== nodes.lastIndexOf(this.id);
21745                     }
21746
21747                     return false;
21748                 });
21749             },
21750
21751
21752             parentIntersectionWays: function(resolver) {
21753                 return resolver.transient(this, 'parentIntersectionWays', function() {
21754                     return resolver.parentWays(this).filter(function(parent) {
21755                         return (parent.tags.highway ||
21756                             parent.tags.waterway ||
21757                             parent.tags.railway ||
21758                             parent.tags.aeroway) &&
21759                             parent.geometry(resolver) === 'line';
21760                     });
21761                 });
21762             },
21763
21764
21765             isIntersection: function(resolver) {
21766                 return this.parentIntersectionWays(resolver).length > 1;
21767             },
21768
21769
21770             isHighwayIntersection: function(resolver) {
21771                 return resolver.transient(this, 'isHighwayIntersection', function() {
21772                     return resolver.parentWays(this).filter(function(parent) {
21773                         return parent.tags.highway && parent.geometry(resolver) === 'line';
21774                     }).length > 1;
21775                 });
21776             },
21777
21778
21779             isOnAddressLine: function(resolver) {
21780                 return resolver.transient(this, 'isOnAddressLine', function() {
21781                     return resolver.parentWays(this).filter(function(parent) {
21782                         return parent.tags.hasOwnProperty('addr:interpolation') &&
21783                             parent.geometry(resolver) === 'line';
21784                     }).length > 0;
21785                 });
21786             },
21787
21788
21789             asJXON: function(changeset_id) {
21790                 var r = {
21791                     node: {
21792                         '@id': this.osmId(),
21793                         '@lon': this.loc[0],
21794                         '@lat': this.loc[1],
21795                         '@version': (this.version || 0),
21796                         tag: Object.keys(this.tags).map(function(k) {
21797                             return { keyAttributes: { k: k, v: this.tags[k] } };
21798                         }, this)
21799                     }
21800                 };
21801                 if (changeset_id) r.node['@changeset'] = changeset_id;
21802                 return r;
21803             },
21804
21805
21806             asGeoJSON: function() {
21807                 return {
21808                     type: 'Point',
21809                     coordinates: this.loc
21810                 };
21811             }
21812         });
21813
21814         function actionCircularize(wayId, projection, maxAngle) {
21815             maxAngle = (maxAngle || 20) * Math.PI / 180;
21816
21817
21818             var action = function(graph, t) {
21819                 if (t === null || !isFinite(t)) t = 1;
21820                 t = Math.min(Math.max(+t, 0), 1);
21821
21822                 var way = graph.entity(wayId);
21823                 var origNodes = {};
21824
21825                 graph.childNodes(way).forEach(function(node) {
21826                     if (!origNodes[node.id]) origNodes[node.id] = node;
21827                 });
21828
21829                 if (!way.isConvex(graph)) {
21830                     graph = action.makeConvex(graph);
21831                 }
21832
21833                 var nodes = utilArrayUniq(graph.childNodes(way));
21834                 var keyNodes = nodes.filter(function(n) { return graph.parentWays(n).length !== 1; });
21835                 var points = nodes.map(function(n) { return projection(n.loc); });
21836                 var keyPoints = keyNodes.map(function(n) { return projection(n.loc); });
21837                 var centroid = (points.length === 2) ? geoVecInterp(points[0], points[1], 0.5) : d3_polygonCentroid(points);
21838                 var radius = d3_median(points, function(p) { return geoVecLength(centroid, p); });
21839                 var sign = d3_polygonArea(points) > 0 ? 1 : -1;
21840                 var ids, i, j, k;
21841
21842                 // we need atleast two key nodes for the algorithm to work
21843                 if (!keyNodes.length) {
21844                     keyNodes = [nodes[0]];
21845                     keyPoints = [points[0]];
21846                 }
21847
21848                 if (keyNodes.length === 1) {
21849                     var index = nodes.indexOf(keyNodes[0]);
21850                     var oppositeIndex = Math.floor((index + nodes.length / 2) % nodes.length);
21851
21852                     keyNodes.push(nodes[oppositeIndex]);
21853                     keyPoints.push(points[oppositeIndex]);
21854                 }
21855
21856                 // key points and nodes are those connected to the ways,
21857                 // they are projected onto the circle, inbetween nodes are moved
21858                 // to constant intervals between key nodes, extra inbetween nodes are
21859                 // added if necessary.
21860                 for (i = 0; i < keyPoints.length; i++) {
21861                     var nextKeyNodeIndex = (i + 1) % keyNodes.length;
21862                     var startNode = keyNodes[i];
21863                     var endNode = keyNodes[nextKeyNodeIndex];
21864                     var startNodeIndex = nodes.indexOf(startNode);
21865                     var endNodeIndex = nodes.indexOf(endNode);
21866                     var numberNewPoints = -1;
21867                     var indexRange = endNodeIndex - startNodeIndex;
21868                     var nearNodes = {};
21869                     var inBetweenNodes = [];
21870                     var startAngle, endAngle, totalAngle, eachAngle;
21871                     var angle, loc, node, origNode;
21872
21873                     if (indexRange < 0) {
21874                         indexRange += nodes.length;
21875                     }
21876
21877                     // position this key node
21878                     var distance = geoVecLength(centroid, keyPoints[i]) || 1e-4;
21879                     keyPoints[i] = [
21880                         centroid[0] + (keyPoints[i][0] - centroid[0]) / distance * radius,
21881                         centroid[1] + (keyPoints[i][1] - centroid[1]) / distance * radius
21882                     ];
21883                     loc = projection.invert(keyPoints[i]);
21884                     node = keyNodes[i];
21885                     origNode = origNodes[node.id];
21886                     node = node.move(geoVecInterp(origNode.loc, loc, t));
21887                     graph = graph.replace(node);
21888
21889                     // figure out the between delta angle we want to match to
21890                     startAngle = Math.atan2(keyPoints[i][1] - centroid[1], keyPoints[i][0] - centroid[0]);
21891                     endAngle = Math.atan2(keyPoints[nextKeyNodeIndex][1] - centroid[1], keyPoints[nextKeyNodeIndex][0] - centroid[0]);
21892                     totalAngle = endAngle - startAngle;
21893
21894                     // detects looping around -pi/pi
21895                     if (totalAngle * sign > 0) {
21896                         totalAngle = -sign * (2 * Math.PI - Math.abs(totalAngle));
21897                     }
21898
21899                     do {
21900                         numberNewPoints++;
21901                         eachAngle = totalAngle / (indexRange + numberNewPoints);
21902                     } while (Math.abs(eachAngle) > maxAngle);
21903
21904
21905                     // move existing nodes
21906                     for (j = 1; j < indexRange; j++) {
21907                         angle = startAngle + j * eachAngle;
21908                         loc = projection.invert([
21909                             centroid[0] + Math.cos(angle) * radius,
21910                             centroid[1] + Math.sin(angle) * radius
21911                         ]);
21912
21913                         node = nodes[(j + startNodeIndex) % nodes.length];
21914                         origNode = origNodes[node.id];
21915                         nearNodes[node.id] = angle;
21916
21917                         node = node.move(geoVecInterp(origNode.loc, loc, t));
21918                         graph = graph.replace(node);
21919                     }
21920
21921                     // add new inbetween nodes if necessary
21922                     for (j = 0; j < numberNewPoints; j++) {
21923                         angle = startAngle + (indexRange + j) * eachAngle;
21924                         loc = projection.invert([
21925                             centroid[0] + Math.cos(angle) * radius,
21926                             centroid[1] + Math.sin(angle) * radius
21927                         ]);
21928
21929                         // choose a nearnode to use as the original
21930                         var min = Infinity;
21931                         for (var nodeId in nearNodes) {
21932                             var nearAngle = nearNodes[nodeId];
21933                             var dist = Math.abs(nearAngle - angle);
21934                             if (dist < min) {
21935                                 dist = min;
21936                                 origNode = origNodes[nodeId];
21937                             }
21938                         }
21939
21940                         node = osmNode({ loc: geoVecInterp(origNode.loc, loc, t) });
21941                         graph = graph.replace(node);
21942
21943                         nodes.splice(endNodeIndex + j, 0, node);
21944                         inBetweenNodes.push(node.id);
21945                     }
21946
21947                     // Check for other ways that share these keyNodes..
21948                     // If keyNodes are adjacent in both ways,
21949                     // we can add inBetween nodes to that shared way too..
21950                     if (indexRange === 1 && inBetweenNodes.length) {
21951                         var startIndex1 = way.nodes.lastIndexOf(startNode.id);
21952                         var endIndex1 = way.nodes.lastIndexOf(endNode.id);
21953                         var wayDirection1 = (endIndex1 - startIndex1);
21954                         if (wayDirection1 < -1) { wayDirection1 = 1; }
21955
21956                         var parentWays = graph.parentWays(keyNodes[i]);
21957                         for (j = 0; j < parentWays.length; j++) {
21958                             var sharedWay = parentWays[j];
21959                             if (sharedWay === way) continue;
21960
21961                             if (sharedWay.areAdjacent(startNode.id, endNode.id)) {
21962                                 var startIndex2 = sharedWay.nodes.lastIndexOf(startNode.id);
21963                                 var endIndex2 = sharedWay.nodes.lastIndexOf(endNode.id);
21964                                 var wayDirection2 = (endIndex2 - startIndex2);
21965                                 var insertAt = endIndex2;
21966                                 if (wayDirection2 < -1) { wayDirection2 = 1; }
21967
21968                                 if (wayDirection1 !== wayDirection2) {
21969                                     inBetweenNodes.reverse();
21970                                     insertAt = startIndex2;
21971                                 }
21972                                 for (k = 0; k < inBetweenNodes.length; k++) {
21973                                     sharedWay = sharedWay.addNode(inBetweenNodes[k], insertAt + k);
21974                                 }
21975                                 graph = graph.replace(sharedWay);
21976                             }
21977                         }
21978                     }
21979
21980                 }
21981
21982                 // update the way to have all the new nodes
21983                 ids = nodes.map(function(n) { return n.id; });
21984                 ids.push(ids[0]);
21985
21986                 way = way.update({nodes: ids});
21987                 graph = graph.replace(way);
21988
21989                 return graph;
21990             };
21991
21992
21993             action.makeConvex = function(graph) {
21994                 var way = graph.entity(wayId);
21995                 var nodes = utilArrayUniq(graph.childNodes(way));
21996                 var points = nodes.map(function(n) { return projection(n.loc); });
21997                 var sign = d3_polygonArea(points) > 0 ? 1 : -1;
21998                 var hull = d3_polygonHull(points);
21999                 var i, j;
22000
22001                 // D3 convex hulls go counterclockwise..
22002                 if (sign === -1) {
22003                     nodes.reverse();
22004                     points.reverse();
22005                 }
22006
22007                 for (i = 0; i < hull.length - 1; i++) {
22008                     var startIndex = points.indexOf(hull[i]);
22009                     var endIndex = points.indexOf(hull[i+1]);
22010                     var indexRange = (endIndex - startIndex);
22011
22012                     if (indexRange < 0) {
22013                         indexRange += nodes.length;
22014                     }
22015
22016                     // move interior nodes to the surface of the convex hull..
22017                     for (j = 1; j < indexRange; j++) {
22018                         var point = geoVecInterp(hull[i], hull[i+1], j / indexRange);
22019                         var node = nodes[(j + startIndex) % nodes.length].move(projection.invert(point));
22020                         graph = graph.replace(node);
22021                     }
22022                 }
22023                 return graph;
22024             };
22025
22026
22027             action.disabled = function(graph) {
22028                 if (!graph.entity(wayId).isClosed()) {
22029                     return 'not_closed';
22030                 }
22031
22032                 //disable when already circular
22033                 var way = graph.entity(wayId);
22034                 var nodes = utilArrayUniq(graph.childNodes(way));
22035                 var points = nodes.map(function(n) { return projection(n.loc); });
22036                 var hull = d3_polygonHull(points);
22037                 var epsilonAngle =  Math.PI / 180;
22038                 if (hull.length !== points.length || hull.length < 3){
22039                     return false;
22040                 }
22041                 var centroid = d3_polygonCentroid(points);
22042                 var radius = geoVecLengthSquare(centroid, points[0]);
22043
22044                 // compare distances between centroid and points
22045                 for (var i = 0; i<hull.length; i++){
22046                     var actualPoint = hull[i];
22047                     var actualDist = geoVecLengthSquare(actualPoint, centroid);
22048                     var diff = Math.abs(actualDist - radius);
22049                     //compare distances with epsilon-error (5%)
22050                     if (diff > 0.05*radius) {
22051                         return false;
22052                     }
22053                 }
22054                 
22055                 //check if central angles are smaller than maxAngle
22056                 for (i = 0; i<hull.length; i++){
22057                     actualPoint = hull[i];
22058                     var nextPoint = hull[(i+1)%hull.length];
22059                     var startAngle = Math.atan2(actualPoint[1] - centroid[1], actualPoint[0] - centroid[0]);
22060                     var endAngle = Math.atan2(nextPoint[1] - centroid[1], nextPoint[0] - centroid[0]);
22061                     var angle = endAngle - startAngle;
22062                     if (angle < 0) {
22063                         angle = -angle;
22064                     }
22065                     if (angle > Math.PI){
22066                         angle = (2*Math.PI - angle);
22067                     }
22068          
22069                     if (angle > maxAngle + epsilonAngle) {
22070                         return false;
22071                     }
22072                 }
22073                 return 'already_circular';
22074             };
22075
22076
22077             action.transitionable = true;
22078
22079
22080             return action;
22081         }
22082
22083         // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteWayAction.as
22084         function actionDeleteWay(wayID) {
22085
22086             function canDeleteNode(node, graph) {
22087                 // don't delete nodes still attached to ways or relations
22088                 if (graph.parentWays(node).length ||
22089                     graph.parentRelations(node).length) return false;
22090
22091                 var geometries = osmNodeGeometriesForTags(node.tags);
22092                 // don't delete if this node can be a standalone point
22093                 if (geometries.point) return false;
22094                 // delete if this node only be a vertex
22095                 if (geometries.vertex) return true;
22096
22097                 // iD doesn't know if this should be a point or vertex,
22098                 // so only delete if there are no interesting tags
22099                 return !node.hasInterestingTags();
22100             }
22101
22102
22103             var action = function(graph) {
22104                 var way = graph.entity(wayID);
22105
22106                 graph.parentRelations(way).forEach(function(parent) {
22107                     parent = parent.removeMembersWithID(wayID);
22108                     graph = graph.replace(parent);
22109
22110                     if (parent.isDegenerate()) {
22111                         graph = actionDeleteRelation(parent.id)(graph);
22112                     }
22113                 });
22114
22115                 (new Set(way.nodes)).forEach(function(nodeID) {
22116                     graph = graph.replace(way.removeNode(nodeID));
22117
22118                     var node = graph.entity(nodeID);
22119                     if (canDeleteNode(node, graph)) {
22120                         graph = graph.remove(node);
22121                     }
22122                 });
22123
22124                 return graph.remove(way);
22125             };
22126
22127
22128             return action;
22129         }
22130
22131         function actionDeleteMultiple(ids) {
22132             var actions = {
22133                 way: actionDeleteWay,
22134                 node: actionDeleteNode,
22135                 relation: actionDeleteRelation
22136             };
22137
22138
22139             var action = function(graph) {
22140                 ids.forEach(function(id) {
22141                     if (graph.hasEntity(id)) { // It may have been deleted aready.
22142                         graph = actions[graph.entity(id).type](id)(graph);
22143                     }
22144                 });
22145
22146                 return graph;
22147             };
22148
22149
22150             return action;
22151         }
22152
22153         // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteRelationAction.as
22154         function actionDeleteRelation(relationID, allowUntaggedMembers) {
22155
22156             function canDeleteEntity(entity, graph) {
22157                 return !graph.parentWays(entity).length &&
22158                     !graph.parentRelations(entity).length &&
22159                     (!entity.hasInterestingTags() && !allowUntaggedMembers);
22160             }
22161
22162
22163             var action = function(graph) {
22164                 var relation = graph.entity(relationID);
22165
22166                 graph.parentRelations(relation)
22167                     .forEach(function(parent) {
22168                         parent = parent.removeMembersWithID(relationID);
22169                         graph = graph.replace(parent);
22170
22171                         if (parent.isDegenerate()) {
22172                             graph = actionDeleteRelation(parent.id)(graph);
22173                         }
22174                     });
22175
22176                 var memberIDs = utilArrayUniq(relation.members.map(function(m) { return m.id; }));
22177                 memberIDs.forEach(function(memberID) {
22178                     graph = graph.replace(relation.removeMembersWithID(memberID));
22179
22180                     var entity = graph.entity(memberID);
22181                     if (canDeleteEntity(entity, graph)) {
22182                         graph = actionDeleteMultiple([memberID])(graph);
22183                     }
22184                 });
22185
22186                 return graph.remove(relation);
22187             };
22188
22189
22190             return action;
22191         }
22192
22193         // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteNodeAction.as
22194         function actionDeleteNode(nodeId) {
22195             var action = function(graph) {
22196                 var node = graph.entity(nodeId);
22197
22198                 graph.parentWays(node)
22199                     .forEach(function(parent) {
22200                         parent = parent.removeNode(nodeId);
22201                         graph = graph.replace(parent);
22202
22203                         if (parent.isDegenerate()) {
22204                             graph = actionDeleteWay(parent.id)(graph);
22205                         }
22206                     });
22207
22208                 graph.parentRelations(node)
22209                     .forEach(function(parent) {
22210                         parent = parent.removeMembersWithID(nodeId);
22211                         graph = graph.replace(parent);
22212
22213                         if (parent.isDegenerate()) {
22214                             graph = actionDeleteRelation(parent.id)(graph);
22215                         }
22216                     });
22217
22218                 return graph.remove(node);
22219             };
22220
22221
22222             return action;
22223         }
22224
22225         // Connect the ways at the given nodes.
22226         //
22227         // First choose a node to be the survivor, with preference given
22228         // to an existing (not new) node.
22229         //
22230         // Tags and relation memberships of of non-surviving nodes are merged
22231         // to the survivor.
22232         //
22233         // This is the inverse of `iD.actionDisconnect`.
22234         //
22235         // Reference:
22236         //   https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeNodesAction.as
22237         //   https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/MergeNodesAction.java
22238         //
22239         function actionConnect(nodeIDs) {
22240             var action = function(graph) {
22241                 var survivor;
22242                 var node;
22243                 var parents;
22244                 var i, j;
22245
22246                 // Choose a survivor node, prefer an existing (not new) node - #4974
22247                 for (i = 0; i < nodeIDs.length; i++) {
22248                     survivor = graph.entity(nodeIDs[i]);
22249                     if (survivor.version) break;  // found one
22250                 }
22251
22252                 // Replace all non-surviving nodes with the survivor and merge tags.
22253                 for (i = 0; i < nodeIDs.length; i++) {
22254                     node = graph.entity(nodeIDs[i]);
22255                     if (node.id === survivor.id) continue;
22256
22257                     parents = graph.parentWays(node);
22258                     for (j = 0; j < parents.length; j++) {
22259                         graph = graph.replace(parents[j].replaceNode(node.id, survivor.id));
22260                     }
22261
22262                     parents = graph.parentRelations(node);
22263                     for (j = 0; j < parents.length; j++) {
22264                         graph = graph.replace(parents[j].replaceMember(node, survivor));
22265                     }
22266
22267                     survivor = survivor.mergeTags(node.tags);
22268                     graph = actionDeleteNode(node.id)(graph);
22269                 }
22270
22271                 graph = graph.replace(survivor);
22272
22273                 // find and delete any degenerate ways created by connecting adjacent vertices
22274                 parents = graph.parentWays(survivor);
22275                 for (i = 0; i < parents.length; i++) {
22276                     if (parents[i].isDegenerate()) {
22277                         graph = actionDeleteWay(parents[i].id)(graph);
22278                     }
22279                 }
22280
22281                 return graph;
22282             };
22283
22284
22285             action.disabled = function(graph) {
22286                 var seen = {};
22287                 var restrictionIDs = [];
22288                 var survivor;
22289                 var node, way;
22290                 var relations, relation, role;
22291                 var i, j, k;
22292
22293                 // Choose a survivor node, prefer an existing (not new) node - #4974
22294                 for (i = 0; i < nodeIDs.length; i++) {
22295                     survivor = graph.entity(nodeIDs[i]);
22296                     if (survivor.version) break;  // found one
22297                 }
22298
22299                 // 1. disable if the nodes being connected have conflicting relation roles
22300                 for (i = 0; i < nodeIDs.length; i++) {
22301                     node = graph.entity(nodeIDs[i]);
22302                     relations = graph.parentRelations(node);
22303
22304                     for (j = 0; j < relations.length; j++) {
22305                         relation = relations[j];
22306                         role = relation.memberById(node.id).role || '';
22307
22308                         // if this node is a via node in a restriction, remember for later
22309                         if (relation.hasFromViaTo()) {
22310                             restrictionIDs.push(relation.id);
22311                         }
22312
22313                         if (seen[relation.id] !== undefined && seen[relation.id] !== role) {
22314                             return 'relation';
22315                         } else {
22316                             seen[relation.id] = role;
22317                         }
22318                     }
22319                 }
22320
22321                 // gather restrictions for parent ways
22322                 for (i = 0; i < nodeIDs.length; i++) {
22323                     node = graph.entity(nodeIDs[i]);
22324
22325                     var parents = graph.parentWays(node);
22326                     for (j = 0; j < parents.length; j++) {
22327                         var parent = parents[j];
22328                         relations = graph.parentRelations(parent);
22329
22330                         for (k = 0; k < relations.length; k++) {
22331                             relation = relations[k];
22332                             if (relation.hasFromViaTo()) {
22333                                 restrictionIDs.push(relation.id);
22334                             }
22335                         }
22336                     }
22337                 }
22338
22339
22340                 // test restrictions
22341                 restrictionIDs = utilArrayUniq(restrictionIDs);
22342                 for (i = 0; i < restrictionIDs.length; i++) {
22343                     relation = graph.entity(restrictionIDs[i]);
22344                     if (!relation.isComplete(graph)) continue;
22345
22346                     var memberWays = relation.members
22347                         .filter(function(m) { return m.type === 'way'; })
22348                         .map(function(m) { return graph.entity(m.id); });
22349
22350                     memberWays = utilArrayUniq(memberWays);
22351                     var f = relation.memberByRole('from');
22352                     var t = relation.memberByRole('to');
22353                     var isUturn = (f.id === t.id);
22354
22355                     // 2a. disable if connection would damage a restriction
22356                     // (a key node is a node at the junction of ways)
22357                     var nodes = { from: [], via: [], to: [], keyfrom: [], keyto: [] };
22358                     for (j = 0; j < relation.members.length; j++) {
22359                         collectNodes(relation.members[j], nodes);
22360                     }
22361
22362                     nodes.keyfrom = utilArrayUniq(nodes.keyfrom.filter(hasDuplicates));
22363                     nodes.keyto = utilArrayUniq(nodes.keyto.filter(hasDuplicates));
22364
22365                     var filter = keyNodeFilter(nodes.keyfrom, nodes.keyto);
22366                     nodes.from = nodes.from.filter(filter);
22367                     nodes.via = nodes.via.filter(filter);
22368                     nodes.to = nodes.to.filter(filter);
22369
22370                     var connectFrom = false;
22371                     var connectVia = false;
22372                     var connectTo = false;
22373                     var connectKeyFrom = false;
22374                     var connectKeyTo = false;
22375
22376                     for (j = 0; j < nodeIDs.length; j++) {
22377                         var n = nodeIDs[j];
22378                         if (nodes.from.indexOf(n) !== -1)    { connectFrom = true; }
22379                         if (nodes.via.indexOf(n) !== -1)     { connectVia = true; }
22380                         if (nodes.to.indexOf(n) !== -1)      { connectTo = true; }
22381                         if (nodes.keyfrom.indexOf(n) !== -1) { connectKeyFrom = true; }
22382                         if (nodes.keyto.indexOf(n) !== -1)   { connectKeyTo = true; }
22383                     }
22384                     if (connectFrom && connectTo && !isUturn) { return 'restriction'; }
22385                     if (connectFrom && connectVia) { return 'restriction'; }
22386                     if (connectTo   && connectVia) { return 'restriction'; }
22387
22388                     // connecting to a key node -
22389                     // if both nodes are on a member way (i.e. part of the turn restriction),
22390                     // the connecting node must be adjacent to the key node.
22391                     if (connectKeyFrom || connectKeyTo) {
22392                         if (nodeIDs.length !== 2) { return 'restriction'; }
22393
22394                         var n0 = null;
22395                         var n1 = null;
22396                         for (j = 0; j < memberWays.length; j++) {
22397                             way = memberWays[j];
22398                             if (way.contains(nodeIDs[0])) { n0 = nodeIDs[0]; }
22399                             if (way.contains(nodeIDs[1])) { n1 = nodeIDs[1]; }
22400                         }
22401
22402                         if (n0 && n1) {    // both nodes are part of the restriction
22403                             var ok = false;
22404                             for (j = 0; j < memberWays.length; j++) {
22405                                 way = memberWays[j];
22406                                 if (way.areAdjacent(n0, n1)) {
22407                                     ok = true;
22408                                     break;
22409                                 }
22410                             }
22411                             if (!ok) {
22412                                 return 'restriction';
22413                             }
22414                         }
22415                     }
22416
22417                     // 2b. disable if nodes being connected will destroy a member way in a restriction
22418                     // (to test, make a copy and try actually connecting the nodes)
22419                     for (j = 0; j < memberWays.length; j++) {
22420                         way = memberWays[j].update({});   // make copy
22421                         for (k = 0; k < nodeIDs.length; k++) {
22422                             if (nodeIDs[k] === survivor.id) continue;
22423
22424                             if (way.areAdjacent(nodeIDs[k], survivor.id)) {
22425                                 way = way.removeNode(nodeIDs[k]);
22426                             } else {
22427                                 way = way.replaceNode(nodeIDs[k], survivor.id);
22428                             }
22429                         }
22430                         if (way.isDegenerate()) {
22431                             return 'restriction';
22432                         }
22433                     }
22434                 }
22435
22436                 return false;
22437
22438
22439                 // if a key node appears multiple times (indexOf !== lastIndexOf) it's a FROM-VIA or TO-VIA junction
22440                 function hasDuplicates(n, i, arr) {
22441                     return arr.indexOf(n) !== arr.lastIndexOf(n);
22442                 }
22443
22444                 function keyNodeFilter(froms, tos) {
22445                     return function(n) {
22446                         return froms.indexOf(n) === -1 && tos.indexOf(n) === -1;
22447                     };
22448                 }
22449
22450                 function collectNodes(member, collection) {
22451                     var entity = graph.hasEntity(member.id);
22452                     if (!entity) return;
22453
22454                     var role = member.role || '';
22455                     if (!collection[role]) {
22456                         collection[role] = [];
22457                     }
22458
22459                     if (member.type === 'node') {
22460                         collection[role].push(member.id);
22461                         if (role === 'via') {
22462                             collection.keyfrom.push(member.id);
22463                             collection.keyto.push(member.id);
22464                         }
22465
22466                     } else if (member.type === 'way') {
22467                         collection[role].push.apply(collection[role], entity.nodes);
22468                         if (role === 'from' || role === 'via') {
22469                             collection.keyfrom.push(entity.first());
22470                             collection.keyfrom.push(entity.last());
22471                         }
22472                         if (role === 'to' || role === 'via') {
22473                             collection.keyto.push(entity.first());
22474                             collection.keyto.push(entity.last());
22475                         }
22476                     }
22477                 }
22478             };
22479
22480
22481             return action;
22482         }
22483
22484         function actionCopyEntities(ids, fromGraph) {
22485             var _copies = {};
22486
22487
22488             var action = function(graph) {
22489                 ids.forEach(function(id) {
22490                     fromGraph.entity(id).copy(fromGraph, _copies);
22491                 });
22492
22493                 for (var id in _copies) {
22494                     graph = graph.replace(_copies[id]);
22495                 }
22496
22497                 return graph;
22498             };
22499
22500
22501             action.copies = function() {
22502                 return _copies;
22503             };
22504
22505
22506             return action;
22507         }
22508
22509         function actionDeleteMember(relationId, memberIndex) {
22510             return function(graph) {
22511                 var relation = graph.entity(relationId)
22512                     .removeMember(memberIndex);
22513
22514                 graph = graph.replace(relation);
22515
22516                 if (relation.isDegenerate())
22517                     graph = actionDeleteRelation(relation.id)(graph);
22518
22519                 return graph;
22520             };
22521         }
22522
22523         function actionDiscardTags(difference, discardTags) {
22524           discardTags = discardTags || {};
22525
22526           return (graph) => {
22527             difference.modified().forEach(checkTags);
22528             difference.created().forEach(checkTags);
22529             return graph;
22530
22531             function checkTags(entity) {
22532               const keys = Object.keys(entity.tags);
22533               let didDiscard = false;
22534               let tags = {};
22535
22536               for (let i = 0; i < keys.length; i++) {
22537                 const k = keys[i];
22538                 if (discardTags[k] || !entity.tags[k]) {
22539                   didDiscard = true;
22540                 } else {
22541                   tags[k] = entity.tags[k];
22542                 }
22543               }
22544               if (didDiscard) {
22545                 graph = graph.replace(entity.update({ tags: tags }));
22546               }
22547             }
22548
22549           };
22550         }
22551
22552         // Disconect the ways at the given node.
22553         //
22554         // Optionally, disconnect only the given ways.
22555         //
22556         // For testing convenience, accepts an ID to assign to the (first) new node.
22557         // Normally, this will be undefined and the way will automatically
22558         // be assigned a new ID.
22559         //
22560         // This is the inverse of `iD.actionConnect`.
22561         //
22562         // Reference:
22563         //   https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/UnjoinNodeAction.as
22564         //   https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/UnGlueAction.java
22565         //
22566         function actionDisconnect(nodeId, newNodeId) {
22567             var wayIds;
22568
22569
22570             var action = function(graph) {
22571                 var node = graph.entity(nodeId);
22572                 var connections = action.connections(graph);
22573
22574                 connections.forEach(function(connection) {
22575                     var way = graph.entity(connection.wayID);
22576                     var newNode = osmNode({id: newNodeId, loc: node.loc, tags: node.tags});
22577
22578                     graph = graph.replace(newNode);
22579                     if (connection.index === 0 && way.isArea()) {
22580                         // replace shared node with shared node..
22581                         graph = graph.replace(way.replaceNode(way.nodes[0], newNode.id));
22582                     } else if (way.isClosed() && connection.index === way.nodes.length - 1) {
22583                         // replace closing node with new new node..
22584                         graph = graph.replace(way.unclose().addNode(newNode.id));
22585                     } else {
22586                         // replace shared node with multiple new nodes..
22587                         graph = graph.replace(way.updateNode(newNode.id, connection.index));
22588                     }
22589                 });
22590
22591                 return graph;
22592             };
22593
22594
22595             action.connections = function(graph) {
22596                 var candidates = [];
22597                 var keeping = false;
22598                 var parentWays = graph.parentWays(graph.entity(nodeId));
22599                 var way, waynode;
22600                 for (var i = 0; i < parentWays.length; i++) {
22601                     way = parentWays[i];
22602                     if (wayIds && wayIds.indexOf(way.id) === -1) {
22603                         keeping = true;
22604                         continue;
22605                     }
22606                     if (way.isArea() && (way.nodes[0] === nodeId)) {
22607                         candidates.push({ wayID: way.id, index: 0 });
22608                     } else {
22609                         for (var j = 0; j < way.nodes.length; j++) {
22610                             waynode = way.nodes[j];
22611                             if (waynode === nodeId) {
22612                                 if (way.isClosed() &&
22613                                     parentWays.length > 1 &&
22614                                     wayIds &&
22615                                     wayIds.indexOf(way.id) !== -1 &&
22616                                     j === way.nodes.length - 1) {
22617                                     continue;
22618                                 }
22619                                 candidates.push({ wayID: way.id, index: j });
22620                             }
22621                         }
22622                     }
22623                 }
22624
22625                 return keeping ? candidates : candidates.slice(1);
22626             };
22627
22628
22629             action.disabled = function(graph) {
22630                 var connections = action.connections(graph);
22631                 if (connections.length === 0)
22632                     return 'not_connected';
22633
22634                 var parentWays = graph.parentWays(graph.entity(nodeId));
22635                 var seenRelationIds = {};
22636                 var sharedRelation;
22637
22638                 parentWays.forEach(function(way) {
22639                     var relations = graph.parentRelations(way);
22640                     relations.forEach(function(relation) {
22641                         if (relation.id in seenRelationIds) {
22642                             if (wayIds) {
22643                                 if (wayIds.indexOf(way.id) !== -1 ||
22644                                     wayIds.indexOf(seenRelationIds[relation.id]) !== -1) {
22645                                     sharedRelation = relation;
22646                                 }
22647                             } else {
22648                                 sharedRelation = relation;
22649                             }
22650                         } else {
22651                             seenRelationIds[relation.id] = way.id;
22652                         }
22653                     });
22654                 });
22655
22656                 if (sharedRelation)
22657                     return 'relation';
22658             };
22659
22660
22661             action.limitWays = function(val) {
22662                 if (!arguments.length) return wayIds;
22663                 wayIds = val;
22664                 return action;
22665             };
22666
22667
22668             return action;
22669         }
22670
22671         function actionExtract(entityID) {
22672
22673             var extractedNodeID;
22674
22675             var action = function(graph) {
22676                 var entity = graph.entity(entityID);
22677
22678                 if (entity.type === 'node') {
22679                     return extractFromNode(entity, graph);
22680                 }
22681
22682                 return extractFromWayOrRelation(entity, graph);
22683             };
22684
22685             function extractFromNode(node, graph) {
22686
22687                 extractedNodeID = node.id;
22688
22689                 // Create a new node to replace the one we will detach
22690                 var replacement = osmNode({ loc: node.loc });
22691                 graph = graph.replace(replacement);
22692
22693                 // Process each way in turn, updating the graph as we go
22694                 graph = graph.parentWays(node)
22695                     .reduce(function(accGraph, parentWay) {
22696                         return accGraph.replace(parentWay.replaceNode(entityID, replacement.id));
22697                     }, graph);
22698
22699                 // Process any relations too
22700                 return graph.parentRelations(node)
22701                     .reduce(function(accGraph, parentRel) {
22702                         return accGraph.replace(parentRel.replaceMember(node, replacement));
22703                     }, graph);
22704             }
22705
22706             function extractFromWayOrRelation(entity, graph) {
22707
22708                 var fromGeometry = entity.geometry(graph);
22709
22710                 var keysToCopyAndRetain = ['source', 'wheelchair'];
22711                 var keysToRetain = ['area'];
22712                 var buildingKeysToRetain = ['architect', 'building', 'height', 'layer'];
22713
22714                 var extractedLoc = d3_geoCentroid(entity.asGeoJSON(graph));
22715                 if (!extractedLoc  || !isFinite(extractedLoc[0]) || !isFinite(extractedLoc[1])) {
22716                     extractedLoc = entity.extent(graph).center();
22717                 }
22718
22719                 var isBuilding = entity.tags.building && entity.tags.building !== 'no';
22720
22721                 var entityTags = Object.assign({}, entity.tags);  // shallow copy
22722                 var pointTags = {};
22723                 for (var key in entityTags) {
22724
22725                     if (entity.type === 'relation' &&
22726                         key === 'type') {
22727                         continue;
22728                     }
22729
22730                     if (keysToRetain.indexOf(key) !== -1) {
22731                         continue;
22732                     }
22733
22734                     if (isBuilding) {
22735                         // don't transfer building-related tags
22736                         if (buildingKeysToRetain.indexOf(key) !== -1 ||
22737                             key.match(/^building:.{1,}/) ||
22738                             key.match(/^roof:.{1,}/)) continue;
22739                     }
22740
22741                     // copy the tag from the entity to the point
22742                     pointTags[key] = entityTags[key];
22743
22744                     // leave addresses and some other tags so they're on both features
22745                     if (keysToCopyAndRetain.indexOf(key) !== -1 ||
22746                         key.match(/^addr:.{1,}/)) {
22747                         continue;
22748                     }
22749
22750                     // remove the tag from the entity
22751                     delete entityTags[key];
22752                 }
22753
22754                 if (!isBuilding && fromGeometry === 'area') {
22755                     // ensure that areas keep area geometry
22756                     entityTags.area = 'yes';
22757                 }
22758
22759                 var replacement = osmNode({ loc: extractedLoc, tags: pointTags });
22760                 graph = graph.replace(replacement);
22761
22762                 extractedNodeID = replacement.id;
22763
22764                 return graph.replace(entity.update({tags: entityTags}));
22765             }
22766
22767             action.getExtractedNodeID = function() {
22768                 return extractedNodeID;
22769             };
22770
22771             return action;
22772         }
22773
22774         // Join ways at the end node they share.
22775         //
22776         // This is the inverse of `iD.actionSplit`.
22777         //
22778         // Reference:
22779         //   https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeWaysAction.as
22780         //   https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/CombineWayAction.java
22781         //
22782         function actionJoin(ids) {
22783
22784             function groupEntitiesByGeometry(graph) {
22785                 var entities = ids.map(function(id) { return graph.entity(id); });
22786                 return Object.assign(
22787                     { line: [] },
22788                     utilArrayGroupBy(entities, function(entity) { return entity.geometry(graph); })
22789                 );
22790             }
22791
22792
22793             var action = function(graph) {
22794                 var ways = ids.map(graph.entity, graph);
22795                 var survivorID = ways[0].id;
22796
22797                 // if any of the ways are sided (e.g. coastline, cliff, kerb)
22798                 // sort them first so they establish the overall order - #6033
22799                 ways.sort(function(a, b) {
22800                     var aSided = a.isSided();
22801                     var bSided = b.isSided();
22802                     return (aSided && !bSided) ? -1
22803                         : (bSided && !aSided) ? 1
22804                         : 0;
22805                 });
22806
22807                 // Prefer to keep an existing way.
22808                 for (var i = 0; i < ways.length; i++) {
22809                     if (!ways[i].isNew()) {
22810                         survivorID = ways[i].id;
22811                         break;
22812                     }
22813                 }
22814
22815                 var sequences = osmJoinWays(ways, graph);
22816                 var joined = sequences[0];
22817
22818                 // We might need to reverse some of these ways before joining them.  #4688
22819                 // `joined.actions` property will contain any actions we need to apply.
22820                 graph = sequences.actions.reduce(function(g, action) { return action(g); }, graph);
22821
22822                 var survivor = graph.entity(survivorID);
22823                 survivor = survivor.update({ nodes: joined.nodes.map(function(n) { return n.id; }) });
22824                 graph = graph.replace(survivor);
22825
22826                 joined.forEach(function(way) {
22827                     if (way.id === survivorID) return;
22828
22829                     graph.parentRelations(way).forEach(function(parent) {
22830                         graph = graph.replace(parent.replaceMember(way, survivor));
22831                     });
22832
22833                     survivor = survivor.mergeTags(way.tags);
22834
22835                     graph = graph.replace(survivor);
22836                     graph = actionDeleteWay(way.id)(graph);
22837                 });
22838
22839                 // Finds if the join created a single-member multipolygon,
22840                 // and if so turns it into a basic area instead
22841                 function checkForSimpleMultipolygon() {
22842                     if (!survivor.isClosed()) return;
22843
22844                     var multipolygons = graph.parentMultipolygons(survivor).filter(function(multipolygon) {
22845                         // find multipolygons where the survivor is the only member
22846                         return multipolygon.members.length === 1;
22847                     });
22848
22849                     // skip if this is the single member of multiple multipolygons
22850                     if (multipolygons.length !== 1) return;
22851
22852                     var multipolygon = multipolygons[0];
22853
22854                     for (var key in survivor.tags) {
22855                         if (multipolygon.tags[key] &&
22856                             // don't collapse if tags cannot be cleanly merged
22857                             multipolygon.tags[key] !== survivor.tags[key]) return;
22858                     }
22859
22860                     survivor = survivor.mergeTags(multipolygon.tags);
22861                     graph = graph.replace(survivor);
22862                     graph = actionDeleteRelation(multipolygon.id, true /* allow untagged members */)(graph);
22863
22864                     var tags = Object.assign({}, survivor.tags);
22865                     if (survivor.geometry(graph) !== 'area') {
22866                         // ensure the feature persists as an area
22867                         tags.area = 'yes';
22868                     }
22869                     delete tags.type; // remove type=multipolygon
22870                     survivor = survivor.update({ tags: tags });
22871                     graph = graph.replace(survivor);
22872                 }
22873                 checkForSimpleMultipolygon();
22874
22875                 return graph;
22876             };
22877
22878             // Returns the number of nodes the resultant way is expected to have
22879             action.resultingWayNodesLength = function(graph) {
22880                 return ids.reduce(function(count, id) {
22881                     return count + graph.entity(id).nodes.length;
22882                 }, 0) - ids.length - 1;
22883             };
22884
22885
22886             action.disabled = function(graph) {
22887                 var geometries = groupEntitiesByGeometry(graph);
22888                 if (ids.length < 2 || ids.length !== geometries.line.length) {
22889                     return 'not_eligible';
22890                 }
22891
22892                 var joined = osmJoinWays(ids.map(graph.entity, graph), graph);
22893                 if (joined.length > 1) {
22894                     return 'not_adjacent';
22895                 }
22896
22897                 // Loop through all combinations of path-pairs
22898                 // to check potential intersections between all pairs
22899                 for (var i = 0; i < ids.length - 1; i++) {
22900                     for (var j = i + 1; j < ids.length; j++) {
22901                         var path1 = graph.childNodes(graph.entity(ids[i]))
22902                             .map(function(e) { return e.loc; });
22903                         var path2 = graph.childNodes(graph.entity(ids[j]))
22904                             .map(function(e) { return e.loc; });
22905                         var intersections = geoPathIntersections(path1, path2);
22906
22907                         // Check if intersections are just nodes lying on top of
22908                         // each other/the line, as opposed to crossing it
22909                         var common = utilArrayIntersection(
22910                             joined[0].nodes.map(function(n) { return n.loc.toString(); }),
22911                             intersections.map(function(n) { return n.toString(); })
22912                         );
22913                         if (common.length !== intersections.length) {
22914                             return 'paths_intersect';
22915                         }
22916                     }
22917                 }
22918
22919                 var nodeIds = joined[0].nodes.map(function(n) { return n.id; }).slice(1, -1);
22920                 var relation;
22921                 var tags = {};
22922                 var conflicting = false;
22923
22924                 joined[0].forEach(function(way) {
22925                     var parents = graph.parentRelations(way);
22926                     parents.forEach(function(parent) {
22927                         if (parent.isRestriction() && parent.members.some(function(m) { return nodeIds.indexOf(m.id) >= 0; })) {
22928                             relation = parent;
22929                         }
22930                     });
22931
22932                     for (var k in way.tags) {
22933                         if (!(k in tags)) {
22934                             tags[k] = way.tags[k];
22935                         } else if (tags[k] && osmIsInterestingTag(k) && tags[k] !== way.tags[k]) {
22936                             conflicting = true;
22937                         }
22938                     }
22939                 });
22940
22941                 if (relation) {
22942                     return 'restriction';
22943                 }
22944
22945                 if (conflicting) {
22946                     return 'conflicting_tags';
22947                 }
22948             };
22949
22950
22951             return action;
22952         }
22953
22954         function actionMerge(ids) {
22955
22956             function groupEntitiesByGeometry(graph) {
22957                 var entities = ids.map(function(id) { return graph.entity(id); });
22958                 return Object.assign(
22959                     { point: [], area: [], line: [], relation: [] },
22960                     utilArrayGroupBy(entities, function(entity) { return entity.geometry(graph); })
22961                 );
22962             }
22963
22964
22965             var action = function(graph) {
22966                 var geometries = groupEntitiesByGeometry(graph);
22967                 var target = geometries.area[0] || geometries.line[0];
22968                 var points = geometries.point;
22969
22970                 points.forEach(function(point) {
22971                     target = target.mergeTags(point.tags);
22972                     graph = graph.replace(target);
22973
22974                     graph.parentRelations(point).forEach(function(parent) {
22975                         graph = graph.replace(parent.replaceMember(point, target));
22976                     });
22977
22978                     var nodes = utilArrayUniq(graph.childNodes(target));
22979                     var removeNode = point;
22980
22981                     for (var i = 0; i < nodes.length; i++) {
22982                         var node = nodes[i];
22983                         if (graph.parentWays(node).length > 1 ||
22984                             graph.parentRelations(node).length ||
22985                             node.hasInterestingTags()) {
22986                             continue;
22987                         }
22988
22989                         // Found an uninteresting child node on the target way.
22990                         // Move orig point into its place to preserve point's history. #3683
22991                         graph = graph.replace(point.update({ tags: {}, loc: node.loc }));
22992                         target = target.replaceNode(node.id, point.id);
22993                         graph = graph.replace(target);
22994                         removeNode = node;
22995                         break;
22996                     }
22997
22998                     graph = graph.remove(removeNode);
22999                 });
23000
23001                 if (target.tags.area === 'yes') {
23002                     var tags = Object.assign({}, target.tags); // shallow copy
23003                     delete tags.area;
23004                     if (osmTagSuggestingArea(tags)) {
23005                         // remove the `area` tag if area geometry is now implied - #3851
23006                         target = target.update({ tags: tags });
23007                         graph = graph.replace(target);
23008                     }
23009                 }
23010
23011                 return graph;
23012             };
23013
23014
23015             action.disabled = function(graph) {
23016                 var geometries = groupEntitiesByGeometry(graph);
23017                 if (geometries.point.length === 0 ||
23018                     (geometries.area.length + geometries.line.length) !== 1 ||
23019                     geometries.relation.length !== 0) {
23020                     return 'not_eligible';
23021                 }
23022             };
23023
23024
23025             return action;
23026         }
23027
23028         // `actionMergeNodes` is just a combination of:
23029         //
23030         // 1. move all the nodes to a common location
23031         // 2. `actionConnect` them
23032
23033         function actionMergeNodes(nodeIDs, loc) {
23034
23035             // If there is a single "interesting" node, use that as the location.
23036             // Otherwise return the average location of all the nodes.
23037             function chooseLoc(graph) {
23038                 if (!nodeIDs.length) return null;
23039                 var sum = [0,0];
23040                 var interestingCount = 0;
23041                 var interestingLoc;
23042
23043                 for (var i = 0; i < nodeIDs.length; i++) {
23044                     var node = graph.entity(nodeIDs[i]);
23045                     if (node.hasInterestingTags()) {
23046                         interestingLoc = (++interestingCount === 1) ? node.loc : null;
23047                     }
23048                     sum = geoVecAdd(sum, node.loc);
23049                 }
23050
23051                 return interestingLoc || geoVecScale(sum, 1 / nodeIDs.length);
23052             }
23053
23054
23055             var action = function(graph) {
23056                 if (nodeIDs.length < 2) return graph;
23057                 var toLoc = loc;
23058                 if (!toLoc) {
23059                     toLoc = chooseLoc(graph);
23060                 }
23061
23062                 for (var i = 0; i < nodeIDs.length; i++) {
23063                     var node = graph.entity(nodeIDs[i]);
23064                     if (node.loc !== toLoc) {
23065                         graph = graph.replace(node.move(toLoc));
23066                     }
23067                 }
23068
23069                 return actionConnect(nodeIDs)(graph);
23070             };
23071
23072
23073             action.disabled = function(graph) {
23074                 if (nodeIDs.length < 2) return 'not_eligible';
23075
23076                 for (var i = 0; i < nodeIDs.length; i++) {
23077                     var entity = graph.entity(nodeIDs[i]);
23078                     if (entity.type !== 'node') return 'not_eligible';
23079                 }
23080
23081                 return actionConnect(nodeIDs).disabled(graph);
23082             };
23083
23084             return action;
23085         }
23086
23087         function osmChangeset() {
23088             if (!(this instanceof osmChangeset)) {
23089                 return (new osmChangeset()).initialize(arguments);
23090             } else if (arguments.length) {
23091                 this.initialize(arguments);
23092             }
23093         }
23094
23095
23096         osmEntity.changeset = osmChangeset;
23097
23098         osmChangeset.prototype = Object.create(osmEntity.prototype);
23099
23100         Object.assign(osmChangeset.prototype, {
23101
23102             type: 'changeset',
23103
23104
23105             extent: function() {
23106                 return new geoExtent();
23107             },
23108
23109
23110             geometry: function() {
23111                 return 'changeset';
23112             },
23113
23114
23115             asJXON: function() {
23116                 return {
23117                     osm: {
23118                         changeset: {
23119                             tag: Object.keys(this.tags).map(function(k) {
23120                                 return { '@k': k, '@v': this.tags[k] };
23121                             }, this),
23122                             '@version': 0.6,
23123                             '@generator': 'iD'
23124                         }
23125                     }
23126                 };
23127             },
23128
23129
23130             // Generate [osmChange](http://wiki.openstreetmap.org/wiki/OsmChange)
23131             // XML. Returns a string.
23132             osmChangeJXON: function(changes) {
23133                 var changeset_id = this.id;
23134
23135                 function nest(x, order) {
23136                     var groups = {};
23137                     for (var i = 0; i < x.length; i++) {
23138                         var tagName = Object.keys(x[i])[0];
23139                         if (!groups[tagName]) groups[tagName] = [];
23140                         groups[tagName].push(x[i][tagName]);
23141                     }
23142                     var ordered = {};
23143                     order.forEach(function(o) {
23144                         if (groups[o]) ordered[o] = groups[o];
23145                     });
23146                     return ordered;
23147                 }
23148
23149
23150                 // sort relations in a changeset by dependencies
23151                 function sort(changes) {
23152
23153                     // find a referenced relation in the current changeset
23154                     function resolve(item) {
23155                         return relations.find(function(relation) {
23156                             return item.keyAttributes.type === 'relation'
23157                                 && item.keyAttributes.ref === relation['@id'];
23158                         });
23159                     }
23160
23161                     // a new item is an item that has not been already processed
23162                     function isNew(item) {
23163                         return !sorted[ item['@id'] ] && !processing.find(function(proc) {
23164                             return proc['@id'] === item['@id'];
23165                         });
23166                     }
23167
23168                     var processing = [];
23169                     var sorted = {};
23170                     var relations = changes.relation;
23171
23172                     if (!relations) return changes;
23173
23174                     for (var i = 0; i < relations.length; i++) {
23175                         var relation = relations[i];
23176
23177                         // skip relation if already sorted
23178                         if (!sorted[relation['@id']]) {
23179                             processing.push(relation);
23180                         }
23181
23182                         while (processing.length > 0) {
23183                             var next = processing[0],
23184                             deps = next.member.map(resolve).filter(Boolean).filter(isNew);
23185                             if (deps.length === 0) {
23186                                 sorted[next['@id']] = next;
23187                                 processing.shift();
23188                             } else {
23189                                 processing = deps.concat(processing);
23190                             }
23191                         }
23192                     }
23193
23194                     changes.relation = Object.values(sorted);
23195                     return changes;
23196                 }
23197
23198                 function rep(entity) {
23199                     return entity.asJXON(changeset_id);
23200                 }
23201
23202                 return {
23203                     osmChange: {
23204                         '@version': 0.6,
23205                         '@generator': 'iD',
23206                         'create': sort(nest(changes.created.map(rep), ['node', 'way', 'relation'])),
23207                         'modify': nest(changes.modified.map(rep), ['node', 'way', 'relation']),
23208                         'delete': Object.assign(nest(changes.deleted.map(rep), ['relation', 'way', 'node']), { '@if-unused': true })
23209                     }
23210                 };
23211             },
23212
23213
23214             asGeoJSON: function() {
23215                 return {};
23216             }
23217
23218         });
23219
23220         function osmNote() {
23221             if (!(this instanceof osmNote)) {
23222                 return (new osmNote()).initialize(arguments);
23223             } else if (arguments.length) {
23224                 this.initialize(arguments);
23225             }
23226         }
23227
23228
23229         osmNote.id = function() {
23230             return osmNote.id.next--;
23231         };
23232
23233
23234         osmNote.id.next = -1;
23235
23236
23237         Object.assign(osmNote.prototype, {
23238
23239             type: 'note',
23240
23241             initialize: function(sources) {
23242                 for (var i = 0; i < sources.length; ++i) {
23243                     var source = sources[i];
23244                     for (var prop in source) {
23245                         if (Object.prototype.hasOwnProperty.call(source, prop)) {
23246                             if (source[prop] === undefined) {
23247                                 delete this[prop];
23248                             } else {
23249                                 this[prop] = source[prop];
23250                             }
23251                         }
23252                     }
23253                 }
23254
23255                 if (!this.id) {
23256                     this.id = osmNote.id() + '';  // as string
23257                 }
23258
23259                 return this;
23260             },
23261
23262             extent: function() {
23263                 return new geoExtent(this.loc);
23264             },
23265
23266             update: function(attrs) {
23267                 return osmNote(this, attrs); // {v: 1 + (this.v || 0)}
23268             },
23269
23270             isNew: function() {
23271                 return this.id < 0;
23272             },
23273
23274             move: function(loc) {
23275                 return this.update({ loc: loc });
23276             }
23277
23278         });
23279
23280         function osmRelation() {
23281             if (!(this instanceof osmRelation)) {
23282                 return (new osmRelation()).initialize(arguments);
23283             } else if (arguments.length) {
23284                 this.initialize(arguments);
23285             }
23286         }
23287
23288
23289         osmEntity.relation = osmRelation;
23290
23291         osmRelation.prototype = Object.create(osmEntity.prototype);
23292
23293
23294         osmRelation.creationOrder = function(a, b) {
23295             var aId = parseInt(osmEntity.id.toOSM(a.id), 10);
23296             var bId = parseInt(osmEntity.id.toOSM(b.id), 10);
23297
23298             if (aId < 0 || bId < 0) return aId - bId;
23299             return bId - aId;
23300         };
23301
23302
23303         Object.assign(osmRelation.prototype, {
23304             type: 'relation',
23305             members: [],
23306
23307
23308             copy: function(resolver, copies) {
23309                 if (copies[this.id]) return copies[this.id];
23310
23311                 var copy = osmEntity.prototype.copy.call(this, resolver, copies);
23312
23313                 var members = this.members.map(function(member) {
23314                     return Object.assign({}, member, { id: resolver.entity(member.id).copy(resolver, copies).id });
23315                 });
23316
23317                 copy = copy.update({members: members});
23318                 copies[this.id] = copy;
23319
23320                 return copy;
23321             },
23322
23323
23324             extent: function(resolver, memo) {
23325                 return resolver.transient(this, 'extent', function() {
23326                     if (memo && memo[this.id]) return geoExtent();
23327                     memo = memo || {};
23328                     memo[this.id] = true;
23329
23330                     var extent = geoExtent();
23331                     for (var i = 0; i < this.members.length; i++) {
23332                         var member = resolver.hasEntity(this.members[i].id);
23333                         if (member) {
23334                             extent._extend(member.extent(resolver, memo));
23335                         }
23336                     }
23337                     return extent;
23338                 });
23339             },
23340
23341
23342             geometry: function(graph) {
23343                 return graph.transient(this, 'geometry', function() {
23344                     return this.isMultipolygon() ? 'area' : 'relation';
23345                 });
23346             },
23347
23348
23349             isDegenerate: function() {
23350                 return this.members.length === 0;
23351             },
23352
23353
23354             // Return an array of members, each extended with an 'index' property whose value
23355             // is the member index.
23356             indexedMembers: function() {
23357                 var result = new Array(this.members.length);
23358                 for (var i = 0; i < this.members.length; i++) {
23359                     result[i] = Object.assign({}, this.members[i], {index: i});
23360                 }
23361                 return result;
23362             },
23363
23364
23365             // Return the first member with the given role. A copy of the member object
23366             // is returned, extended with an 'index' property whose value is the member index.
23367             memberByRole: function(role) {
23368                 for (var i = 0; i < this.members.length; i++) {
23369                     if (this.members[i].role === role) {
23370                         return Object.assign({}, this.members[i], {index: i});
23371                     }
23372                 }
23373             },
23374
23375             // Same as memberByRole, but returns all members with the given role
23376             membersByRole: function(role) {
23377                 var result = [];
23378                 for (var i = 0; i < this.members.length; i++) {
23379                     if (this.members[i].role === role) {
23380                         result.push(Object.assign({}, this.members[i], {index: i}));
23381                     }
23382                 }
23383                 return result;
23384             },
23385
23386             // Return the first member with the given id. A copy of the member object
23387             // is returned, extended with an 'index' property whose value is the member index.
23388             memberById: function(id) {
23389                 for (var i = 0; i < this.members.length; i++) {
23390                     if (this.members[i].id === id) {
23391                         return Object.assign({}, this.members[i], {index: i});
23392                     }
23393                 }
23394             },
23395
23396
23397             // Return the first member with the given id and role. A copy of the member object
23398             // is returned, extended with an 'index' property whose value is the member index.
23399             memberByIdAndRole: function(id, role) {
23400                 for (var i = 0; i < this.members.length; i++) {
23401                     if (this.members[i].id === id && this.members[i].role === role) {
23402                         return Object.assign({}, this.members[i], {index: i});
23403                     }
23404                 }
23405             },
23406
23407
23408             addMember: function(member, index) {
23409                 var members = this.members.slice();
23410                 members.splice(index === undefined ? members.length : index, 0, member);
23411                 return this.update({members: members});
23412             },
23413
23414
23415             updateMember: function(member, index) {
23416                 var members = this.members.slice();
23417                 members.splice(index, 1, Object.assign({}, members[index], member));
23418                 return this.update({members: members});
23419             },
23420
23421
23422             removeMember: function(index) {
23423                 var members = this.members.slice();
23424                 members.splice(index, 1);
23425                 return this.update({members: members});
23426             },
23427
23428
23429             removeMembersWithID: function(id) {
23430                 var members = this.members.filter(function(m) { return m.id !== id; });
23431                 return this.update({members: members});
23432             },
23433
23434             moveMember: function(fromIndex, toIndex) {
23435                 var members = this.members.slice();
23436                 members.splice(toIndex, 0, members.splice(fromIndex, 1)[0]);
23437                 return this.update({members: members});
23438             },
23439
23440
23441             // Wherever a member appears with id `needle.id`, replace it with a member
23442             // with id `replacement.id`, type `replacement.type`, and the original role,
23443             // By default, adding a duplicate member (by id and role) is prevented.
23444             // Return an updated relation.
23445             replaceMember: function(needle, replacement, keepDuplicates) {
23446                 if (!this.memberById(needle.id)) return this;
23447
23448                 var members = [];
23449
23450                 for (var i = 0; i < this.members.length; i++) {
23451                     var member = this.members[i];
23452                     if (member.id !== needle.id) {
23453                         members.push(member);
23454                     } else if (keepDuplicates || !this.memberByIdAndRole(replacement.id, member.role)) {
23455                         members.push({ id: replacement.id, type: replacement.type, role: member.role });
23456                     }
23457                 }
23458
23459                 return this.update({ members: members });
23460             },
23461
23462
23463             asJXON: function(changeset_id) {
23464                 var r = {
23465                     relation: {
23466                         '@id': this.osmId(),
23467                         '@version': this.version || 0,
23468                         member: this.members.map(function(member) {
23469                             return {
23470                                 keyAttributes: {
23471                                     type: member.type,
23472                                     role: member.role,
23473                                     ref: osmEntity.id.toOSM(member.id)
23474                                 }
23475                             };
23476                         }, this),
23477                         tag: Object.keys(this.tags).map(function(k) {
23478                             return { keyAttributes: { k: k, v: this.tags[k] } };
23479                         }, this)
23480                     }
23481                 };
23482                 if (changeset_id) {
23483                     r.relation['@changeset'] = changeset_id;
23484                 }
23485                 return r;
23486             },
23487
23488
23489             asGeoJSON: function(resolver) {
23490                 return resolver.transient(this, 'GeoJSON', function () {
23491                     if (this.isMultipolygon()) {
23492                         return {
23493                             type: 'MultiPolygon',
23494                             coordinates: this.multipolygon(resolver)
23495                         };
23496                     } else {
23497                         return {
23498                             type: 'FeatureCollection',
23499                             properties: this.tags,
23500                             features: this.members.map(function (member) {
23501                                 return Object.assign({role: member.role}, resolver.entity(member.id).asGeoJSON(resolver));
23502                             })
23503                         };
23504                     }
23505                 });
23506             },
23507
23508
23509             area: function(resolver) {
23510                 return resolver.transient(this, 'area', function() {
23511                     return d3_geoArea(this.asGeoJSON(resolver));
23512                 });
23513             },
23514
23515
23516             isMultipolygon: function() {
23517                 return this.tags.type === 'multipolygon';
23518             },
23519
23520
23521             isComplete: function(resolver) {
23522                 for (var i = 0; i < this.members.length; i++) {
23523                     if (!resolver.hasEntity(this.members[i].id)) {
23524                         return false;
23525                     }
23526                 }
23527                 return true;
23528             },
23529
23530
23531             hasFromViaTo: function() {
23532                 return (
23533                     this.members.some(function(m) { return m.role === 'from'; }) &&
23534                     this.members.some(function(m) { return m.role === 'via'; }) &&
23535                     this.members.some(function(m) { return m.role === 'to'; })
23536                 );
23537             },
23538
23539
23540             isRestriction: function() {
23541                 return !!(this.tags.type && this.tags.type.match(/^restriction:?/));
23542             },
23543
23544
23545             isValidRestriction: function() {
23546                 if (!this.isRestriction()) return false;
23547
23548                 var froms = this.members.filter(function(m) { return m.role === 'from'; });
23549                 var vias = this.members.filter(function(m) { return m.role === 'via'; });
23550                 var tos = this.members.filter(function(m) { return m.role === 'to'; });
23551
23552                 if (froms.length !== 1 && this.tags.restriction !== 'no_entry') return false;
23553                 if (froms.some(function(m) { return m.type !== 'way'; })) return false;
23554
23555                 if (tos.length !== 1 && this.tags.restriction !== 'no_exit') return false;
23556                 if (tos.some(function(m) { return m.type !== 'way'; })) return false;
23557
23558                 if (vias.length === 0) return false;
23559                 if (vias.length > 1 && vias.some(function(m) { return m.type !== 'way'; })) return false;
23560
23561                 return true;
23562             },
23563
23564
23565             // Returns an array [A0, ... An], each Ai being an array of node arrays [Nds0, ... Ndsm],
23566             // where Nds0 is an outer ring and subsequent Ndsi's (if any i > 0) being inner rings.
23567             //
23568             // This corresponds to the structure needed for rendering a multipolygon path using a
23569             // `evenodd` fill rule, as well as the structure of a GeoJSON MultiPolygon geometry.
23570             //
23571             // In the case of invalid geometries, this function will still return a result which
23572             // includes the nodes of all way members, but some Nds may be unclosed and some inner
23573             // rings not matched with the intended outer ring.
23574             //
23575             multipolygon: function(resolver) {
23576                 var outers = this.members.filter(function(m) { return 'outer' === (m.role || 'outer'); });
23577                 var inners = this.members.filter(function(m) { return 'inner' === m.role; });
23578
23579                 outers = osmJoinWays(outers, resolver);
23580                 inners = osmJoinWays(inners, resolver);
23581
23582                 var sequenceToLineString = function(sequence) {
23583                     if (sequence.nodes.length > 2 &&
23584                         sequence.nodes[0] !== sequence.nodes[sequence.nodes.length - 1]) {
23585                         // close unclosed parts to ensure correct area rendering - #2945
23586                         sequence.nodes.push(sequence.nodes[0]);
23587                     }
23588                     return sequence.nodes.map(function(node) { return node.loc; });
23589                 };
23590
23591                 outers = outers.map(sequenceToLineString);
23592                 inners = inners.map(sequenceToLineString);
23593
23594                 var result = outers.map(function(o) {
23595                     // Heuristic for detecting counterclockwise winding order. Assumes
23596                     // that OpenStreetMap polygons are not hemisphere-spanning.
23597                     return [d3_geoArea({ type: 'Polygon', coordinates: [o] }) > 2 * Math.PI ? o.reverse() : o];
23598                 });
23599
23600                 function findOuter(inner) {
23601                     var o, outer;
23602
23603                     for (o = 0; o < outers.length; o++) {
23604                         outer = outers[o];
23605                         if (geoPolygonContainsPolygon(outer, inner))
23606                             return o;
23607                     }
23608
23609                     for (o = 0; o < outers.length; o++) {
23610                         outer = outers[o];
23611                         if (geoPolygonIntersectsPolygon(outer, inner, false))
23612                             return o;
23613                     }
23614                 }
23615
23616                 for (var i = 0; i < inners.length; i++) {
23617                     var inner = inners[i];
23618
23619                     if (d3_geoArea({ type: 'Polygon', coordinates: [inner] }) < 2 * Math.PI) {
23620                         inner = inner.reverse();
23621                     }
23622
23623                     var o = findOuter(inners[i]);
23624                     if (o !== undefined) {
23625                         result[o].push(inners[i]);
23626                     } else {
23627                         result.push([inners[i]]); // Invalid geometry
23628                     }
23629                 }
23630
23631                 return result;
23632             }
23633         });
23634
23635         class QAItem {
23636           constructor(loc, service, itemType, id, props) {
23637             // Store required properties
23638             this.loc = loc;
23639             this.service = service.title;
23640             this.itemType = itemType;
23641
23642             // All issues must have an ID for selection, use generic if none specified
23643             this.id = id ? id : `${QAItem.id()}`;
23644
23645             this.update(props);
23646
23647             // Some QA services have marker icons to differentiate issues
23648             if (service && typeof service.getIcon === 'function') {
23649               this.icon = service.getIcon(itemType);
23650             }
23651
23652             return this;
23653           }
23654
23655           update(props) {
23656             // You can't override this inital information
23657             const { loc, service, itemType, id } = this;
23658
23659             Object.keys(props).forEach(prop => this[prop] = props[prop]);
23660
23661             this.loc = loc;
23662             this.service = service;
23663             this.itemType = itemType;
23664             this.id = id;
23665
23666             return this;
23667           }
23668
23669           // Generic handling for newly created QAItems
23670           static id() {
23671             return this.nextId--;
23672           }
23673         }
23674         QAItem.nextId = -1;
23675
23676         // Split a way at the given node.
23677         //
23678         // Optionally, split only the given ways, if multiple ways share
23679         // the given node.
23680         //
23681         // This is the inverse of `iD.actionJoin`.
23682         //
23683         // For testing convenience, accepts an ID to assign to the new way.
23684         // Normally, this will be undefined and the way will automatically
23685         // be assigned a new ID.
23686         //
23687         // Reference:
23688         //   https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/SplitWayAction.as
23689         //
23690         function actionSplit(nodeId, newWayIds) {
23691             var _wayIDs;
23692
23693             // The IDs of the ways actually created by running this action
23694             var createdWayIDs = [];
23695
23696             // If the way is closed, we need to search for a partner node
23697             // to split the way at.
23698             //
23699             // The following looks for a node that is both far away from
23700             // the initial node in terms of way segment length and nearby
23701             // in terms of beeline-distance. This assures that areas get
23702             // split on the most "natural" points (independent of the number
23703             // of nodes).
23704             // For example: bone-shaped areas get split across their waist
23705             // line, circles across the diameter.
23706             function splitArea(nodes, idxA, graph) {
23707                 var lengths = new Array(nodes.length);
23708                 var length;
23709                 var i;
23710                 var best = 0;
23711                 var idxB;
23712
23713                 function wrap(index) {
23714                     return utilWrap(index, nodes.length);
23715                 }
23716
23717                 function dist(nA, nB) {
23718                     var locA = graph.entity(nA).loc;
23719                     var locB = graph.entity(nB).loc;
23720                     var epsilon = 1e-6;
23721                     return (locA && locB) ? geoSphericalDistance(locA, locB) : epsilon;
23722                 }
23723
23724                 // calculate lengths
23725                 length = 0;
23726                 for (i = wrap(idxA + 1); i !== idxA; i = wrap(i + 1)) {
23727                     length += dist(nodes[i], nodes[wrap(i - 1)]);
23728                     lengths[i] = length;
23729                 }
23730
23731                 length = 0;
23732                 for (i = wrap(idxA - 1); i !== idxA; i = wrap(i - 1)) {
23733                     length += dist(nodes[i], nodes[wrap(i + 1)]);
23734                     if (length < lengths[i]) {
23735                         lengths[i] = length;
23736                     }
23737                 }
23738
23739                 // determine best opposite node to split
23740                 for (i = 0; i < nodes.length; i++) {
23741                     var cost = lengths[i] / dist(nodes[idxA], nodes[i]);
23742                     if (cost > best) {
23743                         idxB = i;
23744                         best = cost;
23745                     }
23746                 }
23747
23748                 return idxB;
23749             }
23750
23751
23752             function split(graph, wayA, newWayId) {
23753                 var wayB = osmWay({ id: newWayId, tags: wayA.tags });   // `wayB` is the NEW way
23754                 var origNodes = wayA.nodes.slice();
23755                 var nodesA;
23756                 var nodesB;
23757                 var isArea = wayA.isArea();
23758                 var isOuter = osmIsOldMultipolygonOuterMember(wayA, graph);
23759
23760                 if (wayA.isClosed()) {
23761                     var nodes = wayA.nodes.slice(0, -1);
23762                     var idxA = nodes.indexOf(nodeId);
23763                     var idxB = splitArea(nodes, idxA, graph);
23764
23765                     if (idxB < idxA) {
23766                         nodesA = nodes.slice(idxA).concat(nodes.slice(0, idxB + 1));
23767                         nodesB = nodes.slice(idxB, idxA + 1);
23768                     } else {
23769                         nodesA = nodes.slice(idxA, idxB + 1);
23770                         nodesB = nodes.slice(idxB).concat(nodes.slice(0, idxA + 1));
23771                     }
23772                 } else {
23773                     var idx = wayA.nodes.indexOf(nodeId, 1);
23774                     nodesA = wayA.nodes.slice(0, idx + 1);
23775                     nodesB = wayA.nodes.slice(idx);
23776                 }
23777
23778                 wayA = wayA.update({ nodes: nodesA });
23779                 wayB = wayB.update({ nodes: nodesB });
23780
23781                 graph = graph.replace(wayA);
23782                 graph = graph.replace(wayB);
23783
23784                 graph.parentRelations(wayA).forEach(function(relation) {
23785                     var member;
23786
23787                     // Turn restrictions - make sure:
23788                     // 1. Splitting a FROM/TO way - only `wayA` OR `wayB` remains in relation
23789                     //    (whichever one is connected to the VIA node/ways)
23790                     // 2. Splitting a VIA way - `wayB` remains in relation as a VIA way
23791                     if (relation.hasFromViaTo()) {
23792                         var f = relation.memberByRole('from');
23793                         var v = relation.membersByRole('via');
23794                         var t = relation.memberByRole('to');
23795                         var i;
23796
23797                         // 1. split a FROM/TO
23798                         if (f.id === wayA.id || t.id === wayA.id) {
23799                             var keepB = false;
23800                             if (v.length === 1 && v[0].type === 'node') {   // check via node
23801                                 keepB = wayB.contains(v[0].id);
23802                             } else {                                        // check via way(s)
23803                                 for (i = 0; i < v.length; i++) {
23804                                     if (v[i].type === 'way') {
23805                                         var wayVia = graph.hasEntity(v[i].id);
23806                                         if (wayVia && utilArrayIntersection(wayB.nodes, wayVia.nodes).length) {
23807                                             keepB = true;
23808                                             break;
23809                                         }
23810                                     }
23811                                 }
23812                             }
23813
23814                             if (keepB) {
23815                                 relation = relation.replaceMember(wayA, wayB);
23816                                 graph = graph.replace(relation);
23817                             }
23818
23819                         // 2. split a VIA
23820                         } else {
23821                             for (i = 0; i < v.length; i++) {
23822                                 if (v[i].type === 'way' && v[i].id === wayA.id) {
23823                                     member = {
23824                                         id: wayB.id,
23825                                         type: 'way',
23826                                         role: 'via'
23827                                     };
23828                                     graph = actionAddMember(relation.id, member, v[i].index + 1)(graph);
23829                                     break;
23830                                 }
23831                             }
23832                         }
23833
23834                     // All other relations (Routes, Multipolygons, etc):
23835                     // 1. Both `wayA` and `wayB` remain in the relation
23836                     // 2. But must be inserted as a pair (see `actionAddMember` for details)
23837                     } else {
23838                         if (relation === isOuter) {
23839                             graph = graph.replace(relation.mergeTags(wayA.tags));
23840                             graph = graph.replace(wayA.update({ tags: {} }));
23841                             graph = graph.replace(wayB.update({ tags: {} }));
23842                         }
23843
23844                         member = {
23845                             id: wayB.id,
23846                             type: 'way',
23847                             role: relation.memberById(wayA.id).role
23848                         };
23849
23850                         var insertPair = {
23851                             originalID: wayA.id,
23852                             insertedID: wayB.id,
23853                             nodes: origNodes
23854                         };
23855
23856                         graph = actionAddMember(relation.id, member, undefined, insertPair)(graph);
23857                     }
23858                 });
23859
23860                 if (!isOuter && isArea) {
23861                     var multipolygon = osmRelation({
23862                         tags: Object.assign({}, wayA.tags, { type: 'multipolygon' }),
23863                         members: [
23864                             { id: wayA.id, role: 'outer', type: 'way' },
23865                             { id: wayB.id, role: 'outer', type: 'way' }
23866                         ]
23867                     });
23868
23869                     graph = graph.replace(multipolygon);
23870                     graph = graph.replace(wayA.update({ tags: {} }));
23871                     graph = graph.replace(wayB.update({ tags: {} }));
23872                 }
23873
23874                 createdWayIDs.push(wayB.id);
23875
23876                 return graph;
23877             }
23878
23879             var action = function(graph) {
23880                 var candidates = action.ways(graph);
23881                 createdWayIDs = [];
23882                 for (var i = 0; i < candidates.length; i++) {
23883                     graph = split(graph, candidates[i], newWayIds && newWayIds[i]);
23884                 }
23885                 return graph;
23886             };
23887
23888             action.getCreatedWayIDs = function() {
23889                 return createdWayIDs;
23890             };
23891
23892             action.ways = function(graph) {
23893                 var node = graph.entity(nodeId);
23894                 var parents = graph.parentWays(node);
23895                 var hasLines = parents.some(function(parent) {
23896                     return parent.geometry(graph) === 'line';
23897                 });
23898
23899                 return parents.filter(function(parent) {
23900                     if (_wayIDs && _wayIDs.indexOf(parent.id) === -1)
23901                         return false;
23902
23903                     if (!_wayIDs && hasLines && parent.geometry(graph) !== 'line')
23904                         return false;
23905
23906                     if (parent.isClosed()) {
23907                         return true;
23908                     }
23909
23910                     for (var i = 1; i < parent.nodes.length - 1; i++) {
23911                         if (parent.nodes[i] === nodeId) {
23912                             return true;
23913                         }
23914                     }
23915
23916                     return false;
23917                 });
23918             };
23919
23920
23921             action.disabled = function(graph) {
23922                 var candidates = action.ways(graph);
23923                 if (candidates.length === 0 || (_wayIDs && _wayIDs.length !== candidates.length)) {
23924                     return 'not_eligible';
23925                 }
23926             };
23927
23928
23929             action.limitWays = function(val) {
23930                 if (!arguments.length) return _wayIDs;
23931                 _wayIDs = val;
23932                 return action;
23933             };
23934
23935
23936             return action;
23937         }
23938
23939         function coreGraph(other, mutable) {
23940             if (!(this instanceof coreGraph)) return new coreGraph(other, mutable);
23941
23942             if (other instanceof coreGraph) {
23943                 var base = other.base();
23944                 this.entities = Object.assign(Object.create(base.entities), other.entities);
23945                 this._parentWays = Object.assign(Object.create(base.parentWays), other._parentWays);
23946                 this._parentRels = Object.assign(Object.create(base.parentRels), other._parentRels);
23947
23948             } else {
23949                 this.entities = Object.create({});
23950                 this._parentWays = Object.create({});
23951                 this._parentRels = Object.create({});
23952                 this.rebase(other || [], [this]);
23953             }
23954
23955             this.transients = {};
23956             this._childNodes = {};
23957             this.frozen = !mutable;
23958         }
23959
23960
23961         coreGraph.prototype = {
23962
23963             hasEntity: function(id) {
23964                 return this.entities[id];
23965             },
23966
23967
23968             entity: function(id) {
23969                 var entity = this.entities[id];
23970
23971                 //https://github.com/openstreetmap/iD/issues/3973#issuecomment-307052376
23972                 if (!entity) {
23973                     entity = this.entities.__proto__[id];  // eslint-disable-line no-proto
23974                 }
23975
23976                 if (!entity) {
23977                     throw new Error('entity ' + id + ' not found');
23978                 }
23979                 return entity;
23980             },
23981
23982
23983             geometry: function(id) {
23984                 return this.entity(id).geometry(this);
23985             },
23986
23987
23988             transient: function(entity, key, fn) {
23989                 var id = entity.id;
23990                 var transients = this.transients[id] || (this.transients[id] = {});
23991
23992                 if (transients[key] !== undefined) {
23993                     return transients[key];
23994                 }
23995
23996                 transients[key] = fn.call(entity);
23997
23998                 return transients[key];
23999             },
24000
24001
24002             parentWays: function(entity) {
24003                 var parents = this._parentWays[entity.id];
24004                 var result = [];
24005                 if (parents) {
24006                     parents.forEach(function(id) {
24007                         result.push(this.entity(id));
24008                     }, this);
24009                 }
24010                 return result;
24011             },
24012
24013
24014             isPoi: function(entity) {
24015                 var parents = this._parentWays[entity.id];
24016                 return !parents || parents.size === 0;
24017             },
24018
24019
24020             isShared: function(entity) {
24021                 var parents = this._parentWays[entity.id];
24022                 return parents && parents.size > 1;
24023             },
24024
24025
24026             parentRelations: function(entity) {
24027                 var parents = this._parentRels[entity.id];
24028                 var result = [];
24029                 if (parents) {
24030                     parents.forEach(function(id) {
24031                         result.push(this.entity(id));
24032                     }, this);
24033                 }
24034                 return result;
24035             },
24036
24037             parentMultipolygons: function(entity) {
24038                 return this.parentRelations(entity).filter(function(relation) {
24039                     return relation.isMultipolygon();
24040                 });
24041             },
24042
24043
24044             childNodes: function(entity) {
24045                 if (this._childNodes[entity.id]) return this._childNodes[entity.id];
24046                 if (!entity.nodes) return [];
24047
24048                 var nodes = [];
24049                 for (var i = 0; i < entity.nodes.length; i++) {
24050                     nodes[i] = this.entity(entity.nodes[i]);
24051                 }
24052
24053                 this._childNodes[entity.id] = nodes;
24054                 return this._childNodes[entity.id];
24055             },
24056
24057
24058             base: function() {
24059                 return {
24060                     'entities': Object.getPrototypeOf(this.entities),
24061                     'parentWays': Object.getPrototypeOf(this._parentWays),
24062                     'parentRels': Object.getPrototypeOf(this._parentRels)
24063                 };
24064             },
24065
24066
24067             // Unlike other graph methods, rebase mutates in place. This is because it
24068             // is used only during the history operation that merges newly downloaded
24069             // data into each state. To external consumers, it should appear as if the
24070             // graph always contained the newly downloaded data.
24071             rebase: function(entities, stack, force) {
24072                 var base = this.base();
24073                 var i, j, k, id;
24074
24075                 for (i = 0; i < entities.length; i++) {
24076                     var entity = entities[i];
24077
24078                     if (!entity.visible || (!force && base.entities[entity.id]))
24079                         continue;
24080
24081                     // Merging data into the base graph
24082                     base.entities[entity.id] = entity;
24083                     this._updateCalculated(undefined, entity, base.parentWays, base.parentRels);
24084
24085                     // Restore provisionally-deleted nodes that are discovered to have an extant parent
24086                     if (entity.type === 'way') {
24087                         for (j = 0; j < entity.nodes.length; j++) {
24088                             id = entity.nodes[j];
24089                             for (k = 1; k < stack.length; k++) {
24090                                 var ents = stack[k].entities;
24091                                 if (ents.hasOwnProperty(id) && ents[id] === undefined) {
24092                                     delete ents[id];
24093                                 }
24094                             }
24095                         }
24096                     }
24097                 }
24098
24099                 for (i = 0; i < stack.length; i++) {
24100                     stack[i]._updateRebased();
24101                 }
24102             },
24103
24104
24105             _updateRebased: function() {
24106                 var base = this.base();
24107
24108                 Object.keys(this._parentWays).forEach(function(child) {
24109                     if (base.parentWays[child]) {
24110                         base.parentWays[child].forEach(function(id) {
24111                             if (!this.entities.hasOwnProperty(id)) {
24112                                 this._parentWays[child].add(id);
24113                             }
24114                         }, this);
24115                     }
24116                 }, this);
24117
24118                 Object.keys(this._parentRels).forEach(function(child) {
24119                     if (base.parentRels[child]) {
24120                         base.parentRels[child].forEach(function(id) {
24121                             if (!this.entities.hasOwnProperty(id)) {
24122                                 this._parentRels[child].add(id);
24123                             }
24124                         }, this);
24125                     }
24126                 }, this);
24127
24128                 this.transients = {};
24129
24130                 // this._childNodes is not updated, under the assumption that
24131                 // ways are always downloaded with their child nodes.
24132             },
24133
24134
24135             // Updates calculated properties (parentWays, parentRels) for the specified change
24136             _updateCalculated: function(oldentity, entity, parentWays, parentRels) {
24137                 parentWays = parentWays || this._parentWays;
24138                 parentRels = parentRels || this._parentRels;
24139
24140                 var type = entity && entity.type || oldentity && oldentity.type;
24141                 var removed, added, i;
24142
24143                 if (type === 'way') {   // Update parentWays
24144                     if (oldentity && entity) {
24145                         removed = utilArrayDifference(oldentity.nodes, entity.nodes);
24146                         added = utilArrayDifference(entity.nodes, oldentity.nodes);
24147                     } else if (oldentity) {
24148                         removed = oldentity.nodes;
24149                         added = [];
24150                     } else if (entity) {
24151                         removed = [];
24152                         added = entity.nodes;
24153                     }
24154                     for (i = 0; i < removed.length; i++) {
24155                         // make a copy of prototype property, store as own property, and update..
24156                         parentWays[removed[i]] = new Set(parentWays[removed[i]]);
24157                         parentWays[removed[i]].delete(oldentity.id);
24158                     }
24159                     for (i = 0; i < added.length; i++) {
24160                         // make a copy of prototype property, store as own property, and update..
24161                         parentWays[added[i]] = new Set(parentWays[added[i]]);
24162                         parentWays[added[i]].add(entity.id);
24163                     }
24164
24165                 } else if (type === 'relation') {   // Update parentRels
24166
24167                     // diff only on the IDs since the same entity can be a member multiple times with different roles
24168                     var oldentityMemberIDs = oldentity ? oldentity.members.map(function(m) { return m.id; }) : [];
24169                     var entityMemberIDs = entity ? entity.members.map(function(m) { return m.id; }) : [];
24170
24171                     if (oldentity && entity) {
24172                         removed = utilArrayDifference(oldentityMemberIDs, entityMemberIDs);
24173                         added = utilArrayDifference(entityMemberIDs, oldentityMemberIDs);
24174                     } else if (oldentity) {
24175                         removed = oldentityMemberIDs;
24176                         added = [];
24177                     } else if (entity) {
24178                         removed = [];
24179                         added = entityMemberIDs;
24180                     }
24181                     for (i = 0; i < removed.length; i++) {
24182                         // make a copy of prototype property, store as own property, and update..
24183                         parentRels[removed[i]] = new Set(parentRels[removed[i]]);
24184                         parentRels[removed[i]].delete(oldentity.id);
24185                     }
24186                     for (i = 0; i < added.length; i++) {
24187                         // make a copy of prototype property, store as own property, and update..
24188                         parentRels[added[i]] = new Set(parentRels[added[i]]);
24189                         parentRels[added[i]].add(entity.id);
24190                     }
24191                 }
24192             },
24193
24194
24195             replace: function(entity) {
24196                 if (this.entities[entity.id] === entity) return this;
24197
24198                 return this.update(function() {
24199                     this._updateCalculated(this.entities[entity.id], entity);
24200                     this.entities[entity.id] = entity;
24201                 });
24202             },
24203
24204
24205             remove: function(entity) {
24206                 return this.update(function() {
24207                     this._updateCalculated(entity, undefined);
24208                     this.entities[entity.id] = undefined;
24209                 });
24210             },
24211
24212
24213             revert: function(id) {
24214                 var baseEntity = this.base().entities[id];
24215                 var headEntity = this.entities[id];
24216                 if (headEntity === baseEntity) return this;
24217
24218                 return this.update(function() {
24219                     this._updateCalculated(headEntity, baseEntity);
24220                     delete this.entities[id];
24221                 });
24222             },
24223
24224
24225             update: function() {
24226                 var graph = this.frozen ? coreGraph(this, true) : this;
24227                 for (var i = 0; i < arguments.length; i++) {
24228                     arguments[i].call(graph, graph);
24229                 }
24230
24231                 if (this.frozen) graph.frozen = true;
24232
24233                 return graph;
24234             },
24235
24236
24237             // Obliterates any existing entities
24238             load: function(entities) {
24239                 var base = this.base();
24240                 this.entities = Object.create(base.entities);
24241
24242                 for (var i in entities) {
24243                     this.entities[i] = entities[i];
24244                     this._updateCalculated(base.entities[i], this.entities[i]);
24245                 }
24246
24247                 return this;
24248             }
24249         };
24250
24251         function osmTurn(turn) {
24252             if (!(this instanceof osmTurn)) {
24253                 return new osmTurn(turn);
24254             }
24255             Object.assign(this, turn);
24256         }
24257
24258
24259         function osmIntersection(graph, startVertexId, maxDistance) {
24260             maxDistance = maxDistance || 30;    // in meters
24261             var vgraph = coreGraph();           // virtual graph
24262             var i, j, k;
24263
24264
24265             function memberOfRestriction(entity) {
24266                 return graph.parentRelations(entity)
24267                     .some(function(r) { return r.isRestriction(); });
24268             }
24269
24270             function isRoad(way) {
24271                 if (way.isArea() || way.isDegenerate()) return false;
24272                 var roads = {
24273                     'motorway': true,
24274                     'motorway_link': true,
24275                     'trunk': true,
24276                     'trunk_link': true,
24277                     'primary': true,
24278                     'primary_link': true,
24279                     'secondary': true,
24280                     'secondary_link': true,
24281                     'tertiary': true,
24282                     'tertiary_link': true,
24283                     'residential': true,
24284                     'unclassified': true,
24285                     'living_street': true,
24286                     'service': true,
24287                     'road': true,
24288                     'track': true
24289                 };
24290                 return roads[way.tags.highway];
24291             }
24292
24293
24294             var startNode = graph.entity(startVertexId);
24295             var checkVertices = [startNode];
24296             var checkWays;
24297             var vertices = [];
24298             var vertexIds = [];
24299             var vertex;
24300             var ways = [];
24301             var wayIds = [];
24302             var way;
24303             var nodes = [];
24304             var node;
24305             var parents = [];
24306             var parent;
24307
24308             // `actions` will store whatever actions must be performed to satisfy
24309             // preconditions for adding a turn restriction to this intersection.
24310             //  - Remove any existing degenerate turn restrictions (missing from/to, etc)
24311             //  - Reverse oneways so that they are drawn in the forward direction
24312             //  - Split ways on key vertices
24313             var actions = [];
24314
24315
24316             // STEP 1:  walk the graph outwards from starting vertex to search
24317             //  for more key vertices and ways to include in the intersection..
24318
24319             while (checkVertices.length) {
24320                 vertex = checkVertices.pop();
24321
24322                 // check this vertex for parent ways that are roads
24323                 checkWays = graph.parentWays(vertex);
24324                 var hasWays = false;
24325                 for (i = 0; i < checkWays.length; i++) {
24326                     way = checkWays[i];
24327                     if (!isRoad(way) && !memberOfRestriction(way)) continue;
24328
24329                     ways.push(way);   // it's a road, or it's already in a turn restriction
24330                     hasWays = true;
24331
24332                     // check the way's children for more key vertices
24333                     nodes = utilArrayUniq(graph.childNodes(way));
24334                     for (j = 0; j < nodes.length; j++) {
24335                         node = nodes[j];
24336                         if (node === vertex) continue;                                           // same thing
24337                         if (vertices.indexOf(node) !== -1) continue;                             // seen it already
24338                         if (geoSphericalDistance(node.loc, startNode.loc) > maxDistance) continue;   // too far from start
24339
24340                         // a key vertex will have parents that are also roads
24341                         var hasParents = false;
24342                         parents = graph.parentWays(node);
24343                         for (k = 0; k < parents.length; k++) {
24344                             parent = parents[k];
24345                             if (parent === way) continue;                 // same thing
24346                             if (ways.indexOf(parent) !== -1) continue;    // seen it already
24347                             if (!isRoad(parent)) continue;                // not a road
24348                             hasParents = true;
24349                             break;
24350                         }
24351
24352                         if (hasParents) {
24353                             checkVertices.push(node);
24354                         }
24355                     }
24356                 }
24357
24358                 if (hasWays) {
24359                     vertices.push(vertex);
24360                 }
24361             }
24362
24363             vertices = utilArrayUniq(vertices);
24364             ways = utilArrayUniq(ways);
24365
24366
24367             // STEP 2:  Build a virtual graph containing only the entities in the intersection..
24368             // Everything done after this step should act on the virtual graph
24369             // Any actions that must be performed later to the main graph go in `actions` array
24370             ways.forEach(function(way) {
24371                 graph.childNodes(way).forEach(function(node) {
24372                     vgraph = vgraph.replace(node);
24373                 });
24374
24375                 vgraph = vgraph.replace(way);
24376
24377                 graph.parentRelations(way).forEach(function(relation) {
24378                     if (relation.isRestriction()) {
24379                         if (relation.isValidRestriction(graph)) {
24380                             vgraph = vgraph.replace(relation);
24381                         } else if (relation.isComplete(graph)) {
24382                             actions.push(actionDeleteRelation(relation.id));
24383                         }
24384                     }
24385                 });
24386             });
24387
24388
24389             // STEP 3:  Force all oneways to be drawn in the forward direction
24390             ways.forEach(function(w) {
24391                 var way = vgraph.entity(w.id);
24392                 if (way.tags.oneway === '-1') {
24393                     var action = actionReverse(way.id, { reverseOneway: true });
24394                     actions.push(action);
24395                     vgraph = action(vgraph);
24396                 }
24397             });
24398
24399
24400             // STEP 4:  Split ways on key vertices
24401             var origCount = osmEntity.id.next.way;
24402             vertices.forEach(function(v) {
24403                 // This is an odd way to do it, but we need to find all the ways that
24404                 // will be split here, then split them one at a time to ensure that these
24405                 // actions can be replayed on the main graph exactly in the same order.
24406                 // (It is unintuitive, but the order of ways returned from graph.parentWays()
24407                 // is arbitrary, depending on how the main graph and vgraph were built)
24408                 var splitAll = actionSplit(v.id);
24409                 if (!splitAll.disabled(vgraph)) {
24410                     splitAll.ways(vgraph).forEach(function(way) {
24411                         var splitOne = actionSplit(v.id).limitWays([way.id]);
24412                         actions.push(splitOne);
24413                         vgraph = splitOne(vgraph);
24414                     });
24415                 }
24416             });
24417
24418             // In here is where we should also split the intersection at nearby junction.
24419             //   for https://github.com/mapbox/iD-internal/issues/31
24420             // nearbyVertices.forEach(function(v) {
24421             // });
24422
24423             // Reasons why we reset the way id count here:
24424             //  1. Continuity with way ids created by the splits so that we can replay
24425             //     these actions later if the user decides to create a turn restriction
24426             //  2. Avoids churning way ids just by hovering over a vertex
24427             //     and displaying the turn restriction editor
24428             osmEntity.id.next.way = origCount;
24429
24430
24431             // STEP 5:  Update arrays to point to vgraph entities
24432             vertexIds = vertices.map(function(v) { return v.id; });
24433             vertices = [];
24434             ways = [];
24435
24436             vertexIds.forEach(function(id) {
24437                 var vertex = vgraph.entity(id);
24438                 var parents = vgraph.parentWays(vertex);
24439                 vertices.push(vertex);
24440                 ways = ways.concat(parents);
24441             });
24442
24443             vertices = utilArrayUniq(vertices);
24444             ways = utilArrayUniq(ways);
24445
24446             vertexIds = vertices.map(function(v) { return v.id; });
24447             wayIds = ways.map(function(w) { return w.id; });
24448
24449
24450             // STEP 6:  Update the ways with some metadata that will be useful for
24451             // walking the intersection graph later and rendering turn arrows.
24452
24453             function withMetadata(way, vertexIds) {
24454                 var __oneWay = way.isOneWay();
24455
24456                 // which affixes are key vertices?
24457                 var __first = (vertexIds.indexOf(way.first()) !== -1);
24458                 var __last = (vertexIds.indexOf(way.last()) !== -1);
24459
24460                 // what roles is this way eligible for?
24461                 var __via = (__first && __last);
24462                 var __from = ((__first && !__oneWay) || __last);
24463                 var __to = (__first || (__last && !__oneWay));
24464
24465                 return way.update({
24466                     __first:  __first,
24467                     __last:  __last,
24468                     __from:  __from,
24469                     __via: __via,
24470                     __to:  __to,
24471                     __oneWay:  __oneWay
24472                 });
24473             }
24474
24475             ways = [];
24476             wayIds.forEach(function(id) {
24477                 var way = withMetadata(vgraph.entity(id), vertexIds);
24478                 vgraph = vgraph.replace(way);
24479                 ways.push(way);
24480             });
24481
24482
24483             // STEP 7:  Simplify - This is an iterative process where we:
24484             //  1. Find trivial vertices with only 2 parents
24485             //  2. trim off the leaf way from those vertices and remove from vgraph
24486
24487             var keepGoing;
24488             var removeWayIds = [];
24489             var removeVertexIds = [];
24490
24491             do {
24492                 keepGoing = false;
24493                 checkVertices = vertexIds.slice();
24494
24495                 for (i = 0; i < checkVertices.length; i++) {
24496                     var vertexId = checkVertices[i];
24497                     vertex = vgraph.hasEntity(vertexId);
24498
24499                     if (!vertex) {
24500                         if (vertexIds.indexOf(vertexId) !== -1) {
24501                             vertexIds.splice(vertexIds.indexOf(vertexId), 1);   // stop checking this one
24502                         }
24503                         removeVertexIds.push(vertexId);
24504                         continue;
24505                     }
24506
24507                     parents = vgraph.parentWays(vertex);
24508                     if (parents.length < 3) {
24509                         if (vertexIds.indexOf(vertexId) !== -1) {
24510                             vertexIds.splice(vertexIds.indexOf(vertexId), 1);   // stop checking this one
24511                         }
24512                     }
24513
24514                     if (parents.length === 2) {     // vertex with 2 parents is trivial
24515                         var a = parents[0];
24516                         var b = parents[1];
24517                         var aIsLeaf = a && !a.__via;
24518                         var bIsLeaf = b && !b.__via;
24519                         var leaf, survivor;
24520
24521                         if (aIsLeaf && !bIsLeaf) {
24522                             leaf = a;
24523                             survivor = b;
24524                         } else if (!aIsLeaf && bIsLeaf) {
24525                             leaf = b;
24526                             survivor = a;
24527                         }
24528
24529                         if (leaf && survivor) {
24530                             survivor = withMetadata(survivor, vertexIds);      // update survivor way
24531                             vgraph = vgraph.replace(survivor).remove(leaf);    // update graph
24532                             removeWayIds.push(leaf.id);
24533                             keepGoing = true;
24534                         }
24535                     }
24536
24537                     parents = vgraph.parentWays(vertex);
24538
24539                     if (parents.length < 2) {     // vertex is no longer a key vertex
24540                         if (vertexIds.indexOf(vertexId) !== -1) {
24541                             vertexIds.splice(vertexIds.indexOf(vertexId), 1);   // stop checking this one
24542                         }
24543                         removeVertexIds.push(vertexId);
24544                         keepGoing = true;
24545                     }
24546
24547                     if (parents.length < 1) {     // vertex is no longer attached to anything
24548                         vgraph = vgraph.remove(vertex);
24549                     }
24550
24551                 }
24552             } while (keepGoing);
24553
24554
24555             vertices = vertices
24556                 .filter(function(vertex) { return removeVertexIds.indexOf(vertex.id) === -1; })
24557                 .map(function(vertex) { return vgraph.entity(vertex.id); });
24558             ways = ways
24559                 .filter(function(way) { return removeWayIds.indexOf(way.id) === -1; })
24560                 .map(function(way) { return vgraph.entity(way.id); });
24561
24562
24563             // OK!  Here is our intersection..
24564             var intersection = {
24565                 graph: vgraph,
24566                 actions: actions,
24567                 vertices: vertices,
24568                 ways: ways,
24569             };
24570
24571
24572
24573             // Get all the valid turns through this intersection given a starting way id.
24574             // This operates on the virtual graph for everything.
24575             //
24576             // Basically, walk through all possible paths from starting way,
24577             //   honoring the existing turn restrictions as we go (watch out for loops!)
24578             //
24579             // For each path found, generate and return a `osmTurn` datastructure.
24580             //
24581             intersection.turns = function(fromWayId, maxViaWay) {
24582                 if (!fromWayId) return [];
24583                 if (!maxViaWay) maxViaWay = 0;
24584
24585                 var vgraph = intersection.graph;
24586                 var keyVertexIds = intersection.vertices.map(function(v) { return v.id; });
24587
24588                 var start = vgraph.entity(fromWayId);
24589                 if (!start || !(start.__from || start.__via)) return [];
24590
24591                 // maxViaWay=0   from-*-to              (0 vias)
24592                 // maxViaWay=1   from-*-via-*-to        (1 via max)
24593                 // maxViaWay=2   from-*-via-*-via-*-to  (2 vias max)
24594                 var maxPathLength = (maxViaWay * 2) + 3;
24595                 var turns = [];
24596
24597                 step(start);
24598                 return turns;
24599
24600
24601                 // traverse the intersection graph and find all the valid paths
24602                 function step(entity, currPath, currRestrictions, matchedRestriction) {
24603                     currPath = (currPath || []).slice();  // shallow copy
24604                     if (currPath.length >= maxPathLength) return;
24605                     currPath.push(entity.id);
24606                     currRestrictions = (currRestrictions || []).slice();  // shallow copy
24607                     var i, j;
24608
24609                     if (entity.type === 'node') {
24610                         var parents = vgraph.parentWays(entity);
24611                         var nextWays = [];
24612
24613                         // which ways can we step into?
24614                         for (i = 0; i < parents.length; i++) {
24615                             var way = parents[i];
24616
24617                             // if next way is a oneway incoming to this vertex, skip
24618                             if (way.__oneWay && way.nodes[0] !== entity.id) continue;
24619
24620                             // if we have seen it before (allowing for an initial u-turn), skip
24621                             if (currPath.indexOf(way.id) !== -1 && currPath.length >= 3) continue;
24622
24623                             // Check all "current" restrictions (where we've already walked the `FROM`)
24624                             var restrict = undefined;
24625                             for (j = 0; j < currRestrictions.length; j++) {
24626                                 var restriction = currRestrictions[j];
24627                                 var f = restriction.memberByRole('from');
24628                                 var v = restriction.membersByRole('via');
24629                                 var t = restriction.memberByRole('to');
24630                                 var isOnly = /^only_/.test(restriction.tags.restriction);
24631
24632                                 // Does the current path match this turn restriction?
24633                                 var matchesFrom = (f.id === fromWayId);
24634                                 var matchesViaTo = false;
24635                                 var isAlongOnlyPath = false;
24636
24637                                 if (t.id === way.id) {     // match TO
24638
24639                                     if (v.length === 1 && v[0].type === 'node') {    // match VIA node
24640                                         matchesViaTo = (v[0].id === entity.id && (
24641                                             (matchesFrom && currPath.length === 2) ||
24642                                             (!matchesFrom && currPath.length > 2)
24643                                         ));
24644
24645                                     } else {                                         // match all VIA ways
24646                                         var pathVias = [];
24647                                         for (k = 2; k < currPath.length; k +=2 ) {   // k = 2 skips FROM
24648                                             pathVias.push(currPath[k]);              // (path goes way-node-way...)
24649                                         }
24650                                         var restrictionVias = [];
24651                                         for (k = 0; k < v.length; k++) {
24652                                             if (v[k].type === 'way') {
24653                                                 restrictionVias.push(v[k].id);
24654                                             }
24655                                         }
24656                                         var diff = utilArrayDifference(pathVias, restrictionVias);
24657                                         matchesViaTo = !diff.length;
24658                                     }
24659
24660                                 } else if (isOnly) {
24661                                     for (k = 0; k < v.length; k++) {
24662                                         // way doesn't match TO, but is one of the via ways along the path of an "only"
24663                                         if (v[k].type === 'way' && v[k].id === way.id) {
24664                                             isAlongOnlyPath = true;
24665                                             break;
24666                                         }
24667                                     }
24668                                 }
24669
24670                                 if (matchesViaTo) {
24671                                     if (isOnly) {
24672                                         restrict = { id: restriction.id, direct: matchesFrom, from: f.id, only: true, end: true };
24673                                     } else {
24674                                         restrict = { id: restriction.id, direct: matchesFrom, from: f.id, no: true, end: true };
24675                                     }
24676                                 } else {    // indirect - caused by a different nearby restriction
24677                                     if (isAlongOnlyPath) {
24678                                         restrict = { id: restriction.id, direct: false, from: f.id, only: true, end: false };
24679                                     } else if (isOnly) {
24680                                         restrict = { id: restriction.id, direct: false, from: f.id, no: true, end: true };
24681                                     }
24682                                 }
24683
24684                                 // stop looking if we find a "direct" restriction (matching FROM, VIA, TO)
24685                                 if (restrict && restrict.direct)
24686                                     break;
24687                             }
24688
24689                             nextWays.push({ way: way, restrict: restrict });
24690                         }
24691
24692                         nextWays.forEach(function(nextWay) {
24693                             step(nextWay.way, currPath, currRestrictions, nextWay.restrict);
24694                         });
24695
24696
24697                     } else {  // entity.type === 'way'
24698                         if (currPath.length >= 3) {     // this is a "complete" path..
24699                             var turnPath = currPath.slice();   // shallow copy
24700
24701                             // an indirect restriction - only include the partial path (starting at FROM)
24702                             if (matchedRestriction && matchedRestriction.direct === false) {
24703                                 for (i = 0; i < turnPath.length; i++) {
24704                                     if (turnPath[i] === matchedRestriction.from) {
24705                                         turnPath = turnPath.slice(i);
24706                                         break;
24707                                     }
24708                                 }
24709                             }
24710
24711                             var turn = pathToTurn(turnPath);
24712                             if (turn) {
24713                                 if (matchedRestriction) {
24714                                     turn.restrictionID = matchedRestriction.id;
24715                                     turn.no = matchedRestriction.no;
24716                                     turn.only = matchedRestriction.only;
24717                                     turn.direct = matchedRestriction.direct;
24718                                 }
24719                                 turns.push(osmTurn(turn));
24720                             }
24721
24722                             if (currPath[0] === currPath[2]) return;   // if we made a u-turn - stop here
24723                         }
24724
24725                         if (matchedRestriction && matchedRestriction.end) return;  // don't advance any further
24726
24727                         // which nodes can we step into?
24728                         var n1 = vgraph.entity(entity.first());
24729                         var n2 = vgraph.entity(entity.last());
24730                         var dist = geoSphericalDistance(n1.loc, n2.loc);
24731                         var nextNodes = [];
24732
24733                         if (currPath.length > 1) {
24734                             if (dist > maxDistance) return;   // the next node is too far
24735                             if (!entity.__via) return;        // this way is a leaf / can't be a via
24736                         }
24737
24738                         if (!entity.__oneWay &&                     // bidirectional..
24739                             keyVertexIds.indexOf(n1.id) !== -1 &&   // key vertex..
24740                             currPath.indexOf(n1.id) === -1) {       // haven't seen it yet..
24741                             nextNodes.push(n1);                     // can advance to first node
24742                         }
24743                         if (keyVertexIds.indexOf(n2.id) !== -1 &&   // key vertex..
24744                             currPath.indexOf(n2.id) === -1) {       // haven't seen it yet..
24745                             nextNodes.push(n2);                     // can advance to last node
24746                         }
24747
24748                         nextNodes.forEach(function(nextNode) {
24749                             // gather restrictions FROM this way
24750                             var fromRestrictions = vgraph.parentRelations(entity).filter(function(r) {
24751                                 if (!r.isRestriction()) return false;
24752
24753                                 var f = r.memberByRole('from');
24754                                 if (!f || f.id !== entity.id) return false;
24755
24756                                 var isOnly = /^only_/.test(r.tags.restriction);
24757                                 if (!isOnly) return true;
24758
24759                                 // `only_` restrictions only matter along the direction of the VIA - #4849
24760                                 var isOnlyVia = false;
24761                                 var v = r.membersByRole('via');
24762                                 if (v.length === 1 && v[0].type === 'node') {   // via node
24763                                     isOnlyVia = (v[0].id === nextNode.id);
24764                                 } else {                                        // via way(s)
24765                                     for (var i = 0; i < v.length; i++) {
24766                                         if (v[i].type !== 'way') continue;
24767                                         var viaWay = vgraph.entity(v[i].id);
24768                                         if (viaWay.first() === nextNode.id || viaWay.last() === nextNode.id) {
24769                                             isOnlyVia = true;
24770                                             break;
24771                                         }
24772                                     }
24773                                 }
24774                                 return isOnlyVia;
24775                             });
24776
24777                             step(nextNode, currPath, currRestrictions.concat(fromRestrictions), false);
24778                         });
24779                     }
24780                 }
24781
24782
24783                 // assumes path is alternating way-node-way of odd length
24784                 function pathToTurn(path) {
24785                     if (path.length < 3) return;
24786                     var fromWayId, fromNodeId, fromVertexId;
24787                     var toWayId, toNodeId, toVertexId;
24788                     var viaWayIds, viaNodeId, isUturn;
24789
24790                     fromWayId = path[0];
24791                     toWayId = path[path.length - 1];
24792
24793                     if (path.length === 3 && fromWayId === toWayId) {  // u turn
24794                         var way = vgraph.entity(fromWayId);
24795                         if (way.__oneWay) return null;
24796
24797                         isUturn = true;
24798                         viaNodeId = fromVertexId = toVertexId = path[1];
24799                         fromNodeId = toNodeId = adjacentNode(fromWayId, viaNodeId);
24800
24801                     } else {
24802                         isUturn = false;
24803                         fromVertexId = path[1];
24804                         fromNodeId = adjacentNode(fromWayId, fromVertexId);
24805                         toVertexId = path[path.length - 2];
24806                         toNodeId = adjacentNode(toWayId, toVertexId);
24807
24808                         if (path.length === 3) {
24809                             viaNodeId = path[1];
24810                         } else {
24811                             viaWayIds = path.filter(function(entityId) { return entityId[0] === 'w'; });
24812                             viaWayIds = viaWayIds.slice(1, viaWayIds.length - 1);  // remove first, last
24813                         }
24814                     }
24815
24816                     return {
24817                         key:  path.join('_'),
24818                         path: path,
24819                         from: { node: fromNodeId, way:  fromWayId, vertex: fromVertexId },
24820                         via:  { node: viaNodeId,  ways: viaWayIds },
24821                         to:   { node: toNodeId,   way:  toWayId, vertex: toVertexId },
24822                         u:    isUturn
24823                     };
24824
24825
24826                     function adjacentNode(wayId, affixId) {
24827                         var nodes = vgraph.entity(wayId).nodes;
24828                         return affixId === nodes[0] ? nodes[1] : nodes[nodes.length - 2];
24829                     }
24830                 }
24831
24832             };
24833
24834             return intersection;
24835         }
24836
24837
24838         function osmInferRestriction(graph, turn, projection) {
24839             var fromWay = graph.entity(turn.from.way);
24840             var fromNode = graph.entity(turn.from.node);
24841             var fromVertex = graph.entity(turn.from.vertex);
24842             var toWay = graph.entity(turn.to.way);
24843             var toNode = graph.entity(turn.to.node);
24844             var toVertex = graph.entity(turn.to.vertex);
24845
24846             var fromOneWay = (fromWay.tags.oneway === 'yes');
24847             var toOneWay = (toWay.tags.oneway === 'yes');
24848             var angle = (geoAngle(fromVertex, fromNode, projection) -
24849                         geoAngle(toVertex, toNode, projection)) * 180 / Math.PI;
24850
24851             while (angle < 0)
24852                 angle += 360;
24853
24854             if (fromNode === toNode)
24855                 return 'no_u_turn';
24856             if ((angle < 23 || angle > 336) && fromOneWay && toOneWay)
24857                 return 'no_u_turn';   // wider tolerance for u-turn if both ways are oneway
24858             if ((angle < 40 || angle > 319) && fromOneWay && toOneWay && turn.from.vertex !== turn.to.vertex)
24859                 return 'no_u_turn';   // even wider tolerance for u-turn if there is a via way (from !== to)
24860             if (angle < 158)
24861                 return 'no_right_turn';
24862             if (angle > 202)
24863                 return 'no_left_turn';
24864
24865             return 'no_straight_on';
24866         }
24867
24868         function actionMergePolygon(ids, newRelationId) {
24869
24870             function groupEntities(graph) {
24871                 var entities = ids.map(function (id) { return graph.entity(id); });
24872                 var geometryGroups = utilArrayGroupBy(entities, function(entity) {
24873                     if (entity.type === 'way' && entity.isClosed()) {
24874                         return 'closedWay';
24875                     } else if (entity.type === 'relation' && entity.isMultipolygon()) {
24876                         return 'multipolygon';
24877                     } else {
24878                         return 'other';
24879                     }
24880                 });
24881
24882                 return Object.assign(
24883                     { closedWay: [], multipolygon: [], other: [] },
24884                     geometryGroups
24885                 );
24886             }
24887
24888
24889             var action = function(graph) {
24890                 var entities = groupEntities(graph);
24891
24892                 // An array representing all the polygons that are part of the multipolygon.
24893                 //
24894                 // Each element is itself an array of objects with an id property, and has a
24895                 // locs property which is an array of the locations forming the polygon.
24896                 var polygons = entities.multipolygon.reduce(function(polygons, m) {
24897                     return polygons.concat(osmJoinWays(m.members, graph));
24898                 }, []).concat(entities.closedWay.map(function(d) {
24899                     var member = [{id: d.id}];
24900                     member.nodes = graph.childNodes(d);
24901                     return member;
24902                 }));
24903
24904                 // contained is an array of arrays of boolean values,
24905                 // where contained[j][k] is true iff the jth way is
24906                 // contained by the kth way.
24907                 var contained = polygons.map(function(w, i) {
24908                     return polygons.map(function(d, n) {
24909                         if (i === n) return null;
24910                         return geoPolygonContainsPolygon(
24911                             d.nodes.map(function(n) { return n.loc; }),
24912                             w.nodes.map(function(n) { return n.loc; })
24913                         );
24914                     });
24915                 });
24916
24917                 // Sort all polygons as either outer or inner ways
24918                 var members = [];
24919                 var outer = true;
24920
24921                 while (polygons.length) {
24922                     extractUncontained(polygons);
24923                     polygons = polygons.filter(isContained);
24924                     contained = contained.filter(isContained).map(filterContained);
24925                 }
24926
24927                 function isContained(d, i) {
24928                     return contained[i].some(function(val) { return val; });
24929                 }
24930
24931                 function filterContained(d) {
24932                     return d.filter(isContained);
24933                 }
24934
24935                 function extractUncontained(polygons) {
24936                     polygons.forEach(function(d, i) {
24937                         if (!isContained(d, i)) {
24938                             d.forEach(function(member) {
24939                                 members.push({
24940                                     type: 'way',
24941                                     id: member.id,
24942                                     role: outer ? 'outer' : 'inner'
24943                                 });
24944                             });
24945                         }
24946                     });
24947                     outer = !outer;
24948                 }
24949
24950                 // Move all tags to one relation
24951                 var relation = entities.multipolygon[0] ||
24952                     osmRelation({ id: newRelationId, tags: { type: 'multipolygon' }});
24953
24954                 entities.multipolygon.slice(1).forEach(function(m) {
24955                     relation = relation.mergeTags(m.tags);
24956                     graph = graph.remove(m);
24957                 });
24958
24959                 entities.closedWay.forEach(function(way) {
24960                     function isThisOuter(m) {
24961                         return m.id === way.id && m.role !== 'inner';
24962                     }
24963                     if (members.some(isThisOuter)) {
24964                         relation = relation.mergeTags(way.tags);
24965                         graph = graph.replace(way.update({ tags: {} }));
24966                     }
24967                 });
24968
24969                 return graph.replace(relation.update({
24970                     members: members,
24971                     tags: utilObjectOmit(relation.tags, ['area'])
24972                 }));
24973             };
24974
24975
24976             action.disabled = function(graph) {
24977                 var entities = groupEntities(graph);
24978                 if (entities.other.length > 0 ||
24979                     entities.closedWay.length + entities.multipolygon.length < 2) {
24980                     return 'not_eligible';
24981                 }
24982                 if (!entities.multipolygon.every(function(r) { return r.isComplete(graph); })) {
24983                     return 'incomplete_relation';
24984                 }
24985
24986                 if (!entities.multipolygon.length) {
24987                     var sharedMultipolygons = [];
24988                     entities.closedWay.forEach(function(way, i) {
24989                         if (i === 0) {
24990                             sharedMultipolygons = graph.parentMultipolygons(way);
24991                         } else {
24992                             sharedMultipolygons = utilArrayIntersection(sharedMultipolygons, graph.parentMultipolygons(way));
24993                         }
24994                     });
24995                     sharedMultipolygons = sharedMultipolygons.filter(function(relation) {
24996                         return relation.members.length === entities.closedWay.length;
24997                     });
24998                     if (sharedMultipolygons.length) {
24999                         // don't create a new multipolygon if it'd be redundant
25000                         return 'not_eligible';
25001                     }
25002                 } else if (entities.closedWay.some(function(way) {
25003                         return utilArrayIntersection(graph.parentMultipolygons(way), entities.multipolygon).length;
25004                     })) {
25005                     // don't add a way to a multipolygon again if it's already a member
25006                     return 'not_eligible';
25007                 }
25008             };
25009
25010
25011             return action;
25012         }
25013
25014         // do not edit .js files directly - edit src/index.jst
25015
25016
25017
25018         var fastDeepEqual = function equal(a, b) {
25019           if (a === b) return true;
25020
25021           if (a && b && typeof a == 'object' && typeof b == 'object') {
25022             if (a.constructor !== b.constructor) return false;
25023
25024             var length, i, keys;
25025             if (Array.isArray(a)) {
25026               length = a.length;
25027               if (length != b.length) return false;
25028               for (i = length; i-- !== 0;)
25029                 if (!equal(a[i], b[i])) return false;
25030               return true;
25031             }
25032
25033
25034
25035             if (a.constructor === RegExp) return a.source === b.source && a.flags === b.flags;
25036             if (a.valueOf !== Object.prototype.valueOf) return a.valueOf() === b.valueOf();
25037             if (a.toString !== Object.prototype.toString) return a.toString() === b.toString();
25038
25039             keys = Object.keys(a);
25040             length = keys.length;
25041             if (length !== Object.keys(b).length) return false;
25042
25043             for (i = length; i-- !== 0;)
25044               if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false;
25045
25046             for (i = length; i-- !== 0;) {
25047               var key = keys[i];
25048
25049               if (!equal(a[key], b[key])) return false;
25050             }
25051
25052             return true;
25053           }
25054
25055           // true if both NaN, false otherwise
25056           return a!==a && b!==b;
25057         };
25058
25059         // Text diff algorithm following Hunt and McIlroy 1976.
25060         // J. W. Hunt and M. D. McIlroy, An algorithm for differential buffer
25061         // comparison, Bell Telephone Laboratories CSTR #41 (1976)
25062         // http://www.cs.dartmouth.edu/~doug/
25063         // https://en.wikipedia.org/wiki/Longest_common_subsequence_problem
25064         //
25065         // Expects two arrays, finds longest common sequence
25066         function LCS(buffer1, buffer2) {
25067
25068           let equivalenceClasses = {};
25069           for (let j = 0; j < buffer2.length; j++) {
25070             const item = buffer2[j];
25071             if (equivalenceClasses[item]) {
25072               equivalenceClasses[item].push(j);
25073             } else {
25074               equivalenceClasses[item] = [j];
25075             }
25076           }
25077
25078           const NULLRESULT = { buffer1index: -1, buffer2index: -1, chain: null };
25079           let candidates = [NULLRESULT];
25080
25081           for (let i = 0; i < buffer1.length; i++) {
25082             const item = buffer1[i];
25083             const buffer2indices = equivalenceClasses[item] || [];
25084             let r = 0;
25085             let c = candidates[0];
25086
25087             for (let jx = 0; jx < buffer2indices.length; jx++) {
25088               const j = buffer2indices[jx];
25089
25090               let s;
25091               for (s = r; s < candidates.length; s++) {
25092                 if ((candidates[s].buffer2index < j) && ((s === candidates.length - 1) || (candidates[s + 1].buffer2index > j))) {
25093                   break;
25094                 }
25095               }
25096
25097               if (s < candidates.length) {
25098                 const newCandidate = { buffer1index: i, buffer2index: j, chain: candidates[s] };
25099                 if (r === candidates.length) {
25100                   candidates.push(c);
25101                 } else {
25102                   candidates[r] = c;
25103                 }
25104                 r = s + 1;
25105                 c = newCandidate;
25106                 if (r === candidates.length) {
25107                   break; // no point in examining further (j)s
25108                 }
25109               }
25110             }
25111
25112             candidates[r] = c;
25113           }
25114
25115           // At this point, we know the LCS: it's in the reverse of the
25116           // linked-list through .chain of candidates[candidates.length - 1].
25117
25118           return candidates[candidates.length - 1];
25119         }
25120
25121
25122         // We apply the LCS to give a simple representation of the
25123         // offsets and lengths of mismatched chunks in the input
25124         // buffers. This is used by diff3MergeRegions.
25125         function diffIndices(buffer1, buffer2) {
25126           const lcs = LCS(buffer1, buffer2);
25127           let result = [];
25128           let tail1 = buffer1.length;
25129           let tail2 = buffer2.length;
25130
25131           for (let candidate = lcs; candidate !== null; candidate = candidate.chain) {
25132             const mismatchLength1 = tail1 - candidate.buffer1index - 1;
25133             const mismatchLength2 = tail2 - candidate.buffer2index - 1;
25134             tail1 = candidate.buffer1index;
25135             tail2 = candidate.buffer2index;
25136
25137             if (mismatchLength1 || mismatchLength2) {
25138               result.push({
25139                 buffer1: [tail1 + 1, mismatchLength1],
25140                 buffer1Content: buffer1.slice(tail1 + 1, tail1 + 1 + mismatchLength1),
25141                 buffer2: [tail2 + 1, mismatchLength2],
25142                 buffer2Content: buffer2.slice(tail2 + 1, tail2 + 1 + mismatchLength2)
25143               });
25144             }
25145           }
25146
25147           result.reverse();
25148           return result;
25149         }
25150
25151
25152         // Given three buffers, A, O, and B, where both A and B are
25153         // independently derived from O, returns a fairly complicated
25154         // internal representation of merge decisions it's taken. The
25155         // interested reader may wish to consult
25156         //
25157         // Sanjeev Khanna, Keshav Kunal, and Benjamin C. Pierce.
25158         // 'A Formal Investigation of ' In Arvind and Prasad,
25159         // editors, Foundations of Software Technology and Theoretical
25160         // Computer Science (FSTTCS), December 2007.
25161         //
25162         // (http://www.cis.upenn.edu/~bcpierce/papers/diff3-short.pdf)
25163         //
25164         function diff3MergeRegions(a, o, b) {
25165
25166           // "hunks" are array subsets where `a` or `b` are different from `o`
25167           // https://www.gnu.org/software/diffutils/manual/html_node/diff3-Hunks.html
25168           let hunks = [];
25169           function addHunk(h, ab) {
25170             hunks.push({
25171               ab: ab,
25172               oStart: h.buffer1[0],
25173               oLength: h.buffer1[1],   // length of o to remove
25174               abStart: h.buffer2[0],
25175               abLength: h.buffer2[1]   // length of a/b to insert
25176               // abContent: (ab === 'a' ? a : b).slice(h.buffer2[0], h.buffer2[0] + h.buffer2[1])
25177             });
25178           }
25179
25180           diffIndices(o, a).forEach(item => addHunk(item, 'a'));
25181           diffIndices(o, b).forEach(item => addHunk(item, 'b'));
25182           hunks.sort((x,y) => x.oStart - y.oStart);
25183
25184           let results = [];
25185           let currOffset = 0;
25186
25187           function advanceTo(endOffset) {
25188             if (endOffset > currOffset) {
25189               results.push({
25190                 stable: true,
25191                 buffer: 'o',
25192                 bufferStart: currOffset,
25193                 bufferLength: endOffset - currOffset,
25194                 bufferContent: o.slice(currOffset, endOffset)
25195               });
25196               currOffset = endOffset;
25197             }
25198           }
25199
25200           while (hunks.length) {
25201             let hunk = hunks.shift();
25202             let regionStart = hunk.oStart;
25203             let regionEnd = hunk.oStart + hunk.oLength;
25204             let regionHunks = [hunk];
25205             advanceTo(regionStart);
25206
25207             // Try to pull next overlapping hunk into this region
25208             while (hunks.length) {
25209               const nextHunk = hunks[0];
25210               const nextHunkStart = nextHunk.oStart;
25211               if (nextHunkStart > regionEnd) break;   // no overlap
25212
25213               regionEnd = Math.max(regionEnd, nextHunkStart + nextHunk.oLength);
25214               regionHunks.push(hunks.shift());
25215             }
25216
25217             if (regionHunks.length === 1) {
25218               // Only one hunk touches this region, meaning that there is no conflict here.
25219               // Either `a` or `b` is inserting into a region of `o` unchanged by the other.
25220               if (hunk.abLength > 0) {
25221                 const buffer = (hunk.ab === 'a' ? a : b);
25222                 results.push({
25223                   stable: true,
25224                   buffer: hunk.ab,
25225                   bufferStart: hunk.abStart,
25226                   bufferLength: hunk.abLength,
25227                   bufferContent: buffer.slice(hunk.abStart, hunk.abStart + hunk.abLength)
25228                 });
25229               }
25230             } else {
25231               // A true a/b conflict. Determine the bounds involved from `a`, `o`, and `b`.
25232               // Effectively merge all the `a` hunks into one giant hunk, then do the
25233               // same for the `b` hunks; then, correct for skew in the regions of `o`
25234               // that each side changed, and report appropriate spans for the three sides.
25235               let bounds = {
25236                 a: [a.length, -1, o.length, -1],
25237                 b: [b.length, -1, o.length, -1]
25238               };
25239               while (regionHunks.length) {
25240                 hunk = regionHunks.shift();
25241                 const oStart = hunk.oStart;
25242                 const oEnd = oStart + hunk.oLength;
25243                 const abStart = hunk.abStart;
25244                 const abEnd = abStart + hunk.abLength;
25245                 let b = bounds[hunk.ab];
25246                 b[0] = Math.min(abStart, b[0]);
25247                 b[1] = Math.max(abEnd, b[1]);
25248                 b[2] = Math.min(oStart, b[2]);
25249                 b[3] = Math.max(oEnd, b[3]);
25250               }
25251
25252               const aStart = bounds.a[0] + (regionStart - bounds.a[2]);
25253               const aEnd = bounds.a[1] + (regionEnd - bounds.a[3]);
25254               const bStart = bounds.b[0] + (regionStart - bounds.b[2]);
25255               const bEnd = bounds.b[1] + (regionEnd - bounds.b[3]);
25256
25257               let result = {
25258                 stable: false,
25259                 aStart: aStart,
25260                 aLength: aEnd - aStart,
25261                 aContent: a.slice(aStart, aEnd),
25262                 oStart: regionStart,
25263                 oLength: regionEnd - regionStart,
25264                 oContent: o.slice(regionStart, regionEnd),
25265                 bStart: bStart,
25266                 bLength: bEnd - bStart,
25267                 bContent: b.slice(bStart, bEnd)
25268               };
25269               results.push(result);
25270             }
25271             currOffset = regionEnd;
25272           }
25273
25274           advanceTo(o.length);
25275
25276           return results;
25277         }
25278
25279
25280         // Applies the output of diff3MergeRegions to actually
25281         // construct the merged buffer; the returned result alternates
25282         // between 'ok' and 'conflict' blocks.
25283         // A "false conflict" is where `a` and `b` both change the same from `o`
25284         function diff3Merge(a, o, b, options) {
25285           let defaults = {
25286             excludeFalseConflicts: true,
25287             stringSeparator: /\s+/
25288           };
25289           options = Object.assign(defaults, options);
25290
25291           const aString = (typeof a === 'string');
25292           const oString = (typeof o === 'string');
25293           const bString = (typeof b === 'string');
25294
25295           if (aString) a = a.split(options.stringSeparator);
25296           if (oString) o = o.split(options.stringSeparator);
25297           if (bString) b = b.split(options.stringSeparator);
25298
25299           let results = [];
25300           const regions = diff3MergeRegions(a, o, b);
25301
25302           let okBuffer = [];
25303           function flushOk() {
25304             if (okBuffer.length) {
25305               results.push({ ok: okBuffer });
25306             }
25307             okBuffer = [];
25308           }
25309
25310           function isFalseConflict(a, b) {
25311             if (a.length !== b.length) return false;
25312             for (let i = 0; i < a.length; i++) {
25313               if (a[i] !== b[i]) return false;
25314             }
25315             return true;
25316           }
25317
25318           regions.forEach(region =>  {
25319             if (region.stable) {
25320               okBuffer.push(...region.bufferContent);
25321             } else {
25322               if (options.excludeFalseConflicts && isFalseConflict(region.aContent, region.bContent)) {
25323                 okBuffer.push(...region.aContent);
25324               } else {
25325                 flushOk();
25326                 results.push({
25327                   conflict: {
25328                     a: region.aContent,
25329                     aIndex: region.aStart,
25330                     o: region.oContent,
25331                     oIndex: region.oStart,
25332                     b: region.bContent,
25333                     bIndex: region.bStart
25334                   }
25335                 });
25336               }
25337             }
25338           });
25339
25340           flushOk();
25341           return results;
25342         }
25343
25344         function actionMergeRemoteChanges(id, localGraph, remoteGraph, discardTags, formatUser) {
25345             discardTags = discardTags || {};
25346             var _option = 'safe';  // 'safe', 'force_local', 'force_remote'
25347             var _conflicts = [];
25348
25349
25350             function user(d) {
25351                 return (typeof formatUser === 'function') ? formatUser(d) : d;
25352             }
25353
25354
25355             function mergeLocation(remote, target) {
25356                 function pointEqual(a, b) {
25357                     var epsilon = 1e-6;
25358                     return (Math.abs(a[0] - b[0]) < epsilon) && (Math.abs(a[1] - b[1]) < epsilon);
25359                 }
25360
25361                 if (_option === 'force_local' || pointEqual(target.loc, remote.loc)) {
25362                     return target;
25363                 }
25364                 if (_option === 'force_remote') {
25365                     return target.update({loc: remote.loc});
25366                 }
25367
25368                 _conflicts.push(_t('merge_remote_changes.conflict.location', { user: user(remote.user) }));
25369                 return target;
25370             }
25371
25372
25373             function mergeNodes(base, remote, target) {
25374                 if (_option === 'force_local' || fastDeepEqual(target.nodes, remote.nodes)) {
25375                     return target;
25376                 }
25377                 if (_option === 'force_remote') {
25378                     return target.update({nodes: remote.nodes});
25379                 }
25380
25381                 var ccount = _conflicts.length;
25382                 var o = base.nodes || [];
25383                 var a = target.nodes || [];
25384                 var b = remote.nodes || [];
25385                 var nodes = [];
25386                 var hunks = diff3Merge(a, o, b, { excludeFalseConflicts: true });
25387
25388                 for (var i = 0; i < hunks.length; i++) {
25389                     var hunk = hunks[i];
25390                     if (hunk.ok) {
25391                         nodes.push.apply(nodes, hunk.ok);
25392                     } else {
25393                         // for all conflicts, we can assume c.a !== c.b
25394                         // because `diff3Merge` called with `true` option to exclude false conflicts..
25395                         var c = hunk.conflict;
25396                         if (fastDeepEqual(c.o, c.a)) {  // only changed remotely
25397                             nodes.push.apply(nodes, c.b);
25398                         } else if (fastDeepEqual(c.o, c.b)) {  // only changed locally
25399                             nodes.push.apply(nodes, c.a);
25400                         } else {       // changed both locally and remotely
25401                             _conflicts.push(_t('merge_remote_changes.conflict.nodelist', { user: user(remote.user) }));
25402                             break;
25403                         }
25404                     }
25405                 }
25406
25407                 return (_conflicts.length === ccount) ? target.update({nodes: nodes}) : target;
25408             }
25409
25410
25411             function mergeChildren(targetWay, children, updates, graph) {
25412                 function isUsed(node, targetWay) {
25413                     var hasInterestingParent = graph.parentWays(node)
25414                         .some(function(way) { return way.id !== targetWay.id; });
25415
25416                     return node.hasInterestingTags() ||
25417                         hasInterestingParent ||
25418                         graph.parentRelations(node).length > 0;
25419                 }
25420
25421                 var ccount = _conflicts.length;
25422
25423                 for (var i = 0; i < children.length; i++) {
25424                     var id = children[i];
25425                     var node = graph.hasEntity(id);
25426
25427                     // remove unused childNodes..
25428                     if (targetWay.nodes.indexOf(id) === -1) {
25429                         if (node && !isUsed(node, targetWay)) {
25430                             updates.removeIds.push(id);
25431                         }
25432                         continue;
25433                     }
25434
25435                     // restore used childNodes..
25436                     var local = localGraph.hasEntity(id);
25437                     var remote = remoteGraph.hasEntity(id);
25438                     var target;
25439
25440                     if (_option === 'force_remote' && remote && remote.visible) {
25441                         updates.replacements.push(remote);
25442
25443                     } else if (_option === 'force_local' && local) {
25444                         target = osmEntity(local);
25445                         if (remote) {
25446                             target = target.update({ version: remote.version });
25447                         }
25448                         updates.replacements.push(target);
25449
25450                     } else if (_option === 'safe' && local && remote && local.version !== remote.version) {
25451                         target = osmEntity(local, { version: remote.version });
25452                         if (remote.visible) {
25453                             target = mergeLocation(remote, target);
25454                         } else {
25455                             _conflicts.push(_t('merge_remote_changes.conflict.deleted', { user: user(remote.user) }));
25456                         }
25457
25458                         if (_conflicts.length !== ccount) break;
25459                         updates.replacements.push(target);
25460                     }
25461                 }
25462
25463                 return targetWay;
25464             }
25465
25466
25467             function updateChildren(updates, graph) {
25468                 for (var i = 0; i < updates.replacements.length; i++) {
25469                     graph = graph.replace(updates.replacements[i]);
25470                 }
25471                 if (updates.removeIds.length) {
25472                     graph = actionDeleteMultiple(updates.removeIds)(graph);
25473                 }
25474                 return graph;
25475             }
25476
25477
25478             function mergeMembers(remote, target) {
25479                 if (_option === 'force_local' || fastDeepEqual(target.members, remote.members)) {
25480                     return target;
25481                 }
25482                 if (_option === 'force_remote') {
25483                     return target.update({members: remote.members});
25484                 }
25485
25486                 _conflicts.push(_t('merge_remote_changes.conflict.memberlist', { user: user(remote.user) }));
25487                 return target;
25488             }
25489
25490
25491             function mergeTags(base, remote, target) {
25492                 if (_option === 'force_local' || fastDeepEqual(target.tags, remote.tags)) {
25493                     return target;
25494                 }
25495                 if (_option === 'force_remote') {
25496                     return target.update({tags: remote.tags});
25497                 }
25498
25499                 var ccount = _conflicts.length;
25500                 var o = base.tags || {};
25501                 var a = target.tags || {};
25502                 var b = remote.tags || {};
25503                 var keys = utilArrayUnion(utilArrayUnion(Object.keys(o), Object.keys(a)), Object.keys(b))
25504                     .filter(function(k) { return !discardTags[k]; });
25505                 var tags = Object.assign({}, a);   // shallow copy
25506                 var changed = false;
25507
25508                 for (var i = 0; i < keys.length; i++) {
25509                     var k = keys[i];
25510
25511                     if (o[k] !== b[k] && a[k] !== b[k]) {    // changed remotely..
25512                         if (o[k] !== a[k]) {      // changed locally..
25513                             _conflicts.push(_t('merge_remote_changes.conflict.tags',
25514                                 { tag: k, local: a[k], remote: b[k], user: user(remote.user) }));
25515
25516                         } else {                  // unchanged locally, accept remote change..
25517                             if (b.hasOwnProperty(k)) {
25518                                 tags[k] = b[k];
25519                             } else {
25520                                 delete tags[k];
25521                             }
25522                             changed = true;
25523                         }
25524                     }
25525                 }
25526
25527                 return (changed && _conflicts.length === ccount) ? target.update({tags: tags}) : target;
25528             }
25529
25530
25531             //  `graph.base()` is the common ancestor of the two graphs.
25532             //  `localGraph` contains user's edits up to saving
25533             //  `remoteGraph` contains remote edits to modified nodes
25534             //  `graph` must be a descendent of `localGraph` and may include
25535             //      some conflict resolution actions performed on it.
25536             //
25537             //                  --- ... --- `localGraph` -- ... -- `graph`
25538             //                 /
25539             //  `graph.base()` --- ... --- `remoteGraph`
25540             //
25541             var action = function(graph) {
25542                 var updates = { replacements: [], removeIds: [] };
25543                 var base = graph.base().entities[id];
25544                 var local = localGraph.entity(id);
25545                 var remote = remoteGraph.entity(id);
25546                 var target = osmEntity(local, { version: remote.version });
25547
25548                 // delete/undelete
25549                 if (!remote.visible) {
25550                     if (_option === 'force_remote') {
25551                         return actionDeleteMultiple([id])(graph);
25552
25553                     } else if (_option === 'force_local') {
25554                         if (target.type === 'way') {
25555                             target = mergeChildren(target, utilArrayUniq(local.nodes), updates, graph);
25556                             graph = updateChildren(updates, graph);
25557                         }
25558                         return graph.replace(target);
25559
25560                     } else {
25561                         _conflicts.push(_t('merge_remote_changes.conflict.deleted', { user: user(remote.user) }));
25562                         return graph;  // do nothing
25563                     }
25564                 }
25565
25566                 // merge
25567                 if (target.type === 'node') {
25568                     target = mergeLocation(remote, target);
25569
25570                 } else if (target.type === 'way') {
25571                     // pull in any child nodes that may not be present locally..
25572                     graph.rebase(remoteGraph.childNodes(remote), [graph], false);
25573                     target = mergeNodes(base, remote, target);
25574                     target = mergeChildren(target, utilArrayUnion(local.nodes, remote.nodes), updates, graph);
25575
25576                 } else if (target.type === 'relation') {
25577                     target = mergeMembers(remote, target);
25578                 }
25579
25580                 target = mergeTags(base, remote, target);
25581
25582                 if (!_conflicts.length) {
25583                     graph = updateChildren(updates, graph).replace(target);
25584                 }
25585
25586                 return graph;
25587             };
25588
25589
25590             action.withOption = function(opt) {
25591                 _option = opt;
25592                 return action;
25593             };
25594
25595
25596             action.conflicts = function() {
25597                 return _conflicts;
25598             };
25599
25600
25601             return action;
25602         }
25603
25604         // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/command/MoveCommand.java
25605         // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MoveNodeAction.as
25606         function actionMove(moveIDs, tryDelta, projection, cache) {
25607             var _delta = tryDelta;
25608
25609             function setupCache(graph) {
25610                 function canMove(nodeID) {
25611                     // Allow movement of any node that is in the selectedIDs list..
25612                     if (moveIDs.indexOf(nodeID) !== -1) return true;
25613
25614                     // Allow movement of a vertex where 2 ways meet..
25615                     var parents = graph.parentWays(graph.entity(nodeID));
25616                     if (parents.length < 3) return true;
25617
25618                     // Restrict movement of a vertex where >2 ways meet, unless all parentWays are moving too..
25619                     var parentsMoving = parents.every(function(way) { return cache.moving[way.id]; });
25620                     if (!parentsMoving) delete cache.moving[nodeID];
25621
25622                     return parentsMoving;
25623                 }
25624
25625                 function cacheEntities(ids) {
25626                     for (var i = 0; i < ids.length; i++) {
25627                         var id = ids[i];
25628                         if (cache.moving[id]) continue;
25629                         cache.moving[id] = true;
25630
25631                         var entity = graph.hasEntity(id);
25632                         if (!entity) continue;
25633
25634                         if (entity.type === 'node') {
25635                             cache.nodes.push(id);
25636                             cache.startLoc[id] = entity.loc;
25637                         } else if (entity.type === 'way') {
25638                             cache.ways.push(id);
25639                             cacheEntities(entity.nodes);
25640                         } else {
25641                             cacheEntities(entity.members.map(function(member) {
25642                                 return member.id;
25643                             }));
25644                         }
25645                     }
25646                 }
25647
25648                 function cacheIntersections(ids) {
25649                     function isEndpoint(way, id) {
25650                         return !way.isClosed() && !!way.affix(id);
25651                     }
25652
25653                     for (var i = 0; i < ids.length; i++) {
25654                         var id = ids[i];
25655
25656                         // consider only intersections with 1 moved and 1 unmoved way.
25657                         var childNodes = graph.childNodes(graph.entity(id));
25658                         for (var j = 0; j < childNodes.length; j++) {
25659                             var node = childNodes[j];
25660                             var parents = graph.parentWays(node);
25661                             if (parents.length !== 2) continue;
25662
25663                             var moved = graph.entity(id);
25664                             var unmoved = null;
25665                             for (var k = 0; k < parents.length; k++) {
25666                                 var way = parents[k];
25667                                 if (!cache.moving[way.id]) {
25668                                     unmoved = way;
25669                                     break;
25670                                 }
25671                             }
25672                             if (!unmoved) continue;
25673
25674                             // exclude ways that are overly connected..
25675                             if (utilArrayIntersection(moved.nodes, unmoved.nodes).length > 2) continue;
25676                             if (moved.isArea() || unmoved.isArea()) continue;
25677
25678                             cache.intersections.push({
25679                                 nodeId: node.id,
25680                                 movedId: moved.id,
25681                                 unmovedId: unmoved.id,
25682                                 movedIsEP: isEndpoint(moved, node.id),
25683                                 unmovedIsEP: isEndpoint(unmoved, node.id)
25684                             });
25685                         }
25686                     }
25687                 }
25688
25689
25690                 if (!cache) {
25691                     cache = {};
25692                 }
25693                 if (!cache.ok) {
25694                     cache.moving = {};
25695                     cache.intersections = [];
25696                     cache.replacedVertex = {};
25697                     cache.startLoc = {};
25698                     cache.nodes = [];
25699                     cache.ways = [];
25700
25701                     cacheEntities(moveIDs);
25702                     cacheIntersections(cache.ways);
25703                     cache.nodes = cache.nodes.filter(canMove);
25704
25705                     cache.ok = true;
25706                 }
25707             }
25708
25709
25710             // Place a vertex where the moved vertex used to be, to preserve way shape..
25711             //
25712             //  Start:
25713             //      b ---- e
25714             //     / \
25715             //    /   \
25716             //   /     \
25717             //  a       c
25718             //
25719             //      *               node '*' added to preserve shape
25720             //     / \
25721             //    /   b ---- e      way `b,e` moved here:
25722             //   /     \
25723             //  a       c
25724             //
25725             //
25726             function replaceMovedVertex(nodeId, wayId, graph, delta) {
25727                 var way = graph.entity(wayId);
25728                 var moved = graph.entity(nodeId);
25729                 var movedIndex = way.nodes.indexOf(nodeId);
25730                 var len, prevIndex, nextIndex;
25731
25732                 if (way.isClosed()) {
25733                     len = way.nodes.length - 1;
25734                     prevIndex = (movedIndex + len - 1) % len;
25735                     nextIndex = (movedIndex + len + 1) % len;
25736                 } else {
25737                     len = way.nodes.length;
25738                     prevIndex = movedIndex - 1;
25739                     nextIndex = movedIndex + 1;
25740                 }
25741
25742                 var prev = graph.hasEntity(way.nodes[prevIndex]);
25743                 var next = graph.hasEntity(way.nodes[nextIndex]);
25744
25745                 // Don't add orig vertex at endpoint..
25746                 if (!prev || !next) return graph;
25747
25748                 var key = wayId + '_' + nodeId;
25749                 var orig = cache.replacedVertex[key];
25750                 if (!orig) {
25751                     orig = osmNode();
25752                     cache.replacedVertex[key] = orig;
25753                     cache.startLoc[orig.id] = cache.startLoc[nodeId];
25754                 }
25755
25756                 var start, end;
25757                 if (delta) {
25758                     start = projection(cache.startLoc[nodeId]);
25759                     end = projection.invert(geoVecAdd(start, delta));
25760                 } else {
25761                     end = cache.startLoc[nodeId];
25762                 }
25763                 orig = orig.move(end);
25764
25765                 var angle = Math.abs(geoAngle(orig, prev, projection) -
25766                         geoAngle(orig, next, projection)) * 180 / Math.PI;
25767
25768                 // Don't add orig vertex if it would just make a straight line..
25769                 if (angle > 175 && angle < 185) return graph;
25770
25771                 // moving forward or backward along way?
25772                 var p1 = [prev.loc, orig.loc, moved.loc, next.loc].map(projection);
25773                 var p2 = [prev.loc, moved.loc, orig.loc, next.loc].map(projection);
25774                 var d1 = geoPathLength(p1);
25775                 var d2 = geoPathLength(p2);
25776                 var insertAt = (d1 <= d2) ? movedIndex : nextIndex;
25777
25778                 // moving around closed loop?
25779                 if (way.isClosed() && insertAt === 0) insertAt = len;
25780
25781                 way = way.addNode(orig.id, insertAt);
25782                 return graph.replace(orig).replace(way);
25783             }
25784
25785
25786             // Remove duplicate vertex that might have been added by
25787             // replaceMovedVertex.  This is done after the unzorro checks.
25788             function removeDuplicateVertices(wayId, graph) {
25789                 var way = graph.entity(wayId);
25790                 var epsilon = 1e-6;
25791                 var prev, curr;
25792
25793                 function isInteresting(node, graph) {
25794                     return graph.parentWays(node).length > 1 ||
25795                         graph.parentRelations(node).length ||
25796                         node.hasInterestingTags();
25797                 }
25798
25799                 for (var i = 0; i < way.nodes.length; i++) {
25800                     curr = graph.entity(way.nodes[i]);
25801
25802                     if (prev && curr && geoVecEqual(prev.loc, curr.loc, epsilon)) {
25803                         if (!isInteresting(prev, graph)) {
25804                             way = way.removeNode(prev.id);
25805                             graph = graph.replace(way).remove(prev);
25806                         } else if (!isInteresting(curr, graph)) {
25807                             way = way.removeNode(curr.id);
25808                             graph = graph.replace(way).remove(curr);
25809                         }
25810                     }
25811
25812                     prev = curr;
25813                 }
25814
25815                 return graph;
25816             }
25817
25818
25819             // Reorder nodes around intersections that have moved..
25820             //
25821             //  Start:                way1.nodes: b,e         (moving)
25822             //  a - b - c ----- d     way2.nodes: a,b,c,d     (static)
25823             //      |                 vertex: b
25824             //      e                 isEP1: true,  isEP2, false
25825             //
25826             //  way1 `b,e` moved here:
25827             //  a ----- c = b - d
25828             //              |
25829             //              e
25830             //
25831             //  reorder nodes         way1.nodes: b,e
25832             //  a ----- c - b - d     way2.nodes: a,c,b,d
25833             //              |
25834             //              e
25835             //
25836             function unZorroIntersection(intersection, graph) {
25837                 var vertex = graph.entity(intersection.nodeId);
25838                 var way1 = graph.entity(intersection.movedId);
25839                 var way2 = graph.entity(intersection.unmovedId);
25840                 var isEP1 = intersection.movedIsEP;
25841                 var isEP2 = intersection.unmovedIsEP;
25842
25843                 // don't move the vertex if it is the endpoint of both ways.
25844                 if (isEP1 && isEP2) return graph;
25845
25846                 var nodes1 = graph.childNodes(way1).filter(function(n) { return n !== vertex; });
25847                 var nodes2 = graph.childNodes(way2).filter(function(n) { return n !== vertex; });
25848
25849                 if (way1.isClosed() && way1.first() === vertex.id) nodes1.push(nodes1[0]);
25850                 if (way2.isClosed() && way2.first() === vertex.id) nodes2.push(nodes2[0]);
25851
25852                 var edge1 = !isEP1 && geoChooseEdge(nodes1, projection(vertex.loc), projection);
25853                 var edge2 = !isEP2 && geoChooseEdge(nodes2, projection(vertex.loc), projection);
25854                 var loc;
25855
25856                 // snap vertex to nearest edge (or some point between them)..
25857                 if (!isEP1 && !isEP2) {
25858                     var epsilon = 1e-6, maxIter = 10;
25859                     for (var i = 0; i < maxIter; i++) {
25860                         loc = geoVecInterp(edge1.loc, edge2.loc, 0.5);
25861                         edge1 = geoChooseEdge(nodes1, projection(loc), projection);
25862                         edge2 = geoChooseEdge(nodes2, projection(loc), projection);
25863                         if (Math.abs(edge1.distance - edge2.distance) < epsilon) break;
25864                     }
25865                 } else if (!isEP1) {
25866                     loc = edge1.loc;
25867                 } else {
25868                     loc = edge2.loc;
25869                 }
25870
25871                 graph = graph.replace(vertex.move(loc));
25872
25873                 // if zorro happened, reorder nodes..
25874                 if (!isEP1 && edge1.index !== way1.nodes.indexOf(vertex.id)) {
25875                     way1 = way1.removeNode(vertex.id).addNode(vertex.id, edge1.index);
25876                     graph = graph.replace(way1);
25877                 }
25878                 if (!isEP2 && edge2.index !== way2.nodes.indexOf(vertex.id)) {
25879                     way2 = way2.removeNode(vertex.id).addNode(vertex.id, edge2.index);
25880                     graph = graph.replace(way2);
25881                 }
25882
25883                 return graph;
25884             }
25885
25886
25887             function cleanupIntersections(graph) {
25888                 for (var i = 0; i < cache.intersections.length; i++) {
25889                     var obj = cache.intersections[i];
25890                     graph = replaceMovedVertex(obj.nodeId, obj.movedId, graph, _delta);
25891                     graph = replaceMovedVertex(obj.nodeId, obj.unmovedId, graph, null);
25892                     graph = unZorroIntersection(obj, graph);
25893                     graph = removeDuplicateVertices(obj.movedId, graph);
25894                     graph = removeDuplicateVertices(obj.unmovedId, graph);
25895                 }
25896
25897                 return graph;
25898             }
25899
25900
25901             // check if moving way endpoint can cross an unmoved way, if so limit delta..
25902             function limitDelta(graph) {
25903                 function moveNode(loc) {
25904                     return geoVecAdd(projection(loc), _delta);
25905                 }
25906
25907                 for (var i = 0; i < cache.intersections.length; i++) {
25908                     var obj = cache.intersections[i];
25909
25910                     // Don't limit movement if this is vertex joins 2 endpoints..
25911                     if (obj.movedIsEP && obj.unmovedIsEP) continue;
25912                     // Don't limit movement if this vertex is not an endpoint anyway..
25913                     if (!obj.movedIsEP) continue;
25914
25915                     var node = graph.entity(obj.nodeId);
25916                     var start = projection(node.loc);
25917                     var end = geoVecAdd(start, _delta);
25918                     var movedNodes = graph.childNodes(graph.entity(obj.movedId));
25919                     var movedPath = movedNodes.map(function(n) { return moveNode(n.loc); });
25920                     var unmovedNodes = graph.childNodes(graph.entity(obj.unmovedId));
25921                     var unmovedPath = unmovedNodes.map(function(n) { return projection(n.loc); });
25922                     var hits = geoPathIntersections(movedPath, unmovedPath);
25923
25924                     for (var j = 0; i < hits.length; i++) {
25925                         if (geoVecEqual(hits[j], end)) continue;
25926                         var edge = geoChooseEdge(unmovedNodes, end, projection);
25927                         _delta = geoVecSubtract(projection(edge.loc), start);
25928                     }
25929                 }
25930             }
25931
25932
25933             var action = function(graph) {
25934                 if (_delta[0] === 0 && _delta[1] === 0) return graph;
25935
25936                 setupCache(graph);
25937
25938                 if (cache.intersections.length) {
25939                     limitDelta(graph);
25940                 }
25941
25942                 for (var i = 0; i < cache.nodes.length; i++) {
25943                     var node = graph.entity(cache.nodes[i]);
25944                     var start = projection(node.loc);
25945                     var end = geoVecAdd(start, _delta);
25946                     graph = graph.replace(node.move(projection.invert(end)));
25947                 }
25948
25949                 if (cache.intersections.length) {
25950                     graph = cleanupIntersections(graph);
25951                 }
25952
25953                 return graph;
25954             };
25955
25956
25957             action.delta = function() {
25958                 return _delta;
25959             };
25960
25961
25962             return action;
25963         }
25964
25965         function actionMoveMember(relationId, fromIndex, toIndex) {
25966             return function(graph) {
25967                 return graph.replace(graph.entity(relationId).moveMember(fromIndex, toIndex));
25968             };
25969         }
25970
25971         function actionMoveNode(nodeID, toLoc) {
25972
25973             var action = function(graph, t) {
25974                 if (t === null || !isFinite(t)) t = 1;
25975                 t = Math.min(Math.max(+t, 0), 1);
25976
25977                 var node = graph.entity(nodeID);
25978                 return graph.replace(
25979                     node.move(geoVecInterp(node.loc, toLoc, t))
25980                 );
25981             };
25982
25983             action.transitionable = true;
25984
25985             return action;
25986         }
25987
25988         function actionNoop() {
25989             return function(graph) {
25990                 return graph;
25991             };
25992         }
25993
25994         function actionOrthogonalize(wayID, projection, vertexID, degThresh, ep) {
25995             var epsilon = ep || 1e-4;
25996             var threshold = degThresh || 13;  // degrees within right or straight to alter
25997
25998             // We test normalized dot products so we can compare as cos(angle)
25999             var lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180);
26000             var upperThreshold = Math.cos(threshold * Math.PI / 180);
26001
26002
26003             var action = function(graph, t) {
26004                 if (t === null || !isFinite(t)) t = 1;
26005                 t = Math.min(Math.max(+t, 0), 1);
26006
26007                 var way = graph.entity(wayID);
26008                 way = way.removeNode('');   // sanity check - remove any consecutive duplicates
26009
26010                 if (way.tags.nonsquare) {
26011                     var tags = Object.assign({}, way.tags);
26012                     // since we're squaring, remove indication that this is physically unsquare
26013                     delete tags.nonsquare;
26014                     way = way.update({tags: tags});
26015                 }
26016
26017                 graph = graph.replace(way);
26018
26019                 var isClosed = way.isClosed();
26020                 var nodes = graph.childNodes(way).slice();  // shallow copy
26021                 if (isClosed) nodes.pop();
26022
26023                 if (vertexID !== undefined) {
26024                     nodes = nodeSubset(nodes, vertexID, isClosed);
26025                     if (nodes.length !== 3) return graph;
26026                 }
26027
26028                 // note: all geometry functions here use the unclosed node/point/coord list
26029
26030                 var nodeCount = {};
26031                 var points = [];
26032                 var corner = { i: 0, dotp: 1 };
26033                 var node, point, loc, score, motions, i, j;
26034
26035                 for (i = 0; i < nodes.length; i++) {
26036                     node = nodes[i];
26037                     nodeCount[node.id] = (nodeCount[node.id] || 0) + 1;
26038                     points.push({ id: node.id, coord: projection(node.loc) });
26039                 }
26040
26041
26042                 if (points.length === 3) {   // move only one vertex for right triangle
26043                     for (i = 0; i < 1000; i++) {
26044                         motions = points.map(calcMotion);
26045
26046                         points[corner.i].coord = geoVecAdd(points[corner.i].coord, motions[corner.i]);
26047                         score = corner.dotp;
26048                         if (score < epsilon) {
26049                             break;
26050                         }
26051                     }
26052
26053                     node = graph.entity(nodes[corner.i].id);
26054                     loc = projection.invert(points[corner.i].coord);
26055                     graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t)));
26056
26057                 } else {
26058                     var straights = [];
26059                     var simplified = [];
26060
26061                     // Remove points from nearly straight sections..
26062                     // This produces a simplified shape to orthogonalize
26063                     for (i = 0; i < points.length; i++) {
26064                         point = points[i];
26065                         var dotp = 0;
26066                         if (isClosed || (i > 0 && i < points.length - 1)) {
26067                             var a = points[(i - 1 + points.length) % points.length];
26068                             var b = points[(i + 1) % points.length];
26069                             dotp = Math.abs(geoOrthoNormalizedDotProduct(a.coord, b.coord, point.coord));
26070                         }
26071
26072                         if (dotp > upperThreshold) {
26073                             straights.push(point);
26074                         } else {
26075                             simplified.push(point);
26076                         }
26077                     }
26078
26079                     // Orthogonalize the simplified shape
26080                     var bestPoints = clonePoints(simplified);
26081                     var originalPoints = clonePoints(simplified);
26082
26083                     score = Infinity;
26084                     for (i = 0; i < 1000; i++) {
26085                         motions = simplified.map(calcMotion);
26086
26087                         for (j = 0; j < motions.length; j++) {
26088                             simplified[j].coord = geoVecAdd(simplified[j].coord, motions[j]);
26089                         }
26090                         var newScore = geoOrthoCalcScore(simplified, isClosed, epsilon, threshold);
26091                         if (newScore < score) {
26092                             bestPoints = clonePoints(simplified);
26093                             score = newScore;
26094                         }
26095                         if (score < epsilon) {
26096                             break;
26097                         }
26098                     }
26099
26100                     var bestCoords = bestPoints.map(function(p) { return p.coord; });
26101                     if (isClosed) bestCoords.push(bestCoords[0]);
26102
26103                     // move the nodes that should move
26104                     for (i = 0; i < bestPoints.length; i++) {
26105                         point = bestPoints[i];
26106                         if (!geoVecEqual(originalPoints[i].coord, point.coord)) {
26107                             node = graph.entity(point.id);
26108                             loc = projection.invert(point.coord);
26109                             graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t)));
26110                         }
26111                     }
26112
26113                     // move the nodes along straight segments
26114                     for (i = 0; i < straights.length; i++) {
26115                         point = straights[i];
26116                         if (nodeCount[point.id] > 1) continue;   // skip self-intersections
26117
26118                         node = graph.entity(point.id);
26119
26120                         if (t === 1 &&
26121                             graph.parentWays(node).length === 1 &&
26122                             graph.parentRelations(node).length === 0 &&
26123                             !node.hasInterestingTags()
26124                         ) {
26125                             // remove uninteresting points..
26126                             graph = actionDeleteNode(node.id)(graph);
26127
26128                         } else {
26129                             // move interesting points to the nearest edge..
26130                             var choice = geoVecProject(point.coord, bestCoords);
26131                             if (choice) {
26132                                 loc = projection.invert(choice.target);
26133                                 graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t)));
26134                             }
26135                         }
26136                     }
26137                 }
26138
26139                 return graph;
26140
26141
26142                 function clonePoints(array) {
26143                     return array.map(function(p) {
26144                         return { id: p.id, coord: [p.coord[0], p.coord[1]] };
26145                     });
26146                 }
26147
26148
26149                 function calcMotion(point, i, array) {
26150                     // don't try to move the endpoints of a non-closed way.
26151                     if (!isClosed && (i === 0 || i === array.length - 1)) return [0, 0];
26152                     // don't try to move a node that appears more than once (self intersection)
26153                     if (nodeCount[array[i].id] > 1) return [0, 0];
26154
26155                     var a = array[(i - 1 + array.length) % array.length].coord;
26156                     var origin = point.coord;
26157                     var b = array[(i + 1) % array.length].coord;
26158                     var p = geoVecSubtract(a, origin);
26159                     var q = geoVecSubtract(b, origin);
26160
26161                     var scale = 2 * Math.min(geoVecLength(p), geoVecLength(q));
26162                     p = geoVecNormalize(p);
26163                     q = geoVecNormalize(q);
26164
26165                     var dotp = (p[0] * q[0] + p[1] * q[1]);
26166                     var val = Math.abs(dotp);
26167
26168                     if (val < lowerThreshold) {  // nearly orthogonal
26169                         corner.i = i;
26170                         corner.dotp = val;
26171                         var vec = geoVecNormalize(geoVecAdd(p, q));
26172                         return geoVecScale(vec, 0.1 * dotp * scale);
26173                     }
26174
26175                     return [0, 0];   // do nothing
26176                 }
26177             };
26178
26179
26180             // if we are only orthogonalizing one vertex,
26181             // get that vertex and the previous and next
26182             function nodeSubset(nodes, vertexID, isClosed) {
26183                 var first = isClosed ? 0 : 1;
26184                 var last = isClosed ? nodes.length : nodes.length - 1;
26185
26186                 for (var i = first; i < last; i++) {
26187                     if (nodes[i].id === vertexID) {
26188                         return [
26189                             nodes[(i - 1 + nodes.length) % nodes.length],
26190                             nodes[i],
26191                             nodes[(i + 1) % nodes.length]
26192                         ];
26193                     }
26194                 }
26195
26196                 return [];
26197             }
26198
26199
26200             action.disabled = function(graph) {
26201                 var way = graph.entity(wayID);
26202                 way = way.removeNode('');  // sanity check - remove any consecutive duplicates
26203                 graph = graph.replace(way);
26204
26205                 var isClosed = way.isClosed();
26206                 var nodes = graph.childNodes(way).slice();  // shallow copy
26207                 if (isClosed) nodes.pop();
26208
26209                 var allowStraightAngles = false;
26210                 if (vertexID !== undefined) {
26211                     allowStraightAngles = true;
26212                     nodes = nodeSubset(nodes, vertexID, isClosed);
26213                     if (nodes.length !== 3) return 'end_vertex';
26214                 }
26215
26216                 var coords = nodes.map(function(n) { return projection(n.loc); });
26217                 var score = geoOrthoCanOrthogonalize(coords, isClosed, epsilon, threshold, allowStraightAngles);
26218
26219                 if (score === null) {
26220                     return 'not_squarish';
26221                 } else if (score === 0) {
26222                     return 'square_enough';
26223                 } else {
26224                     return false;
26225                 }
26226             };
26227
26228
26229             action.transitionable = true;
26230
26231             return action;
26232         }
26233
26234         // `actionRestrictTurn` creates a turn restriction relation.
26235         //
26236         // `turn` must be an `osmTurn` object
26237         // see osm/intersection.js, pathToTurn()
26238         //
26239         // This specifies a restriction of type `restriction` when traveling from
26240         // `turn.from.way` toward `turn.to.way` via `turn.via.node` OR `turn.via.ways`.
26241         // (The action does not check that these entities form a valid intersection.)
26242         //
26243         // From, to, and via ways should be split before calling this action.
26244         // (old versions of the code would split the ways here, but we no longer do it)
26245         //
26246         // For testing convenience, accepts a restrictionID to assign to the new
26247         // relation. Normally, this will be undefined and the relation will
26248         // automatically be assigned a new ID.
26249         //
26250         function actionRestrictTurn(turn, restrictionType, restrictionID) {
26251
26252             return function(graph) {
26253                 var fromWay = graph.entity(turn.from.way);
26254                 var toWay = graph.entity(turn.to.way);
26255                 var viaNode = turn.via.node && graph.entity(turn.via.node);
26256                 var viaWays = turn.via.ways && turn.via.ways.map(function(id) { return graph.entity(id); });
26257                 var members = [];
26258
26259                 members.push({ id: fromWay.id, type: 'way',  role: 'from' });
26260
26261                 if (viaNode) {
26262                     members.push({ id: viaNode.id,  type: 'node', role: 'via' });
26263                 } else if (viaWays) {
26264                     viaWays.forEach(function(viaWay) {
26265                         members.push({ id: viaWay.id,  type: 'way', role: 'via' });
26266                     });
26267                 }
26268
26269                 members.push({ id: toWay.id, type: 'way',  role: 'to' });
26270
26271                 return graph.replace(osmRelation({
26272                     id: restrictionID,
26273                     tags: {
26274                         type: 'restriction',
26275                         restriction: restrictionType
26276                     },
26277                     members: members
26278                 }));
26279             };
26280         }
26281
26282         function actionRevert(id) {
26283             var action = function(graph) {
26284                 var entity = graph.hasEntity(id),
26285                     base = graph.base().entities[id];
26286
26287                 if (entity && !base) {    // entity will be removed..
26288                     if (entity.type === 'node') {
26289                         graph.parentWays(entity)
26290                             .forEach(function(parent) {
26291                                 parent = parent.removeNode(id);
26292                                 graph = graph.replace(parent);
26293
26294                                 if (parent.isDegenerate()) {
26295                                     graph = actionDeleteWay(parent.id)(graph);
26296                                 }
26297                             });
26298                     }
26299
26300                     graph.parentRelations(entity)
26301                         .forEach(function(parent) {
26302                             parent = parent.removeMembersWithID(id);
26303                             graph = graph.replace(parent);
26304
26305                             if (parent.isDegenerate()) {
26306                                 graph = actionDeleteRelation(parent.id)(graph);
26307                             }
26308                         });
26309                 }
26310
26311                 return graph.revert(id);
26312             };
26313
26314             return action;
26315         }
26316
26317         function actionRotate(rotateIds, pivot, angle, projection) {
26318
26319             var action = function(graph) {
26320                 return graph.update(function(graph) {
26321                     utilGetAllNodes(rotateIds, graph).forEach(function(node) {
26322                         var point = geoRotate([projection(node.loc)], angle, pivot)[0];
26323                         graph = graph.replace(node.move(projection.invert(point)));
26324                     });
26325                 });
26326             };
26327
26328             return action;
26329         }
26330
26331         /* Align nodes along their common axis */
26332         function actionStraightenNodes(nodeIDs, projection) {
26333
26334             function positionAlongWay(a, o, b) {
26335                 return geoVecDot(a, b, o) / geoVecDot(b, b, o);
26336             }
26337
26338             // returns the endpoints of the long axis of symmetry of the `points` bounding rect 
26339             function getEndpoints(points) {
26340                 var ssr = geoGetSmallestSurroundingRectangle(points);
26341
26342                 // Choose line pq = axis of symmetry.
26343                 // The shape's surrounding rectangle has 2 axes of symmetry.
26344                 // Snap points to the long axis
26345                 var p1 = [(ssr.poly[0][0] + ssr.poly[1][0]) / 2, (ssr.poly[0][1] + ssr.poly[1][1]) / 2 ];
26346                 var q1 = [(ssr.poly[2][0] + ssr.poly[3][0]) / 2, (ssr.poly[2][1] + ssr.poly[3][1]) / 2 ];
26347                 var p2 = [(ssr.poly[3][0] + ssr.poly[4][0]) / 2, (ssr.poly[3][1] + ssr.poly[4][1]) / 2 ];
26348                 var q2 = [(ssr.poly[1][0] + ssr.poly[2][0]) / 2, (ssr.poly[1][1] + ssr.poly[2][1]) / 2 ];
26349
26350                 var isLong = (geoVecLength(p1, q1) > geoVecLength(p2, q2));
26351                 if (isLong) {
26352                     return [p1, q1];
26353                 }
26354                 return [p2, q2];
26355             }
26356
26357
26358             var action = function(graph, t) {
26359                 if (t === null || !isFinite(t)) t = 1;
26360                 t = Math.min(Math.max(+t, 0), 1);
26361
26362                 var nodes = nodeIDs.map(function(id) { return graph.entity(id); });
26363                 var points = nodes.map(function(n) { return projection(n.loc); });
26364                 var endpoints = getEndpoints(points);
26365                 var startPoint = endpoints[0];
26366                 var endPoint = endpoints[1];
26367
26368                 // Move points onto the line connecting the endpoints
26369                 for (var i = 0; i < points.length; i++) {
26370                     var node = nodes[i];
26371                     var point = points[i];
26372                     var u = positionAlongWay(point, startPoint, endPoint);
26373                     var point2 = geoVecInterp(startPoint, endPoint, u);
26374                     var loc2 = projection.invert(point2);
26375                     graph = graph.replace(node.move(geoVecInterp(node.loc, loc2, t)));
26376                 }
26377
26378                 return graph;
26379             };
26380
26381
26382             action.disabled = function(graph) {
26383
26384                 var nodes = nodeIDs.map(function(id) { return graph.entity(id); });
26385                 var points = nodes.map(function(n) { return projection(n.loc); });
26386                 var endpoints = getEndpoints(points);
26387                 var startPoint = endpoints[0];
26388                 var endPoint = endpoints[1];
26389
26390                 var maxDistance = 0;
26391
26392                 for (var i = 0; i < points.length; i++) {
26393                     var point = points[i];
26394                     var u = positionAlongWay(point, startPoint, endPoint);
26395                     var p = geoVecInterp(startPoint, endPoint, u);
26396                     var dist = geoVecLength(p, point);
26397
26398                     if (!isNaN(dist) && dist > maxDistance) {
26399                         maxDistance = dist;
26400                     }
26401                 }
26402
26403                 if (maxDistance < 0.0001) {
26404                     return 'straight_enough';
26405                 }
26406             };
26407
26408
26409             action.transitionable = true;
26410
26411
26412             return action;
26413         }
26414
26415         /*
26416          * Based on https://github.com/openstreetmap/potlatch2/net/systemeD/potlatch2/tools/Straighten.as
26417          */
26418         function actionStraightenWay(selectedIDs, projection) {
26419
26420             function positionAlongWay(a, o, b) {
26421                 return geoVecDot(a, b, o) / geoVecDot(b, b, o);
26422             }
26423
26424             // Return all selected ways as a continuous, ordered array of nodes
26425             function allNodes(graph) {
26426                 var nodes = [];
26427                 var startNodes = [];
26428                 var endNodes = [];
26429                 var remainingWays = [];
26430                 var selectedWays = selectedIDs.filter(function(w) {
26431                     return graph.entity(w).type === 'way';
26432                 });
26433                 var selectedNodes = selectedIDs.filter(function(n) {
26434                     return graph.entity(n).type === 'node';
26435                 });
26436
26437                 for (var i = 0; i < selectedWays.length; i++) {
26438                     var way = graph.entity(selectedWays[i]);
26439                     nodes = way.nodes.slice(0);
26440                     remainingWays.push(nodes);
26441                     startNodes.push(nodes[0]);
26442                     endNodes.push(nodes[nodes.length-1]);
26443                 }
26444
26445                 // Remove duplicate end/startNodes (duplicate nodes cannot be at the line end,
26446                 //   and need to be removed so currNode difference calculation below works)
26447                 // i.e. ["n-1", "n-1", "n-2"] => ["n-2"]
26448                 startNodes = startNodes.filter(function(n) {
26449                     return startNodes.indexOf(n) === startNodes.lastIndexOf(n);
26450                 });
26451                 endNodes = endNodes.filter(function(n) {
26452                     return endNodes.indexOf(n) === endNodes.lastIndexOf(n);
26453                 });
26454
26455                 // Choose the initial endpoint to start from
26456                 var currNode = utilArrayDifference(startNodes, endNodes)
26457                     .concat(utilArrayDifference(endNodes, startNodes))[0];
26458                 var nextWay = [];
26459                 nodes = [];
26460
26461                 // Create nested function outside of loop to avoid "function in loop" lint error
26462                 var getNextWay = function(currNode, remainingWays) {
26463                     return remainingWays.filter(function(way) {
26464                         return way[0] === currNode || way[way.length-1] === currNode;
26465                     })[0];
26466                 };
26467
26468                 // Add nodes to end of nodes array, until all ways are added
26469                 while (remainingWays.length) {
26470                     nextWay = getNextWay(currNode, remainingWays);
26471                     remainingWays = utilArrayDifference(remainingWays, [nextWay]);
26472
26473                     if (nextWay[0] !== currNode) {
26474                         nextWay.reverse();
26475                     }
26476                     nodes = nodes.concat(nextWay);
26477                     currNode = nodes[nodes.length-1];
26478                 }
26479
26480                 // If user selected 2 nodes to straighten between, then slice nodes array to those nodes
26481                 if (selectedNodes.length === 2) {
26482                     var startNodeIdx = nodes.indexOf(selectedNodes[0]);
26483                     var endNodeIdx = nodes.indexOf(selectedNodes[1]);
26484                     var sortedStartEnd = [startNodeIdx, endNodeIdx];
26485
26486                     sortedStartEnd.sort(function(a, b) { return a - b; });
26487                     nodes = nodes.slice(sortedStartEnd[0], sortedStartEnd[1]+1);
26488                 }
26489
26490                 return nodes.map(function(n) { return graph.entity(n); });
26491             }
26492
26493             function shouldKeepNode(node, graph) {
26494                 return graph.parentWays(node).length > 1 ||
26495                     graph.parentRelations(node).length ||
26496                     node.hasInterestingTags();
26497             }
26498
26499
26500             var action = function(graph, t) {
26501                 if (t === null || !isFinite(t)) t = 1;
26502                 t = Math.min(Math.max(+t, 0), 1);
26503
26504                 var nodes = allNodes(graph);
26505                 var points = nodes.map(function(n) { return projection(n.loc); });
26506                 var startPoint = points[0];
26507                 var endPoint = points[points.length-1];
26508                 var toDelete = [];
26509                 var i;
26510
26511                 for (i = 1; i < points.length-1; i++) {
26512                     var node = nodes[i];
26513                     var point = points[i];
26514
26515                     if (t < 1 || shouldKeepNode(node, graph)) {
26516                         var u = positionAlongWay(point, startPoint, endPoint);
26517                         var p = geoVecInterp(startPoint, endPoint, u);
26518                         var loc2 = projection.invert(p);
26519                         graph = graph.replace(node.move(geoVecInterp(node.loc, loc2, t)));
26520
26521                     } else {
26522                         // safe to delete
26523                         if (toDelete.indexOf(node) === -1) {
26524                             toDelete.push(node);
26525                         }
26526                     }
26527                 }
26528
26529                 for (i = 0; i < toDelete.length; i++) {
26530                     graph = actionDeleteNode(toDelete[i].id)(graph);
26531                 }
26532
26533                 return graph;
26534             };
26535
26536
26537             action.disabled = function(graph) {
26538                 // check way isn't too bendy
26539                 var nodes = allNodes(graph);
26540                 var points = nodes.map(function(n) { return projection(n.loc); });
26541                 var startPoint = points[0];
26542                 var endPoint = points[points.length-1];
26543                 var threshold = 0.2 * geoVecLength(startPoint, endPoint);
26544                 var i;
26545
26546                 if (threshold === 0) {
26547                     return 'too_bendy';
26548                 }
26549
26550                 var maxDistance = 0;
26551
26552                 for (i = 1; i < points.length - 1; i++) {
26553                     var point = points[i];
26554                     var u = positionAlongWay(point, startPoint, endPoint);
26555                     var p = geoVecInterp(startPoint, endPoint, u);
26556                     var dist = geoVecLength(p, point);
26557
26558                     // to bendy if point is off by 20% of total start/end distance in projected space
26559                     if (isNaN(dist) || dist > threshold) {
26560                         return 'too_bendy';
26561                     } else if (dist > maxDistance) {
26562                         maxDistance = dist;
26563                     }
26564                 }
26565
26566                 var keepingAllNodes = nodes.every(function(node, i) {
26567                     return i === 0 || i === nodes.length - 1 || shouldKeepNode(node, graph);
26568                 });
26569
26570                 if (maxDistance < 0.0001 &&
26571                     // Allow straightening even if already straight in order to remove extraneous nodes
26572                     keepingAllNodes) {
26573                     return 'straight_enough';
26574                 }
26575             };
26576
26577             action.transitionable = true;
26578
26579
26580             return action;
26581         }
26582
26583         // `actionUnrestrictTurn` deletes a turn restriction relation.
26584         //
26585         // `turn` must be an `osmTurn` object with a `restrictionID` property.
26586         // see osm/intersection.js, pathToTurn()
26587         //
26588         function actionUnrestrictTurn(turn) {
26589             return function(graph) {
26590                 return actionDeleteRelation(turn.restrictionID)(graph);
26591             };
26592         }
26593
26594         /* Reflect the given area around its axis of symmetry */
26595         function actionReflect(reflectIds, projection) {
26596             var _useLongAxis = true;
26597
26598
26599             var action = function(graph, t) {
26600                 if (t === null || !isFinite(t)) t = 1;
26601                 t = Math.min(Math.max(+t, 0), 1);
26602
26603                 var nodes = utilGetAllNodes(reflectIds, graph);
26604                 var points = nodes.map(function(n) { return projection(n.loc); });
26605                 var ssr = geoGetSmallestSurroundingRectangle(points);
26606
26607                 // Choose line pq = axis of symmetry.
26608                 // The shape's surrounding rectangle has 2 axes of symmetry.
26609                 // Reflect across the longer axis by default.
26610                 var p1 = [(ssr.poly[0][0] + ssr.poly[1][0]) / 2, (ssr.poly[0][1] + ssr.poly[1][1]) / 2 ];
26611                 var q1 = [(ssr.poly[2][0] + ssr.poly[3][0]) / 2, (ssr.poly[2][1] + ssr.poly[3][1]) / 2 ];
26612                 var p2 = [(ssr.poly[3][0] + ssr.poly[4][0]) / 2, (ssr.poly[3][1] + ssr.poly[4][1]) / 2 ];
26613                 var q2 = [(ssr.poly[1][0] + ssr.poly[2][0]) / 2, (ssr.poly[1][1] + ssr.poly[2][1]) / 2 ];
26614                 var p, q;
26615
26616                 var isLong = (geoVecLength(p1, q1) > geoVecLength(p2, q2));
26617                 if ((_useLongAxis && isLong) || (!_useLongAxis && !isLong)) {
26618                     p = p1;
26619                     q = q1;
26620                 } else {
26621                     p = p2;
26622                     q = q2;
26623                 }
26624
26625                 // reflect c across pq
26626                 // http://math.stackexchange.com/questions/65503/point-reflection-over-a-line
26627                 var dx = q[0] - p[0];
26628                 var dy = q[1] - p[1];
26629                 var a = (dx * dx - dy * dy) / (dx * dx + dy * dy);
26630                 var b = 2 * dx * dy / (dx * dx + dy * dy);
26631                 for (var i = 0; i < nodes.length; i++) {
26632                     var node = nodes[i];
26633                     var c = projection(node.loc);
26634                     var c2 = [
26635                         a * (c[0] - p[0]) + b * (c[1] - p[1]) + p[0],
26636                         b * (c[0] - p[0]) - a * (c[1] - p[1]) + p[1]
26637                     ];
26638                     var loc2 = projection.invert(c2);
26639                     node = node.move(geoVecInterp(node.loc, loc2, t));
26640                     graph = graph.replace(node);
26641                 }
26642
26643                 return graph;
26644             };
26645
26646
26647             action.useLongAxis = function(val) {
26648                 if (!arguments.length) return _useLongAxis;
26649                 _useLongAxis = val;
26650                 return action;
26651             };
26652
26653
26654             action.transitionable = true;
26655
26656
26657             return action;
26658         }
26659
26660         function actionUpgradeTags(entityId, oldTags, replaceTags) {
26661
26662             return function(graph) {
26663                 var entity = graph.entity(entityId);
26664                 var tags = Object.assign({}, entity.tags);  // shallow copy
26665                 var transferValue;
26666                 var semiIndex;
26667
26668                 for (var oldTagKey in oldTags) {
26669                     if (oldTags[oldTagKey] === '*') {
26670                         transferValue = tags[oldTagKey];
26671                         delete tags[oldTagKey];
26672                     } else {
26673                         var vals = tags[oldTagKey].split(';').filter(Boolean);
26674                         var oldIndex = vals.indexOf(oldTags[oldTagKey]);
26675                         if (vals.length === 1 || oldIndex === -1) {
26676                             delete tags[oldTagKey];
26677                         } else {
26678                             if (replaceTags && replaceTags[oldTagKey]) {
26679                                 // replacing a value within a semicolon-delimited value, note the index
26680                                 semiIndex = oldIndex;
26681                             }
26682                             vals.splice(oldIndex, 1);
26683                             tags[oldTagKey] = vals.join(';');
26684                         }
26685                     }
26686                 }
26687
26688                 if (replaceTags) {
26689                     for (var replaceKey in replaceTags) {
26690                         var replaceValue = replaceTags[replaceKey];
26691                         if (replaceValue === '*') {
26692                             if (tags[replaceKey] && tags[replaceKey] !== 'no') {
26693                                 // allow any pre-existing value except `no` (troll tag)
26694                                 continue;
26695                             } else {
26696                                 // otherwise assume `yes` is okay
26697                                 tags[replaceKey] = 'yes';
26698                             }
26699                         } else if (replaceValue === '$1') {
26700                             tags[replaceKey] = transferValue;
26701                         } else {
26702                             if (tags[replaceKey] && oldTags[replaceKey] && semiIndex !== undefined) {
26703                                 // don't override preexisting values
26704                                 var existingVals = tags[replaceKey].split(';').filter(Boolean);
26705                                 if (existingVals.indexOf(replaceValue) === -1) {
26706                                     existingVals.splice(semiIndex, 0, replaceValue);
26707                                     tags[replaceKey] = existingVals.join(';');
26708                                 }
26709                             } else {
26710                                 tags[replaceKey] = replaceValue;
26711                             }
26712                         }
26713                     }
26714                 }
26715
26716                 return graph.replace(entity.update({ tags: tags }));
26717             };
26718         }
26719
26720         function behaviorEdit(context) {
26721
26722             function behavior() {
26723                 context.map()
26724                     .minzoom(context.minEditableZoom());
26725             }
26726
26727
26728             behavior.off = function() {
26729                 context.map()
26730                     .minzoom(0);
26731             };
26732
26733             return behavior;
26734         }
26735
26736         /*
26737            The hover behavior adds the `.hover` class on pointerover to all elements to which
26738            the identical datum is bound, and removes it on pointerout.
26739
26740            The :hover pseudo-class is insufficient for iD's purposes because a datum's visual
26741            representation may consist of several elements scattered throughout the DOM hierarchy.
26742            Only one of these elements can have the :hover pseudo-class, but all of them will
26743            have the .hover class.
26744          */
26745         function behaviorHover(context) {
26746             var dispatch$1 = dispatch('hover');
26747             var _selection = select(null);
26748             var _newNodeId = null;
26749             var _initialNodeID = null;
26750             var _altDisables;
26751             var _ignoreVertex;
26752             var _targets = [];
26753
26754             // use pointer events on supported platforms; fallback to mouse events
26755             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
26756
26757
26758             function keydown() {
26759                 if (_altDisables && event.keyCode === utilKeybinding.modifierCodes.alt) {
26760                     _selection.selectAll('.hover')
26761                         .classed('hover-suppressed', true)
26762                         .classed('hover', false);
26763
26764                     _selection
26765                         .classed('hover-disabled', true);
26766
26767                     dispatch$1.call('hover', this, null);
26768                 }
26769             }
26770
26771
26772             function keyup() {
26773                 if (_altDisables && event.keyCode === utilKeybinding.modifierCodes.alt) {
26774                     _selection.selectAll('.hover-suppressed')
26775                         .classed('hover-suppressed', false)
26776                         .classed('hover', true);
26777
26778                     _selection
26779                         .classed('hover-disabled', false);
26780
26781                     dispatch$1.call('hover', this, _targets);
26782                 }
26783             }
26784
26785
26786             function behavior(selection) {
26787                 _selection = selection;
26788
26789                 _targets = [];
26790
26791                 if (_initialNodeID) {
26792                     _newNodeId = _initialNodeID;
26793                     _initialNodeID = null;
26794                 } else {
26795                     _newNodeId = null;
26796                 }
26797
26798                 _selection
26799                     .on(_pointerPrefix + 'over.hover', pointerover)
26800                     .on(_pointerPrefix + 'out.hover', pointerout)
26801                     // treat pointerdown as pointerover for touch devices
26802                     .on(_pointerPrefix + 'down.hover', pointerover);
26803
26804                 select(window)
26805                     .on(_pointerPrefix + 'up.hover pointercancel.hover', pointerout, true)
26806                     .on('keydown.hover', keydown)
26807                     .on('keyup.hover', keyup);
26808
26809
26810                 function eventTarget() {
26811                     var datum = event.target && event.target.__data__;
26812                     if (typeof datum !== 'object') return null;
26813                     if (!(datum instanceof osmEntity) && datum.properties && (datum.properties.entity instanceof osmEntity)) {
26814                         return datum.properties.entity;
26815                     }
26816                     return datum;
26817                 }
26818
26819                 function pointerover() {
26820                     // ignore mouse hovers with buttons pressed unless dragging
26821                     if (context.mode().id.indexOf('drag') === -1 &&
26822                         (!event.pointerType || event.pointerType === 'mouse') &&
26823                         event.buttons) return;
26824
26825                     var target = eventTarget();
26826                     if (target && _targets.indexOf(target) === -1) {
26827                         _targets.push(target);
26828                         updateHover(_targets);
26829                     }
26830                 }
26831
26832                 function pointerout() {
26833
26834                     var target = eventTarget();
26835                     var index = _targets.indexOf(target);
26836                     if (index !== -1) {
26837                         _targets.splice(index);
26838                         updateHover(_targets);
26839                     }
26840                 }
26841
26842                 function allowsVertex(d) {
26843                     return d.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(d, context.graph());
26844                 }
26845
26846                 function modeAllowsHover(target) {
26847                     var mode = context.mode();
26848                     if (mode.id === 'add-point') {
26849                         return mode.preset.matchGeometry('vertex') ||
26850                             (target.type !== 'way' && target.geometry(context.graph()) !== 'vertex');
26851                     }
26852                     return true;
26853                 }
26854
26855                 function updateHover(targets) {
26856
26857                     _selection.selectAll('.hover')
26858                         .classed('hover', false);
26859                     _selection.selectAll('.hover-suppressed')
26860                         .classed('hover-suppressed', false);
26861
26862                     var mode = context.mode();
26863
26864                     if (!_newNodeId && (mode.id === 'draw-line' || mode.id === 'draw-area')) {
26865                         var node = targets.find(function(target) {
26866                             return target instanceof osmEntity && target.type === 'node';
26867                         });
26868                         _newNodeId = node && node.id;
26869                     }
26870
26871                     targets = targets.filter(function(datum) {
26872                         if (datum instanceof osmEntity) {
26873                             // If drawing a way, don't hover on a node that was just placed. #3974
26874                             return datum.id !== _newNodeId &&
26875                                 (datum.type !== 'node' || !_ignoreVertex || allowsVertex(datum)) &&
26876                                 modeAllowsHover(datum);
26877                         }
26878                         return true;
26879                     });
26880
26881                     var selector = '';
26882
26883                     for (var i in targets) {
26884                         var datum = targets[i];
26885
26886                         // What are we hovering over?
26887                         if (datum.__featurehash__) {
26888                             // hovering custom data
26889                             selector += ', .data' + datum.__featurehash__;
26890
26891                         } else if (datum instanceof QAItem) {
26892                             selector += ', .' + datum.service + '.itemId-' + datum.id;
26893
26894                         } else if (datum instanceof osmNote) {
26895                             selector += ', .note-' + datum.id;
26896
26897                         } else if (datum instanceof osmEntity) {
26898                             selector += ', .' + datum.id;
26899                             if (datum.type === 'relation') {
26900                                 for (var j in datum.members) {
26901                                     selector += ', .' + datum.members[j].id;
26902                                 }
26903                             }
26904                         }
26905                     }
26906
26907                     var suppressed = _altDisables && event && event.altKey;
26908
26909                     if (selector.trim().length) {
26910                         // remove the first comma
26911                         selector = selector.slice(1);
26912                         _selection.selectAll(selector)
26913                             .classed(suppressed ? 'hover-suppressed' : 'hover', true);
26914                     }
26915
26916                     dispatch$1.call('hover', this, !suppressed && targets);
26917                 }
26918             }
26919
26920
26921             behavior.off = function(selection) {
26922                 selection.selectAll('.hover')
26923                     .classed('hover', false);
26924                 selection.selectAll('.hover-suppressed')
26925                     .classed('hover-suppressed', false);
26926                 selection
26927                     .classed('hover-disabled', false);
26928
26929                 selection
26930                     .on(_pointerPrefix + 'over.hover', null)
26931                     .on(_pointerPrefix + 'out.hover', null)
26932                     .on(_pointerPrefix + 'down.hover', null);
26933
26934                 select(window)
26935                     .on(_pointerPrefix + 'up.hover pointercancel.hover', null, true)
26936                     .on('keydown.hover', null)
26937                     .on('keyup.hover', null);
26938             };
26939
26940
26941             behavior.altDisables = function(val) {
26942                 if (!arguments.length) return _altDisables;
26943                 _altDisables = val;
26944                 return behavior;
26945             };
26946
26947             behavior.ignoreVertex = function(val) {
26948                 if (!arguments.length) return _ignoreVertex;
26949                 _ignoreVertex = val;
26950                 return behavior;
26951             };
26952
26953             behavior.initialNodeID = function(nodeId) {
26954                 _initialNodeID = nodeId;
26955                 return behavior;
26956             };
26957
26958             return utilRebind(behavior, dispatch$1, 'on');
26959         }
26960
26961         var _disableSpace = false;
26962         var _lastSpace = null;
26963
26964
26965         function behaviorDraw(context) {
26966             var dispatch$1 = dispatch(
26967                 'move', 'down', 'downcancel', 'click', 'clickWay', 'clickNode', 'undo', 'cancel', 'finish'
26968             );
26969
26970             var keybinding = utilKeybinding('draw');
26971
26972             var _hover = behaviorHover(context)
26973                 .altDisables(true)
26974                 .ignoreVertex(true)
26975                 .on('hover', context.ui().sidebar.hover);
26976             var _edit = behaviorEdit(context);
26977
26978             var _closeTolerance = 4;
26979             var _tolerance = 12;
26980             var _mouseLeave = false;
26981             var _lastMouse = null;
26982             var _lastPointerUpEvent;
26983
26984             var _downPointer;
26985
26986             // use pointer events on supported platforms; fallback to mouse events
26987             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
26988
26989
26990             // related code
26991             // - `mode/drag_node.js` `datum()`
26992             function datum() {
26993                 var mode = context.mode();
26994                 var isNote = mode && (mode.id.indexOf('note') !== -1);
26995                 if (event.altKey || isNote) return {};
26996
26997                 var element;
26998                 if (event.type === 'keydown') {
26999                     element = _lastMouse && _lastMouse.target;
27000                 } else {
27001                     element = event.target;
27002                 }
27003
27004                 // When drawing, snap only to touch targets..
27005                 // (this excludes area fills and active drawing elements)
27006                 var d = element.__data__;
27007                 return (d && d.properties && d.properties.target) ? d : {};
27008             }
27009
27010             function pointerdown() {
27011
27012                 if (_downPointer) return;
27013
27014                 var pointerLocGetter = utilFastMouse(this);
27015                 _downPointer = {
27016                     id: event.pointerId || 'mouse',
27017                     pointerLocGetter: pointerLocGetter,
27018                     downTime: +new Date(),
27019                     downLoc: pointerLocGetter(event)
27020                 };
27021
27022                 dispatch$1.call('down', this, datum());
27023             }
27024
27025             function pointerup() {
27026
27027                 if (!_downPointer || _downPointer.id !== (event.pointerId || 'mouse')) return;
27028
27029                 var downPointer = _downPointer;
27030                 _downPointer = null;
27031
27032                 _lastPointerUpEvent = event;
27033
27034                 if (downPointer.isCancelled) return;
27035
27036                 var t2 = +new Date();
27037                 var p2 = downPointer.pointerLocGetter(event);
27038                 var dist = geoVecLength(downPointer.downLoc, p2);
27039
27040                 if (dist < _closeTolerance || (dist < _tolerance && (t2 - downPointer.downTime) < 500)) {
27041                     // Prevent a quick second click
27042                     select(window).on('click.draw-block', function() {
27043                         event.stopPropagation();
27044                     }, true);
27045
27046                     context.map().dblclickZoomEnable(false);
27047
27048                     window.setTimeout(function() {
27049                         context.map().dblclickZoomEnable(true);
27050                         select(window).on('click.draw-block', null);
27051                     }, 500);
27052
27053                     click(p2);
27054                 }
27055             }
27056
27057             function pointermove() {
27058                 if (_downPointer &&
27059                     _downPointer.id === (event.pointerId || 'mouse') &&
27060                     !_downPointer.isCancelled) {
27061                     var p2 = _downPointer.pointerLocGetter(event);
27062                     var dist = geoVecLength(_downPointer.downLoc, p2);
27063                     if (dist >= _closeTolerance) {
27064                         _downPointer.isCancelled = true;
27065                         dispatch$1.call('downcancel', this);
27066                     }
27067                 }
27068
27069                 if ((event.pointerType && event.pointerType !== 'mouse') ||
27070                     event.buttons ||
27071                     _downPointer) return;
27072
27073                 // HACK: Mobile Safari likes to send one or more `mouse` type pointermove
27074                 // events immediately after non-mouse pointerup events; detect and ignore them.
27075                 if (_lastPointerUpEvent &&
27076                     _lastPointerUpEvent.pointerType !== 'mouse' &&
27077                     event.timeStamp - _lastPointerUpEvent.timeStamp < 100) return;
27078
27079                 _lastMouse = event;
27080                 dispatch$1.call('move', this, datum());
27081             }
27082
27083             function pointercancel() {
27084                 if (_downPointer &&
27085                     _downPointer.id === (event.pointerId || 'mouse')) {
27086
27087                     if (!_downPointer.isCancelled) {
27088                         dispatch$1.call('downcancel', this);
27089                     }
27090                     _downPointer = null;
27091                 }
27092             }
27093
27094             function mouseenter() {
27095                 _mouseLeave = false;
27096             }
27097
27098             function mouseleave() {
27099                 _mouseLeave = true;
27100             }
27101
27102             function allowsVertex(d) {
27103                 return d.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(d, context.graph());
27104             }
27105
27106             // related code
27107             // - `mode/drag_node.js`     `doMove()`
27108             // - `behavior/draw.js`      `click()`
27109             // - `behavior/draw_way.js`  `move()`
27110             function click(loc) {
27111                 var d = datum();
27112                 var target = d && d.properties && d.properties.entity;
27113
27114                 var mode = context.mode();
27115
27116                 if (target && target.type === 'node' && allowsVertex(target)) {   // Snap to a node
27117                     dispatch$1.call('clickNode', this, target, d);
27118                     return;
27119
27120                 } else if (target && target.type === 'way' && (mode.id !== 'add-point' || mode.preset.matchGeometry('vertex'))) {   // Snap to a way
27121                     var choice = geoChooseEdge(
27122                         context.graph().childNodes(target), loc, context.projection, context.activeID()
27123                     );
27124                     if (choice) {
27125                         var edge = [target.nodes[choice.index - 1], target.nodes[choice.index]];
27126                         dispatch$1.call('clickWay', this, choice.loc, edge, d);
27127                         return;
27128                     }
27129                 } else if (mode.id !== 'add-point' || mode.preset.matchGeometry('point')) {
27130                     var locLatLng = context.projection.invert(loc);
27131                     dispatch$1.call('click', this, locLatLng, d);
27132                 }
27133
27134             }
27135
27136             // treat a spacebar press like a click
27137             function space() {
27138                 event.preventDefault();
27139                 event.stopPropagation();
27140
27141                 var currSpace = context.map().mouse();
27142                 if (_disableSpace && _lastSpace) {
27143                     var dist = geoVecLength(_lastSpace, currSpace);
27144                     if (dist > _tolerance) {
27145                         _disableSpace = false;
27146                     }
27147                 }
27148
27149                 if (_disableSpace || _mouseLeave || !_lastMouse) return;
27150
27151                 // user must move mouse or release space bar to allow another click
27152                 _lastSpace = currSpace;
27153                 _disableSpace = true;
27154
27155                 select(window).on('keyup.space-block', function() {
27156                     event.preventDefault();
27157                     event.stopPropagation();
27158                     _disableSpace = false;
27159                     select(window).on('keyup.space-block', null);
27160                 });
27161
27162                 // get the current mouse position
27163                 var loc = context.map().mouse() ||
27164                     // or the map center if the mouse has never entered the map
27165                     context.projection(context.map().center());
27166                 click(loc);
27167             }
27168
27169
27170             function backspace() {
27171                 event.preventDefault();
27172                 dispatch$1.call('undo');
27173             }
27174
27175
27176             function del() {
27177                 event.preventDefault();
27178                 dispatch$1.call('cancel');
27179             }
27180
27181
27182             function ret() {
27183                 event.preventDefault();
27184                 dispatch$1.call('finish');
27185             }
27186
27187
27188             function behavior(selection) {
27189                 context.install(_hover);
27190                 context.install(_edit);
27191
27192                 _downPointer = null;
27193
27194                 keybinding
27195                     .on('⌫', backspace)
27196                     .on('⌦', del)
27197                     .on('⎋', ret)
27198                     .on('↩', ret)
27199                     .on('space', space)
27200                     .on('⌥space', space);
27201
27202                 selection
27203                     .on('mouseenter.draw', mouseenter)
27204                     .on('mouseleave.draw', mouseleave)
27205                     .on(_pointerPrefix + 'down.draw', pointerdown)
27206                     .on(_pointerPrefix + 'move.draw', pointermove);
27207
27208                 select(window)
27209                     .on(_pointerPrefix + 'up.draw', pointerup, true)
27210                     .on('pointercancel.draw', pointercancel, true);
27211
27212                 select(document)
27213                     .call(keybinding);
27214
27215                 return behavior;
27216             }
27217
27218
27219             behavior.off = function(selection) {
27220                 context.ui().sidebar.hover.cancel();
27221                 context.uninstall(_hover);
27222                 context.uninstall(_edit);
27223
27224                 selection
27225                     .on('mouseenter.draw', null)
27226                     .on('mouseleave.draw', null)
27227                     .on(_pointerPrefix + 'down.draw', null)
27228                     .on(_pointerPrefix + 'move.draw', null);
27229
27230                 select(window)
27231                     .on(_pointerPrefix + 'up.draw', null)
27232                     .on('pointercancel.draw', null);
27233                     // note: keyup.space-block, click.draw-block should remain
27234
27235                 select(document)
27236                     .call(keybinding.unbind);
27237             };
27238
27239
27240             behavior.hover = function() {
27241                 return _hover;
27242             };
27243
27244
27245             return utilRebind(behavior, dispatch$1, 'on');
27246         }
27247
27248         function initRange(domain, range) {
27249           switch (arguments.length) {
27250             case 0: break;
27251             case 1: this.range(domain); break;
27252             default: this.range(range).domain(domain); break;
27253           }
27254           return this;
27255         }
27256
27257         var prefix = "$";
27258
27259         function Map$1() {}
27260
27261         Map$1.prototype = map$2.prototype = {
27262           constructor: Map$1,
27263           has: function(key) {
27264             return (prefix + key) in this;
27265           },
27266           get: function(key) {
27267             return this[prefix + key];
27268           },
27269           set: function(key, value) {
27270             this[prefix + key] = value;
27271             return this;
27272           },
27273           remove: function(key) {
27274             var property = prefix + key;
27275             return property in this && delete this[property];
27276           },
27277           clear: function() {
27278             for (var property in this) if (property[0] === prefix) delete this[property];
27279           },
27280           keys: function() {
27281             var keys = [];
27282             for (var property in this) if (property[0] === prefix) keys.push(property.slice(1));
27283             return keys;
27284           },
27285           values: function() {
27286             var values = [];
27287             for (var property in this) if (property[0] === prefix) values.push(this[property]);
27288             return values;
27289           },
27290           entries: function() {
27291             var entries = [];
27292             for (var property in this) if (property[0] === prefix) entries.push({key: property.slice(1), value: this[property]});
27293             return entries;
27294           },
27295           size: function() {
27296             var size = 0;
27297             for (var property in this) if (property[0] === prefix) ++size;
27298             return size;
27299           },
27300           empty: function() {
27301             for (var property in this) if (property[0] === prefix) return false;
27302             return true;
27303           },
27304           each: function(f) {
27305             for (var property in this) if (property[0] === prefix) f(this[property], property.slice(1), this);
27306           }
27307         };
27308
27309         function map$2(object, f) {
27310           var map = new Map$1;
27311
27312           // Copy constructor.
27313           if (object instanceof Map$1) object.each(function(value, key) { map.set(key, value); });
27314
27315           // Index array by numeric index or specified key function.
27316           else if (Array.isArray(object)) {
27317             var i = -1,
27318                 n = object.length,
27319                 o;
27320
27321             if (f == null) while (++i < n) map.set(i, object[i]);
27322             else while (++i < n) map.set(f(o = object[i], i, object), o);
27323           }
27324
27325           // Convert object to map.
27326           else if (object) for (var key in object) map.set(key, object[key]);
27327
27328           return map;
27329         }
27330
27331         function Set$1() {}
27332
27333         var proto = map$2.prototype;
27334
27335         Set$1.prototype = set$2.prototype = {
27336           constructor: Set$1,
27337           has: proto.has,
27338           add: function(value) {
27339             value += "";
27340             this[prefix + value] = value;
27341             return this;
27342           },
27343           remove: proto.remove,
27344           clear: proto.clear,
27345           values: proto.keys,
27346           size: proto.size,
27347           empty: proto.empty,
27348           each: proto.each
27349         };
27350
27351         function set$2(object, f) {
27352           var set = new Set$1;
27353
27354           // Copy constructor.
27355           if (object instanceof Set$1) object.each(function(value) { set.add(value); });
27356
27357           // Otherwise, assume it’s an array.
27358           else if (object) {
27359             var i = -1, n = object.length;
27360             if (f == null) while (++i < n) set.add(object[i]);
27361             else while (++i < n) set.add(f(object[i], i, object));
27362           }
27363
27364           return set;
27365         }
27366
27367         var array$1 = Array.prototype;
27368
27369         var map$3 = array$1.map;
27370         var slice$4 = array$1.slice;
27371
27372         function constant$4(x) {
27373           return function() {
27374             return x;
27375           };
27376         }
27377
27378         function number$1(x) {
27379           return +x;
27380         }
27381
27382         var unit = [0, 1];
27383
27384         function identity$3(x) {
27385           return x;
27386         }
27387
27388         function normalize(a, b) {
27389           return (b -= (a = +a))
27390               ? function(x) { return (x - a) / b; }
27391               : constant$4(isNaN(b) ? NaN : 0.5);
27392         }
27393
27394         function clamper(domain) {
27395           var a = domain[0], b = domain[domain.length - 1], t;
27396           if (a > b) t = a, a = b, b = t;
27397           return function(x) { return Math.max(a, Math.min(b, x)); };
27398         }
27399
27400         // normalize(a, b)(x) takes a domain value x in [a,b] and returns the corresponding parameter t in [0,1].
27401         // interpolate(a, b)(t) takes a parameter t in [0,1] and returns the corresponding range value x in [a,b].
27402         function bimap(domain, range, interpolate) {
27403           var d0 = domain[0], d1 = domain[1], r0 = range[0], r1 = range[1];
27404           if (d1 < d0) d0 = normalize(d1, d0), r0 = interpolate(r1, r0);
27405           else d0 = normalize(d0, d1), r0 = interpolate(r0, r1);
27406           return function(x) { return r0(d0(x)); };
27407         }
27408
27409         function polymap(domain, range, interpolate) {
27410           var j = Math.min(domain.length, range.length) - 1,
27411               d = new Array(j),
27412               r = new Array(j),
27413               i = -1;
27414
27415           // Reverse descending domains.
27416           if (domain[j] < domain[0]) {
27417             domain = domain.slice().reverse();
27418             range = range.slice().reverse();
27419           }
27420
27421           while (++i < j) {
27422             d[i] = normalize(domain[i], domain[i + 1]);
27423             r[i] = interpolate(range[i], range[i + 1]);
27424           }
27425
27426           return function(x) {
27427             var i = bisectRight(domain, x, 1, j) - 1;
27428             return r[i](d[i](x));
27429           };
27430         }
27431
27432         function copy$1(source, target) {
27433           return target
27434               .domain(source.domain())
27435               .range(source.range())
27436               .interpolate(source.interpolate())
27437               .clamp(source.clamp())
27438               .unknown(source.unknown());
27439         }
27440
27441         function transformer$1() {
27442           var domain = unit,
27443               range = unit,
27444               interpolate$1 = interpolate,
27445               transform,
27446               untransform,
27447               unknown,
27448               clamp = identity$3,
27449               piecewise,
27450               output,
27451               input;
27452
27453           function rescale() {
27454             piecewise = Math.min(domain.length, range.length) > 2 ? polymap : bimap;
27455             output = input = null;
27456             return scale;
27457           }
27458
27459           function scale(x) {
27460             return isNaN(x = +x) ? unknown : (output || (output = piecewise(domain.map(transform), range, interpolate$1)))(transform(clamp(x)));
27461           }
27462
27463           scale.invert = function(y) {
27464             return clamp(untransform((input || (input = piecewise(range, domain.map(transform), d3_interpolateNumber)))(y)));
27465           };
27466
27467           scale.domain = function(_) {
27468             return arguments.length ? (domain = map$3.call(_, number$1), clamp === identity$3 || (clamp = clamper(domain)), rescale()) : domain.slice();
27469           };
27470
27471           scale.range = function(_) {
27472             return arguments.length ? (range = slice$4.call(_), rescale()) : range.slice();
27473           };
27474
27475           scale.rangeRound = function(_) {
27476             return range = slice$4.call(_), interpolate$1 = interpolateRound, rescale();
27477           };
27478
27479           scale.clamp = function(_) {
27480             return arguments.length ? (clamp = _ ? clamper(domain) : identity$3, scale) : clamp !== identity$3;
27481           };
27482
27483           scale.interpolate = function(_) {
27484             return arguments.length ? (interpolate$1 = _, rescale()) : interpolate$1;
27485           };
27486
27487           scale.unknown = function(_) {
27488             return arguments.length ? (unknown = _, scale) : unknown;
27489           };
27490
27491           return function(t, u) {
27492             transform = t, untransform = u;
27493             return rescale();
27494           };
27495         }
27496
27497         function continuous(transform, untransform) {
27498           return transformer$1()(transform, untransform);
27499         }
27500
27501         // Computes the decimal coefficient and exponent of the specified number x with
27502         // significant digits p, where x is positive and p is in [1, 21] or undefined.
27503         // For example, formatDecimal(1.23) returns ["123", 0].
27504         function formatDecimal(x, p) {
27505           if ((i = (x = p ? x.toExponential(p - 1) : x.toExponential()).indexOf("e")) < 0) return null; // NaN, ±Infinity
27506           var i, coefficient = x.slice(0, i);
27507
27508           // The string returned by toExponential either has the form \d\.\d+e[-+]\d+
27509           // (e.g., 1.2e+3) or the form \de[-+]\d+ (e.g., 1e+3).
27510           return [
27511             coefficient.length > 1 ? coefficient[0] + coefficient.slice(2) : coefficient,
27512             +x.slice(i + 1)
27513           ];
27514         }
27515
27516         function exponent(x) {
27517           return x = formatDecimal(Math.abs(x)), x ? x[1] : NaN;
27518         }
27519
27520         function formatGroup(grouping, thousands) {
27521           return function(value, width) {
27522             var i = value.length,
27523                 t = [],
27524                 j = 0,
27525                 g = grouping[0],
27526                 length = 0;
27527
27528             while (i > 0 && g > 0) {
27529               if (length + g + 1 > width) g = Math.max(1, width - length);
27530               t.push(value.substring(i -= g, i + g));
27531               if ((length += g + 1) > width) break;
27532               g = grouping[j = (j + 1) % grouping.length];
27533             }
27534
27535             return t.reverse().join(thousands);
27536           };
27537         }
27538
27539         function formatNumerals(numerals) {
27540           return function(value) {
27541             return value.replace(/[0-9]/g, function(i) {
27542               return numerals[+i];
27543             });
27544           };
27545         }
27546
27547         // [[fill]align][sign][symbol][0][width][,][.precision][~][type]
27548         var re = /^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;
27549
27550         function formatSpecifier(specifier) {
27551           if (!(match = re.exec(specifier))) throw new Error("invalid format: " + specifier);
27552           var match;
27553           return new FormatSpecifier({
27554             fill: match[1],
27555             align: match[2],
27556             sign: match[3],
27557             symbol: match[4],
27558             zero: match[5],
27559             width: match[6],
27560             comma: match[7],
27561             precision: match[8] && match[8].slice(1),
27562             trim: match[9],
27563             type: match[10]
27564           });
27565         }
27566
27567         formatSpecifier.prototype = FormatSpecifier.prototype; // instanceof
27568
27569         function FormatSpecifier(specifier) {
27570           this.fill = specifier.fill === undefined ? " " : specifier.fill + "";
27571           this.align = specifier.align === undefined ? ">" : specifier.align + "";
27572           this.sign = specifier.sign === undefined ? "-" : specifier.sign + "";
27573           this.symbol = specifier.symbol === undefined ? "" : specifier.symbol + "";
27574           this.zero = !!specifier.zero;
27575           this.width = specifier.width === undefined ? undefined : +specifier.width;
27576           this.comma = !!specifier.comma;
27577           this.precision = specifier.precision === undefined ? undefined : +specifier.precision;
27578           this.trim = !!specifier.trim;
27579           this.type = specifier.type === undefined ? "" : specifier.type + "";
27580         }
27581
27582         FormatSpecifier.prototype.toString = function() {
27583           return this.fill
27584               + this.align
27585               + this.sign
27586               + this.symbol
27587               + (this.zero ? "0" : "")
27588               + (this.width === undefined ? "" : Math.max(1, this.width | 0))
27589               + (this.comma ? "," : "")
27590               + (this.precision === undefined ? "" : "." + Math.max(0, this.precision | 0))
27591               + (this.trim ? "~" : "")
27592               + this.type;
27593         };
27594
27595         // Trims insignificant zeros, e.g., replaces 1.2000k with 1.2k.
27596         function formatTrim(s) {
27597           out: for (var n = s.length, i = 1, i0 = -1, i1; i < n; ++i) {
27598             switch (s[i]) {
27599               case ".": i0 = i1 = i; break;
27600               case "0": if (i0 === 0) i0 = i; i1 = i; break;
27601               default: if (!+s[i]) break out; if (i0 > 0) i0 = 0; break;
27602             }
27603           }
27604           return i0 > 0 ? s.slice(0, i0) + s.slice(i1 + 1) : s;
27605         }
27606
27607         var prefixExponent;
27608
27609         function formatPrefixAuto(x, p) {
27610           var d = formatDecimal(x, p);
27611           if (!d) return x + "";
27612           var coefficient = d[0],
27613               exponent = d[1],
27614               i = exponent - (prefixExponent = Math.max(-8, Math.min(8, Math.floor(exponent / 3))) * 3) + 1,
27615               n = coefficient.length;
27616           return i === n ? coefficient
27617               : i > n ? coefficient + new Array(i - n + 1).join("0")
27618               : i > 0 ? coefficient.slice(0, i) + "." + coefficient.slice(i)
27619               : "0." + new Array(1 - i).join("0") + formatDecimal(x, Math.max(0, p + i - 1))[0]; // less than 1y!
27620         }
27621
27622         function formatRounded(x, p) {
27623           var d = formatDecimal(x, p);
27624           if (!d) return x + "";
27625           var coefficient = d[0],
27626               exponent = d[1];
27627           return exponent < 0 ? "0." + new Array(-exponent).join("0") + coefficient
27628               : coefficient.length > exponent + 1 ? coefficient.slice(0, exponent + 1) + "." + coefficient.slice(exponent + 1)
27629               : coefficient + new Array(exponent - coefficient.length + 2).join("0");
27630         }
27631
27632         var formatTypes = {
27633           "%": function(x, p) { return (x * 100).toFixed(p); },
27634           "b": function(x) { return Math.round(x).toString(2); },
27635           "c": function(x) { return x + ""; },
27636           "d": function(x) { return Math.round(x).toString(10); },
27637           "e": function(x, p) { return x.toExponential(p); },
27638           "f": function(x, p) { return x.toFixed(p); },
27639           "g": function(x, p) { return x.toPrecision(p); },
27640           "o": function(x) { return Math.round(x).toString(8); },
27641           "p": function(x, p) { return formatRounded(x * 100, p); },
27642           "r": formatRounded,
27643           "s": formatPrefixAuto,
27644           "X": function(x) { return Math.round(x).toString(16).toUpperCase(); },
27645           "x": function(x) { return Math.round(x).toString(16); }
27646         };
27647
27648         function identity$4(x) {
27649           return x;
27650         }
27651
27652         var map$4 = Array.prototype.map,
27653             prefixes = ["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"];
27654
27655         function formatLocale(locale) {
27656           var group = locale.grouping === undefined || locale.thousands === undefined ? identity$4 : formatGroup(map$4.call(locale.grouping, Number), locale.thousands + ""),
27657               currencyPrefix = locale.currency === undefined ? "" : locale.currency[0] + "",
27658               currencySuffix = locale.currency === undefined ? "" : locale.currency[1] + "",
27659               decimal = locale.decimal === undefined ? "." : locale.decimal + "",
27660               numerals = locale.numerals === undefined ? identity$4 : formatNumerals(map$4.call(locale.numerals, String)),
27661               percent = locale.percent === undefined ? "%" : locale.percent + "",
27662               minus = locale.minus === undefined ? "-" : locale.minus + "",
27663               nan = locale.nan === undefined ? "NaN" : locale.nan + "";
27664
27665           function newFormat(specifier) {
27666             specifier = formatSpecifier(specifier);
27667
27668             var fill = specifier.fill,
27669                 align = specifier.align,
27670                 sign = specifier.sign,
27671                 symbol = specifier.symbol,
27672                 zero = specifier.zero,
27673                 width = specifier.width,
27674                 comma = specifier.comma,
27675                 precision = specifier.precision,
27676                 trim = specifier.trim,
27677                 type = specifier.type;
27678
27679             // The "n" type is an alias for ",g".
27680             if (type === "n") comma = true, type = "g";
27681
27682             // The "" type, and any invalid type, is an alias for ".12~g".
27683             else if (!formatTypes[type]) precision === undefined && (precision = 12), trim = true, type = "g";
27684
27685             // If zero fill is specified, padding goes after sign and before digits.
27686             if (zero || (fill === "0" && align === "=")) zero = true, fill = "0", align = "=";
27687
27688             // Compute the prefix and suffix.
27689             // For SI-prefix, the suffix is lazily computed.
27690             var prefix = symbol === "$" ? currencyPrefix : symbol === "#" && /[boxX]/.test(type) ? "0" + type.toLowerCase() : "",
27691                 suffix = symbol === "$" ? currencySuffix : /[%p]/.test(type) ? percent : "";
27692
27693             // What format function should we use?
27694             // Is this an integer type?
27695             // Can this type generate exponential notation?
27696             var formatType = formatTypes[type],
27697                 maybeSuffix = /[defgprs%]/.test(type);
27698
27699             // Set the default precision if not specified,
27700             // or clamp the specified precision to the supported range.
27701             // For significant precision, it must be in [1, 21].
27702             // For fixed precision, it must be in [0, 20].
27703             precision = precision === undefined ? 6
27704                 : /[gprs]/.test(type) ? Math.max(1, Math.min(21, precision))
27705                 : Math.max(0, Math.min(20, precision));
27706
27707             function format(value) {
27708               var valuePrefix = prefix,
27709                   valueSuffix = suffix,
27710                   i, n, c;
27711
27712               if (type === "c") {
27713                 valueSuffix = formatType(value) + valueSuffix;
27714                 value = "";
27715               } else {
27716                 value = +value;
27717
27718                 // Determine the sign. -0 is not less than 0, but 1 / -0 is!
27719                 var valueNegative = value < 0 || 1 / value < 0;
27720
27721                 // Perform the initial formatting.
27722                 value = isNaN(value) ? nan : formatType(Math.abs(value), precision);
27723
27724                 // Trim insignificant zeros.
27725                 if (trim) value = formatTrim(value);
27726
27727                 // If a negative value rounds to zero after formatting, and no explicit positive sign is requested, hide the sign.
27728                 if (valueNegative && +value === 0 && sign !== "+") valueNegative = false;
27729
27730                 // Compute the prefix and suffix.
27731                 valuePrefix = (valueNegative ? (sign === "(" ? sign : minus) : sign === "-" || sign === "(" ? "" : sign) + valuePrefix;
27732                 valueSuffix = (type === "s" ? prefixes[8 + prefixExponent / 3] : "") + valueSuffix + (valueNegative && sign === "(" ? ")" : "");
27733
27734                 // Break the formatted value into the integer “value” part that can be
27735                 // grouped, and fractional or exponential “suffix” part that is not.
27736                 if (maybeSuffix) {
27737                   i = -1, n = value.length;
27738                   while (++i < n) {
27739                     if (c = value.charCodeAt(i), 48 > c || c > 57) {
27740                       valueSuffix = (c === 46 ? decimal + value.slice(i + 1) : value.slice(i)) + valueSuffix;
27741                       value = value.slice(0, i);
27742                       break;
27743                     }
27744                   }
27745                 }
27746               }
27747
27748               // If the fill character is not "0", grouping is applied before padding.
27749               if (comma && !zero) value = group(value, Infinity);
27750
27751               // Compute the padding.
27752               var length = valuePrefix.length + value.length + valueSuffix.length,
27753                   padding = length < width ? new Array(width - length + 1).join(fill) : "";
27754
27755               // If the fill character is "0", grouping is applied after padding.
27756               if (comma && zero) value = group(padding + value, padding.length ? width - valueSuffix.length : Infinity), padding = "";
27757
27758               // Reconstruct the final output based on the desired alignment.
27759               switch (align) {
27760                 case "<": value = valuePrefix + value + valueSuffix + padding; break;
27761                 case "=": value = valuePrefix + padding + value + valueSuffix; break;
27762                 case "^": value = padding.slice(0, length = padding.length >> 1) + valuePrefix + value + valueSuffix + padding.slice(length); break;
27763                 default: value = padding + valuePrefix + value + valueSuffix; break;
27764               }
27765
27766               return numerals(value);
27767             }
27768
27769             format.toString = function() {
27770               return specifier + "";
27771             };
27772
27773             return format;
27774           }
27775
27776           function formatPrefix(specifier, value) {
27777             var f = newFormat((specifier = formatSpecifier(specifier), specifier.type = "f", specifier)),
27778                 e = Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3,
27779                 k = Math.pow(10, -e),
27780                 prefix = prefixes[8 + e / 3];
27781             return function(value) {
27782               return f(k * value) + prefix;
27783             };
27784           }
27785
27786           return {
27787             format: newFormat,
27788             formatPrefix: formatPrefix
27789           };
27790         }
27791
27792         var locale;
27793         var format;
27794         var formatPrefix;
27795
27796         defaultLocale({
27797           decimal: ".",
27798           thousands: ",",
27799           grouping: [3],
27800           currency: ["$", ""],
27801           minus: "-"
27802         });
27803
27804         function defaultLocale(definition) {
27805           locale = formatLocale(definition);
27806           format = locale.format;
27807           formatPrefix = locale.formatPrefix;
27808           return locale;
27809         }
27810
27811         function precisionFixed(step) {
27812           return Math.max(0, -exponent(Math.abs(step)));
27813         }
27814
27815         function precisionPrefix(step, value) {
27816           return Math.max(0, Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3 - exponent(Math.abs(step)));
27817         }
27818
27819         function precisionRound(step, max) {
27820           step = Math.abs(step), max = Math.abs(max) - step;
27821           return Math.max(0, exponent(max) - exponent(step)) + 1;
27822         }
27823
27824         function tickFormat(start, stop, count, specifier) {
27825           var step = tickStep(start, stop, count),
27826               precision;
27827           specifier = formatSpecifier(specifier == null ? ",f" : specifier);
27828           switch (specifier.type) {
27829             case "s": {
27830               var value = Math.max(Math.abs(start), Math.abs(stop));
27831               if (specifier.precision == null && !isNaN(precision = precisionPrefix(step, value))) specifier.precision = precision;
27832               return formatPrefix(specifier, value);
27833             }
27834             case "":
27835             case "e":
27836             case "g":
27837             case "p":
27838             case "r": {
27839               if (specifier.precision == null && !isNaN(precision = precisionRound(step, Math.max(Math.abs(start), Math.abs(stop))))) specifier.precision = precision - (specifier.type === "e");
27840               break;
27841             }
27842             case "f":
27843             case "%": {
27844               if (specifier.precision == null && !isNaN(precision = precisionFixed(step))) specifier.precision = precision - (specifier.type === "%") * 2;
27845               break;
27846             }
27847           }
27848           return format(specifier);
27849         }
27850
27851         function linearish(scale) {
27852           var domain = scale.domain;
27853
27854           scale.ticks = function(count) {
27855             var d = domain();
27856             return ticks(d[0], d[d.length - 1], count == null ? 10 : count);
27857           };
27858
27859           scale.tickFormat = function(count, specifier) {
27860             var d = domain();
27861             return tickFormat(d[0], d[d.length - 1], count == null ? 10 : count, specifier);
27862           };
27863
27864           scale.nice = function(count) {
27865             if (count == null) count = 10;
27866
27867             var d = domain(),
27868                 i0 = 0,
27869                 i1 = d.length - 1,
27870                 start = d[i0],
27871                 stop = d[i1],
27872                 step;
27873
27874             if (stop < start) {
27875               step = start, start = stop, stop = step;
27876               step = i0, i0 = i1, i1 = step;
27877             }
27878
27879             step = tickIncrement(start, stop, count);
27880
27881             if (step > 0) {
27882               start = Math.floor(start / step) * step;
27883               stop = Math.ceil(stop / step) * step;
27884               step = tickIncrement(start, stop, count);
27885             } else if (step < 0) {
27886               start = Math.ceil(start * step) / step;
27887               stop = Math.floor(stop * step) / step;
27888               step = tickIncrement(start, stop, count);
27889             }
27890
27891             if (step > 0) {
27892               d[i0] = Math.floor(start / step) * step;
27893               d[i1] = Math.ceil(stop / step) * step;
27894               domain(d);
27895             } else if (step < 0) {
27896               d[i0] = Math.ceil(start * step) / step;
27897               d[i1] = Math.floor(stop * step) / step;
27898               domain(d);
27899             }
27900
27901             return scale;
27902           };
27903
27904           return scale;
27905         }
27906
27907         function linear$2() {
27908           var scale = continuous(identity$3, identity$3);
27909
27910           scale.copy = function() {
27911             return copy$1(scale, linear$2());
27912           };
27913
27914           initRange.apply(scale, arguments);
27915
27916           return linearish(scale);
27917         }
27918
27919         function quantize() {
27920           var x0 = 0,
27921               x1 = 1,
27922               n = 1,
27923               domain = [0.5],
27924               range = [0, 1],
27925               unknown;
27926
27927           function scale(x) {
27928             return x <= x ? range[bisectRight(domain, x, 0, n)] : unknown;
27929           }
27930
27931           function rescale() {
27932             var i = -1;
27933             domain = new Array(n);
27934             while (++i < n) domain[i] = ((i + 1) * x1 - (i - n) * x0) / (n + 1);
27935             return scale;
27936           }
27937
27938           scale.domain = function(_) {
27939             return arguments.length ? (x0 = +_[0], x1 = +_[1], rescale()) : [x0, x1];
27940           };
27941
27942           scale.range = function(_) {
27943             return arguments.length ? (n = (range = slice$4.call(_)).length - 1, rescale()) : range.slice();
27944           };
27945
27946           scale.invertExtent = function(y) {
27947             var i = range.indexOf(y);
27948             return i < 0 ? [NaN, NaN]
27949                 : i < 1 ? [x0, domain[0]]
27950                 : i >= n ? [domain[n - 1], x1]
27951                 : [domain[i - 1], domain[i]];
27952           };
27953
27954           scale.unknown = function(_) {
27955             return arguments.length ? (unknown = _, scale) : scale;
27956           };
27957
27958           scale.thresholds = function() {
27959             return domain.slice();
27960           };
27961
27962           scale.copy = function() {
27963             return quantize()
27964                 .domain([x0, x1])
27965                 .range(range)
27966                 .unknown(unknown);
27967           };
27968
27969           return initRange.apply(linearish(scale), arguments);
27970         }
27971
27972         function behaviorBreathe() {
27973             var duration = 800;
27974             var steps = 4;
27975             var selector = '.selected.shadow, .selected .shadow';
27976             var _selected = select(null);
27977             var _classed = '';
27978             var _params = {};
27979             var _done = false;
27980             var _timer;
27981
27982
27983             function ratchetyInterpolator(a, b, steps, units) {
27984                 a = parseFloat(a);
27985                 b = parseFloat(b);
27986                 var sample = quantize()
27987                     .domain([0, 1])
27988                     .range(d3_quantize(d3_interpolateNumber(a, b), steps));
27989
27990                 return function(t) {
27991                     return String(sample(t)) + (units || '');
27992                 };
27993             }
27994
27995
27996             function reset(selection) {
27997                 selection
27998                     .style('stroke-opacity', null)
27999                     .style('stroke-width', null)
28000                     .style('fill-opacity', null)
28001                     .style('r', null);
28002             }
28003
28004
28005             function setAnimationParams(transition, fromTo) {
28006                 var toFrom = (fromTo === 'from' ? 'to' : 'from');
28007
28008                 transition
28009                     .styleTween('stroke-opacity', function(d) {
28010                         return ratchetyInterpolator(
28011                             _params[d.id][toFrom].opacity,
28012                             _params[d.id][fromTo].opacity,
28013                             steps
28014                         );
28015                     })
28016                     .styleTween('stroke-width', function(d) {
28017                         return ratchetyInterpolator(
28018                             _params[d.id][toFrom].width,
28019                             _params[d.id][fromTo].width,
28020                             steps,
28021                             'px'
28022                         );
28023                     })
28024                     .styleTween('fill-opacity', function(d) {
28025                         return ratchetyInterpolator(
28026                             _params[d.id][toFrom].opacity,
28027                             _params[d.id][fromTo].opacity,
28028                             steps
28029                         );
28030                     })
28031                     .styleTween('r', function(d) {
28032                         return ratchetyInterpolator(
28033                             _params[d.id][toFrom].width,
28034                             _params[d.id][fromTo].width,
28035                             steps,
28036                             'px'
28037                         );
28038                     });
28039             }
28040
28041
28042             function calcAnimationParams(selection) {
28043                 selection
28044                     .call(reset)
28045                     .each(function(d) {
28046                         var s = select(this);
28047                         var tag = s.node().tagName;
28048                         var p = {'from': {}, 'to': {}};
28049                         var opacity;
28050                         var width;
28051
28052                         // determine base opacity and width
28053                         if (tag === 'circle') {
28054                             opacity = parseFloat(s.style('fill-opacity') || 0.5);
28055                             width = parseFloat(s.style('r') || 15.5);
28056                         } else {
28057                             opacity = parseFloat(s.style('stroke-opacity') || 0.7);
28058                             width = parseFloat(s.style('stroke-width') || 10);
28059                         }
28060
28061                         // calculate from/to interpolation params..
28062                         p.tag = tag;
28063                         p.from.opacity = opacity * 0.6;
28064                         p.to.opacity = opacity * 1.25;
28065                         p.from.width = width * 0.7;
28066                         p.to.width = width * (tag === 'circle' ? 1.5 : 1);
28067                         _params[d.id] = p;
28068                     });
28069             }
28070
28071
28072             function run(surface, fromTo) {
28073                 var toFrom = (fromTo === 'from' ? 'to' : 'from');
28074                 var currSelected = surface.selectAll(selector);
28075                 var currClassed = surface.attr('class');
28076
28077                 if (_done || currSelected.empty()) {
28078                     _selected.call(reset);
28079                     _selected = select(null);
28080                     return;
28081                 }
28082
28083                 if (!fastDeepEqual(currSelected.data(), _selected.data()) || currClassed !== _classed) {
28084                     _selected.call(reset);
28085                     _classed = currClassed;
28086                     _selected = currSelected.call(calcAnimationParams);
28087                 }
28088
28089                 var didCallNextRun = false;
28090
28091                 _selected
28092                     .transition()
28093                     .duration(duration)
28094                     .call(setAnimationParams, fromTo)
28095                     .on('end', function() {
28096                         // `end` event is called for each selected element, but we want
28097                         // it to run only once
28098                         if (!didCallNextRun) {
28099                             surface.call(run, toFrom);
28100                             didCallNextRun = true;
28101                         }
28102
28103                         // if entity was deselected, remove breathe styling
28104                         if (!select(this).classed('selected')) {
28105                             reset(select(this));
28106                         }
28107                     });
28108             }
28109
28110             function behavior(surface) {
28111                 _done = false;
28112                 _timer = timer(function() {
28113                     // wait for elements to actually become selected
28114                     if (surface.selectAll(selector).empty()) {
28115                         return false;
28116                     }
28117
28118                     surface.call(run, 'from');
28119                     _timer.stop();
28120                     return true;
28121                 }, 20);
28122             }
28123
28124             behavior.restartIfNeeded = function(surface) {
28125                 if (_selected.empty()) {
28126                     surface.call(run, 'from');
28127                     if (_timer) {
28128                         _timer.stop();
28129                     }
28130                 }
28131             };
28132
28133             behavior.off = function() {
28134                 _done = true;
28135                 if (_timer) {
28136                     _timer.stop();
28137                 }
28138                 _selected
28139                     .interrupt()
28140                     .call(reset);
28141             };
28142
28143
28144             return behavior;
28145         }
28146
28147         /* Creates a keybinding behavior for an operation */
28148         function behaviorOperation(context) {
28149             var _operation;
28150
28151             function keypress() {
28152                 // prevent operations during low zoom selection
28153                 if (!context.map().withinEditableZoom()) return;
28154
28155                 event.preventDefault();
28156                 var disabled = _operation.disabled();
28157
28158                 if (disabled) {
28159                     context.ui().flash
28160                         .duration(4000)
28161                         .iconName('#iD-operation-' + _operation.id)
28162                         .iconClass('operation disabled')
28163                         .text(_operation.tooltip)();
28164
28165                 } else {
28166                     context.ui().flash
28167                         .duration(2000)
28168                         .iconName('#iD-operation-' + _operation.id)
28169                         .iconClass('operation')
28170                         .text(_operation.annotation() || _operation.title)();
28171
28172                     if (_operation.point) _operation.point(null);
28173                     _operation();
28174                 }
28175             }
28176
28177
28178             function behavior() {
28179                 if (_operation && _operation.available()) {
28180                     context.keybinding()
28181                         .on(_operation.keys, keypress);
28182                 }
28183
28184                 return behavior;
28185             }
28186
28187
28188             behavior.off = function() {
28189                 context.keybinding()
28190                     .off(_operation.keys);
28191             };
28192
28193
28194             behavior.which = function (_) {
28195                 if (!arguments.length) return _operation;
28196                 _operation = _;
28197                 return behavior;
28198             };
28199
28200
28201             return behavior;
28202         }
28203
28204         function operationCircularize(context, selectedIDs) {
28205             var _extent;
28206             var _actions = selectedIDs.map(getAction).filter(Boolean);
28207             var _amount = _actions.length === 1 ? 'single' : 'multiple';
28208             var _coords = utilGetAllNodes(selectedIDs, context.graph())
28209                 .map(function(n) { return n.loc; });
28210
28211             function getAction(entityID) {
28212
28213                 var entity = context.entity(entityID);
28214
28215                 if (entity.type !== 'way' || new Set(entity.nodes).size <= 1) return null;
28216
28217                 if (!_extent) {
28218                     _extent =  entity.extent(context.graph());
28219                 } else {
28220                     _extent = _extent.extend(entity.extent(context.graph()));
28221                 }
28222
28223                 return actionCircularize(entityID, context.projection);
28224             }
28225
28226             var operation = function() {
28227                 if (!_actions.length) return;
28228
28229                 var combinedAction = function(graph, t) {
28230                     _actions.forEach(function(action) {
28231                         if (!action.disabled(graph)) {
28232                             graph = action(graph, t);
28233                         }
28234                     });
28235                     return graph;
28236                 };
28237                 combinedAction.transitionable = true;
28238
28239                 context.perform(combinedAction, operation.annotation());
28240
28241                 window.setTimeout(function() {
28242                     context.validator().validate();
28243                 }, 300);  // after any transition
28244             };
28245
28246
28247             operation.available = function() {
28248                 return _actions.length && selectedIDs.length === _actions.length;
28249             };
28250
28251
28252             // don't cache this because the visible extent could change
28253             operation.disabled = function() {
28254                 if (!_actions.length) return '';
28255
28256                 var actionDisableds = _actions.map(function(action) {
28257                     return action.disabled(context.graph());
28258                 }).filter(Boolean);
28259
28260                 if (actionDisableds.length === _actions.length) {
28261                     // none of the features can be circularized
28262
28263                     if (new Set(actionDisableds).size > 1) {
28264                         return 'multiple_blockers';
28265                     }
28266                     return actionDisableds[0];
28267                 } else if (_extent.percentContainedIn(context.map().extent()) < 0.8) {
28268                     return 'too_large';
28269                 } else if (someMissing()) {
28270                     return 'not_downloaded';
28271                 } else if (selectedIDs.some(context.hasHiddenConnections)) {
28272                     return 'connected_to_hidden';
28273                 }
28274
28275                 return false;
28276
28277
28278                 function someMissing() {
28279                     if (context.inIntro()) return false;
28280                     var osm = context.connection();
28281                     if (osm) {
28282                         var missing = _coords.filter(function(loc) { return !osm.isDataLoaded(loc); });
28283                         if (missing.length) {
28284                             missing.forEach(function(loc) { context.loadTileAtLoc(loc); });
28285                             return true;
28286                         }
28287                     }
28288                     return false;
28289                 }
28290             };
28291
28292
28293             operation.tooltip = function() {
28294                 var disable = operation.disabled();
28295                 return disable ?
28296                     _t('operations.circularize.' + disable + '.' + _amount) :
28297                     _t('operations.circularize.description.' + _amount);
28298             };
28299
28300
28301             operation.annotation = function() {
28302                 return _t('operations.circularize.annotation.' + _amount);
28303             };
28304
28305
28306             operation.id = 'circularize';
28307             operation.keys = [_t('operations.circularize.key')];
28308             operation.title = _t('operations.circularize.title');
28309             operation.behavior = behaviorOperation(context).which(operation);
28310
28311             return operation;
28312         }
28313
28314         // Translate a MacOS key command into the appropriate Windows/Linux equivalent.
28315         // For example, ⌘Z -> Ctrl+Z
28316         var uiCmd = function (code) {
28317             var detected = utilDetect();
28318
28319             if (detected.os === 'mac') {
28320                 return code;
28321             }
28322
28323             if (detected.os === 'win') {
28324                 if (code === '⌘⇧Z') return 'Ctrl+Y';
28325             }
28326
28327             var result = '',
28328                 replacements = {
28329                     '⌘': 'Ctrl',
28330                     '⇧': 'Shift',
28331                     '⌥': 'Alt',
28332                     '⌫': 'Backspace',
28333                     '⌦': 'Delete'
28334                 };
28335
28336             for (var i = 0; i < code.length; i++) {
28337                 if (code[i] in replacements) {
28338                     result += replacements[code[i]] + (i < code.length - 1 ? '+' : '');
28339                 } else {
28340                     result += code[i];
28341                 }
28342             }
28343
28344             return result;
28345         };
28346
28347
28348         // return a display-focused string for a given keyboard code
28349         uiCmd.display = function(code) {
28350             if (code.length !== 1) return code;
28351
28352             var detected = utilDetect();
28353             var mac = (detected.os === 'mac');
28354             var replacements = {
28355                 '⌘': mac ? '⌘ ' + _t('shortcuts.key.cmd')    : _t('shortcuts.key.ctrl'),
28356                 '⇧': mac ? '⇧ ' + _t('shortcuts.key.shift')  : _t('shortcuts.key.shift'),
28357                 '⌥': mac ? '⌥ ' + _t('shortcuts.key.option') : _t('shortcuts.key.alt'),
28358                 '⌃': mac ? '⌃ ' + _t('shortcuts.key.ctrl')   : _t('shortcuts.key.ctrl'),
28359                 '⌫': mac ? '⌫ ' + _t('shortcuts.key.delete') : _t('shortcuts.key.backspace'),
28360                 '⌦': mac ? '⌦ ' + _t('shortcuts.key.del')    : _t('shortcuts.key.del'),
28361                 '↖': mac ? '↖ ' + _t('shortcuts.key.pgup')   : _t('shortcuts.key.pgup'),
28362                 '↘': mac ? '↘ ' + _t('shortcuts.key.pgdn')   : _t('shortcuts.key.pgdn'),
28363                 '⇞': mac ? '⇞ ' + _t('shortcuts.key.home')   : _t('shortcuts.key.home'),
28364                 '⇟': mac ? '⇟ ' + _t('shortcuts.key.end')    : _t('shortcuts.key.end'),
28365                 '↵': mac ? '⏎ ' + _t('shortcuts.key.return') : _t('shortcuts.key.enter'),
28366                 '⎋': mac ? '⎋ ' + _t('shortcuts.key.esc')    : _t('shortcuts.key.esc'),
28367                 '☰': mac ? '☰ ' + _t('shortcuts.key.menu')  : _t('shortcuts.key.menu'),
28368             };
28369
28370             return replacements[code] || code;
28371         };
28372
28373         function operationDelete(context, selectedIDs) {
28374             var multi = (selectedIDs.length === 1 ? 'single' : 'multiple');
28375             var action = actionDeleteMultiple(selectedIDs);
28376             var nodes = utilGetAllNodes(selectedIDs, context.graph());
28377             var coords = nodes.map(function(n) { return n.loc; });
28378             var extent = utilTotalExtent(selectedIDs, context.graph());
28379
28380
28381             var operation = function() {
28382                 var nextSelectedID;
28383                 var nextSelectedLoc;
28384
28385                 if (selectedIDs.length === 1) {
28386                     var id = selectedIDs[0];
28387                     var entity = context.entity(id);
28388                     var geometry = entity.geometry(context.graph());
28389                     var parents = context.graph().parentWays(entity);
28390                     var parent = parents[0];
28391
28392                     // Select the next closest node in the way.
28393                     if (geometry === 'vertex') {
28394                         var nodes = parent.nodes;
28395                         var i = nodes.indexOf(id);
28396
28397                         if (i === 0) {
28398                             i++;
28399                         } else if (i === nodes.length - 1) {
28400                             i--;
28401                         } else {
28402                             var a = geoSphericalDistance(entity.loc, context.entity(nodes[i - 1]).loc);
28403                             var b = geoSphericalDistance(entity.loc, context.entity(nodes[i + 1]).loc);
28404                             i = a < b ? i - 1 : i + 1;
28405                         }
28406
28407                         nextSelectedID = nodes[i];
28408                         nextSelectedLoc = context.entity(nextSelectedID).loc;
28409                     }
28410                 }
28411
28412                 context.perform(action, operation.annotation());
28413                 context.validator().validate();
28414
28415                 if (nextSelectedID && nextSelectedLoc) {
28416                     if (context.hasEntity(nextSelectedID)) {
28417                         context.enter(modeSelect(context, [nextSelectedID]).follow(true));
28418                     } else {
28419                         context.map().centerEase(nextSelectedLoc);
28420                         context.enter(modeBrowse(context));
28421                     }
28422                 } else {
28423                     context.enter(modeBrowse(context));
28424                 }
28425
28426             };
28427
28428
28429             operation.available = function() {
28430                 return true;
28431             };
28432
28433
28434             operation.disabled = function() {
28435                 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
28436                     return 'too_large';
28437                 } else if (someMissing()) {
28438                     return 'not_downloaded';
28439                 } else if (selectedIDs.some(context.hasHiddenConnections)) {
28440                     return 'connected_to_hidden';
28441                 } else if (selectedIDs.some(protectedMember)) {
28442                     return 'part_of_relation';
28443                 } else if (selectedIDs.some(incompleteRelation)) {
28444                     return 'incomplete_relation';
28445                 } else if (selectedIDs.some(hasWikidataTag)) {
28446                     return 'has_wikidata_tag';
28447                 }
28448
28449                 return false;
28450
28451
28452                 function someMissing() {
28453                     if (context.inIntro()) return false;
28454                     var osm = context.connection();
28455                     if (osm) {
28456                         var missing = coords.filter(function(loc) { return !osm.isDataLoaded(loc); });
28457                         if (missing.length) {
28458                             missing.forEach(function(loc) { context.loadTileAtLoc(loc); });
28459                             return true;
28460                         }
28461                     }
28462                     return false;
28463                 }
28464
28465                 function hasWikidataTag(id) {
28466                     var entity = context.entity(id);
28467                     return entity.tags.wikidata && entity.tags.wikidata.trim().length > 0;
28468                 }
28469
28470                 function incompleteRelation(id) {
28471                     var entity = context.entity(id);
28472                     return entity.type === 'relation' && !entity.isComplete(context.graph());
28473                 }
28474
28475                 function protectedMember(id) {
28476                     var entity = context.entity(id);
28477                     if (entity.type !== 'way') return false;
28478
28479                     var parents = context.graph().parentRelations(entity);
28480                     for (var i = 0; i < parents.length; i++) {
28481                         var parent = parents[i];
28482                         var type = parent.tags.type;
28483                         var role = parent.memberById(id).role || 'outer';
28484                         if (type === 'route' || type === 'boundary' || (type === 'multipolygon' && role === 'outer')) {
28485                             return true;
28486                         }
28487                     }
28488                     return false;
28489                 }
28490             };
28491
28492
28493             operation.tooltip = function() {
28494                 var disable = operation.disabled();
28495                 return disable ?
28496                     _t('operations.delete.' + disable + '.' + multi) :
28497                     _t('operations.delete.description' + '.' + multi);
28498             };
28499
28500
28501             operation.annotation = function() {
28502                 return selectedIDs.length === 1 ?
28503                     _t('operations.delete.annotation.' + context.graph().geometry(selectedIDs[0])) :
28504                     _t('operations.delete.annotation.multiple', { n: selectedIDs.length });
28505             };
28506
28507
28508             operation.id = 'delete';
28509             operation.keys = [uiCmd('⌘⌫'), uiCmd('⌘⌦'), uiCmd('⌦')];
28510             operation.title = _t('operations.delete.title');
28511             operation.behavior = behaviorOperation(context).which(operation);
28512
28513             return operation;
28514         }
28515
28516         function operationOrthogonalize(context, selectedIDs) {
28517             var _extent;
28518             var _type;
28519             var _actions = selectedIDs.map(chooseAction).filter(Boolean);
28520             var _amount = _actions.length === 1 ? 'single' : 'multiple';
28521             var _coords = utilGetAllNodes(selectedIDs, context.graph())
28522                 .map(function(n) { return n.loc; });
28523
28524
28525             function chooseAction(entityID) {
28526
28527                 var entity = context.entity(entityID);
28528                 var geometry = entity.geometry(context.graph());
28529
28530                 if (!_extent) {
28531                     _extent =  entity.extent(context.graph());
28532                 } else {
28533                     _extent = _extent.extend(entity.extent(context.graph()));
28534                 }
28535
28536                 // square a line/area
28537                 if (entity.type === 'way' && new Set(entity.nodes).size > 2 ) {
28538                     if (_type && _type !== 'feature') return null;
28539                     _type = 'feature';
28540                     return actionOrthogonalize(entityID, context.projection);
28541
28542                 // square a single vertex
28543                 } else if (geometry === 'vertex') {
28544                     if (_type && _type !== 'corner') return null;
28545                     _type = 'corner';
28546                     var graph = context.graph();
28547                     var parents = graph.parentWays(entity);
28548                     if (parents.length === 1) {
28549                         var way = parents[0];
28550                         if (way.nodes.indexOf(entityID) !== -1) {
28551                             return actionOrthogonalize(way.id, context.projection, entityID);
28552                         }
28553                     }
28554                 }
28555
28556                 return null;
28557             }
28558
28559
28560             var operation = function() {
28561                 if (!_actions.length) return;
28562
28563                 var combinedAction = function(graph, t) {
28564                     _actions.forEach(function(action) {
28565                         if (!action.disabled(graph)) {
28566                             graph = action(graph, t);
28567                         }
28568                     });
28569                     return graph;
28570                 };
28571                 combinedAction.transitionable = true;
28572
28573                 context.perform(combinedAction, operation.annotation());
28574
28575                 window.setTimeout(function() {
28576                     context.validator().validate();
28577                 }, 300);  // after any transition
28578             };
28579
28580
28581             operation.available = function() {
28582                 return _actions.length && selectedIDs.length === _actions.length;
28583             };
28584
28585
28586             // don't cache this because the visible extent could change
28587             operation.disabled = function() {
28588                 if (!_actions.length) return '';
28589
28590                 var actionDisableds = _actions.map(function(action) {
28591                     return action.disabled(context.graph());
28592                 }).filter(Boolean);
28593
28594                 if (actionDisableds.length === _actions.length) {
28595                     // none of the features can be squared
28596
28597                     if (new Set(actionDisableds).size > 1) {
28598                         return 'multiple_blockers';
28599                     }
28600                     return actionDisableds[0];
28601                 } else if (_extent &&
28602                            _extent.percentContainedIn(context.map().extent()) < 0.8) {
28603                     return 'too_large';
28604                 } else if (someMissing()) {
28605                     return 'not_downloaded';
28606                 } else if (selectedIDs.some(context.hasHiddenConnections)) {
28607                     return 'connected_to_hidden';
28608                 }
28609
28610                 return false;
28611
28612
28613                 function someMissing() {
28614                     if (context.inIntro()) return false;
28615                     var osm = context.connection();
28616                     if (osm) {
28617                         var missing = _coords.filter(function(loc) { return !osm.isDataLoaded(loc); });
28618                         if (missing.length) {
28619                             missing.forEach(function(loc) { context.loadTileAtLoc(loc); });
28620                             return true;
28621                         }
28622                     }
28623                     return false;
28624                 }
28625             };
28626
28627
28628             operation.tooltip = function() {
28629                 var disable = operation.disabled();
28630                 return disable ?
28631                     _t('operations.orthogonalize.' + disable + '.' + _amount) :
28632                     _t('operations.orthogonalize.description.' + _type + '.' + _amount);
28633             };
28634
28635
28636             operation.annotation = function() {
28637                 return _t('operations.orthogonalize.annotation.' + _type + '.' + _amount);
28638             };
28639
28640
28641             operation.id = 'orthogonalize';
28642             operation.keys = [_t('operations.orthogonalize.key')];
28643             operation.title = _t('operations.orthogonalize.title');
28644             operation.behavior = behaviorOperation(context).which(operation);
28645
28646             return operation;
28647         }
28648
28649         function operationReflectShort(context, selectedIDs) {
28650             return operationReflect(context, selectedIDs, 'short');
28651         }
28652
28653
28654         function operationReflectLong(context, selectedIDs) {
28655             return operationReflect(context, selectedIDs, 'long');
28656         }
28657
28658
28659         function operationReflect(context, selectedIDs, axis) {
28660             axis = axis || 'long';
28661             var multi = (selectedIDs.length === 1 ? 'single' : 'multiple');
28662             var nodes = utilGetAllNodes(selectedIDs, context.graph());
28663             var coords = nodes.map(function(n) { return n.loc; });
28664             var extent = utilTotalExtent(selectedIDs, context.graph());
28665
28666
28667             var operation = function() {
28668                 var action = actionReflect(selectedIDs, context.projection)
28669                     .useLongAxis(Boolean(axis === 'long'));
28670
28671                 context.perform(action, operation.annotation());
28672
28673                 window.setTimeout(function() {
28674                     context.validator().validate();
28675                 }, 300);  // after any transition
28676             };
28677
28678
28679             operation.available = function() {
28680                 return nodes.length >= 3;
28681             };
28682
28683
28684             // don't cache this because the visible extent could change
28685             operation.disabled = function() {
28686                 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
28687                     return 'too_large';
28688                 } else if (someMissing()) {
28689                     return 'not_downloaded';
28690                 } else if (selectedIDs.some(context.hasHiddenConnections)) {
28691                     return 'connected_to_hidden';
28692                 } else if (selectedIDs.some(incompleteRelation)) {
28693                     return 'incomplete_relation';
28694                 }
28695
28696                 return false;
28697
28698
28699                 function someMissing() {
28700                     if (context.inIntro()) return false;
28701                     var osm = context.connection();
28702                     if (osm) {
28703                         var missing = coords.filter(function(loc) { return !osm.isDataLoaded(loc); });
28704                         if (missing.length) {
28705                             missing.forEach(function(loc) { context.loadTileAtLoc(loc); });
28706                             return true;
28707                         }
28708                     }
28709                     return false;
28710                 }
28711
28712                 function incompleteRelation(id) {
28713                     var entity = context.entity(id);
28714                     return entity.type === 'relation' && !entity.isComplete(context.graph());
28715                 }
28716             };
28717
28718
28719             operation.tooltip = function() {
28720                 var disable = operation.disabled();
28721                 return disable ?
28722                     _t('operations.reflect.' + disable + '.' + multi) :
28723                     _t('operations.reflect.description.' + axis + '.' + multi);
28724             };
28725
28726
28727             operation.annotation = function() {
28728                 return _t('operations.reflect.annotation.' + axis + '.' + multi);
28729             };
28730
28731
28732             operation.id = 'reflect-' + axis;
28733             operation.keys = [_t('operations.reflect.key.' + axis)];
28734             operation.title = _t('operations.reflect.title.' + axis);
28735             operation.behavior = behaviorOperation(context).which(operation);
28736
28737             return operation;
28738         }
28739
28740         function operationMove(context, selectedIDs) {
28741             var multi = (selectedIDs.length === 1 ? 'single' : 'multiple');
28742             var nodes = utilGetAllNodes(selectedIDs, context.graph());
28743             var coords = nodes.map(function(n) { return n.loc; });
28744             var extent = utilTotalExtent(selectedIDs, context.graph());
28745
28746
28747             var operation = function() {
28748                 context.enter(modeMove(context, selectedIDs));
28749             };
28750
28751
28752             operation.available = function() {
28753                 return selectedIDs.length > 1 ||
28754                     context.entity(selectedIDs[0]).type !== 'node';
28755             };
28756
28757
28758             operation.disabled = function() {
28759                 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
28760                     return 'too_large';
28761                 } else if (someMissing()) {
28762                     return 'not_downloaded';
28763                 } else if (selectedIDs.some(context.hasHiddenConnections)) {
28764                     return 'connected_to_hidden';
28765                 } else if (selectedIDs.some(incompleteRelation)) {
28766                     return 'incomplete_relation';
28767                 }
28768
28769                 return false;
28770
28771
28772                 function someMissing() {
28773                     if (context.inIntro()) return false;
28774                     var osm = context.connection();
28775                     if (osm) {
28776                         var missing = coords.filter(function(loc) { return !osm.isDataLoaded(loc); });
28777                         if (missing.length) {
28778                             missing.forEach(function(loc) { context.loadTileAtLoc(loc); });
28779                             return true;
28780                         }
28781                     }
28782                     return false;
28783                 }
28784
28785                 function incompleteRelation(id) {
28786                     var entity = context.entity(id);
28787                     return entity.type === 'relation' && !entity.isComplete(context.graph());
28788                 }
28789             };
28790
28791
28792             operation.tooltip = function() {
28793                 var disable = operation.disabled();
28794                 return disable ?
28795                     _t('operations.move.' + disable + '.' + multi) :
28796                     _t('operations.move.description.' + multi);
28797             };
28798
28799
28800             operation.annotation = function() {
28801                 return selectedIDs.length === 1 ?
28802                     _t('operations.move.annotation.' + context.graph().geometry(selectedIDs[0])) :
28803                     _t('operations.move.annotation.multiple');
28804             };
28805
28806
28807             operation.id = 'move';
28808             operation.keys = [_t('operations.move.key')];
28809             operation.title = _t('operations.move.title');
28810             operation.behavior = behaviorOperation(context).which(operation);
28811
28812             operation.mouseOnly = true;
28813
28814             return operation;
28815         }
28816
28817         function modeRotate(context, entityIDs) {
28818             var mode = {
28819                 id: 'rotate',
28820                 button: 'browse'
28821             };
28822
28823             var keybinding = utilKeybinding('rotate');
28824             var behaviors = [
28825                 behaviorEdit(context),
28826                 operationCircularize(context, entityIDs).behavior,
28827                 operationDelete(context, entityIDs).behavior,
28828                 operationMove(context, entityIDs).behavior,
28829                 operationOrthogonalize(context, entityIDs).behavior,
28830                 operationReflectLong(context, entityIDs).behavior,
28831                 operationReflectShort(context, entityIDs).behavior
28832             ];
28833             var annotation = entityIDs.length === 1 ?
28834                 _t('operations.rotate.annotation.' + context.graph().geometry(entityIDs[0])) :
28835                 _t('operations.rotate.annotation.multiple');
28836
28837             var _prevGraph;
28838             var _prevAngle;
28839             var _prevTransform;
28840             var _pivot;
28841
28842
28843             function doRotate() {
28844                 var fn;
28845                 if (context.graph() !== _prevGraph) {
28846                     fn = context.perform;
28847                 } else {
28848                     fn = context.replace;
28849                 }
28850
28851                 // projection changed, recalculate _pivot
28852                 var projection = context.projection;
28853                 var currTransform = projection.transform();
28854                 if (!_prevTransform ||
28855                     currTransform.k !== _prevTransform.k ||
28856                     currTransform.x !== _prevTransform.x ||
28857                     currTransform.y !== _prevTransform.y) {
28858
28859                     var nodes = utilGetAllNodes(entityIDs, context.graph());
28860                     var points = nodes.map(function(n) { return projection(n.loc); });
28861                     _pivot = getPivot(points);
28862                     _prevAngle = undefined;
28863                 }
28864
28865
28866                 var currMouse = context.map().mouse();
28867                 var currAngle = Math.atan2(currMouse[1] - _pivot[1], currMouse[0] - _pivot[0]);
28868
28869                 if (typeof _prevAngle === 'undefined') _prevAngle = currAngle;
28870                 var delta = currAngle - _prevAngle;
28871
28872                 fn(actionRotate(entityIDs, _pivot, delta, projection));
28873
28874                 _prevTransform = currTransform;
28875                 _prevAngle = currAngle;
28876                 _prevGraph = context.graph();
28877             }
28878
28879             function getPivot(points) {
28880                 var _pivot;
28881                 if (points.length === 1) {
28882                     _pivot = points[0];
28883                 } else if (points.length === 2) {
28884                     _pivot = geoVecInterp(points[0], points[1], 0.5);
28885                 } else {
28886                     var polygonHull = d3_polygonHull(points);
28887                     if (polygonHull.length === 2) {
28888                         _pivot = geoVecInterp(points[0], points[1], 0.5);
28889                     } else {
28890                         _pivot = d3_polygonCentroid(d3_polygonHull(points));
28891                     }
28892                 }
28893                 return _pivot;
28894             }
28895
28896
28897             function finish() {
28898                 event.stopPropagation();
28899                 context.replace(actionNoop(), annotation);
28900                 context.enter(modeSelect(context, entityIDs));
28901             }
28902
28903
28904             function cancel() {
28905                 context.pop();
28906                 context.enter(modeSelect(context, entityIDs));
28907             }
28908
28909
28910             function undone() {
28911                 context.enter(modeBrowse(context));
28912             }
28913
28914
28915             mode.enter = function() {
28916                 context.features().forceVisible(entityIDs);
28917
28918                 behaviors.forEach(context.install);
28919
28920                 context.surface()
28921                     .on('mousemove.rotate', doRotate)
28922                     .on('click.rotate', finish);
28923
28924                 context.history()
28925                     .on('undone.rotate', undone);
28926
28927                 keybinding
28928                     .on('⎋', cancel)
28929                     .on('↩', finish);
28930
28931                 select(document)
28932                     .call(keybinding);
28933             };
28934
28935
28936             mode.exit = function() {
28937                 behaviors.forEach(context.uninstall);
28938
28939                 context.surface()
28940                     .on('mousemove.rotate', null)
28941                     .on('click.rotate', null);
28942
28943                 context.history()
28944                     .on('undone.rotate', null);
28945
28946                 select(document)
28947                     .call(keybinding.unbind);
28948
28949                 context.features().forceVisible([]);
28950             };
28951
28952
28953             mode.selectedIDs = function() {
28954                 if (!arguments.length) return entityIDs;
28955                 // no assign
28956                 return mode;
28957             };
28958
28959
28960             return mode;
28961         }
28962
28963         function operationRotate(context, selectedIDs) {
28964             var multi = (selectedIDs.length === 1 ? 'single' : 'multiple');
28965             var nodes = utilGetAllNodes(selectedIDs, context.graph());
28966             var coords = nodes.map(function(n) { return n.loc; });
28967             var extent = utilTotalExtent(selectedIDs, context.graph());
28968
28969
28970             var operation = function() {
28971                 context.enter(modeRotate(context, selectedIDs));
28972             };
28973
28974
28975             operation.available = function() {
28976                 return nodes.length >= 2;
28977             };
28978
28979
28980             operation.disabled = function() {
28981
28982                 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
28983                     return 'too_large';
28984                 } else if (someMissing()) {
28985                     return 'not_downloaded';
28986                 } else if (selectedIDs.some(context.hasHiddenConnections)) {
28987                     return 'connected_to_hidden';
28988                 } else if (selectedIDs.some(incompleteRelation)) {
28989                     return 'incomplete_relation';
28990                 }
28991
28992                 return false;
28993
28994
28995                 function someMissing() {
28996                     if (context.inIntro()) return false;
28997                     var osm = context.connection();
28998                     if (osm) {
28999                         var missing = coords.filter(function(loc) { return !osm.isDataLoaded(loc); });
29000                         if (missing.length) {
29001                             missing.forEach(function(loc) { context.loadTileAtLoc(loc); });
29002                             return true;
29003                         }
29004                     }
29005                     return false;
29006                 }
29007
29008                 function incompleteRelation(id) {
29009                     var entity = context.entity(id);
29010                     return entity.type === 'relation' && !entity.isComplete(context.graph());
29011                 }
29012             };
29013
29014
29015             operation.tooltip = function() {
29016                 var disable = operation.disabled();
29017                 return disable ?
29018                     _t('operations.rotate.' + disable + '.' + multi) :
29019                     _t('operations.rotate.description.' + multi);
29020             };
29021
29022
29023             operation.annotation = function() {
29024                 return selectedIDs.length === 1 ?
29025                     _t('operations.rotate.annotation.' + context.graph().geometry(selectedIDs[0])) :
29026                     _t('operations.rotate.annotation.multiple');
29027             };
29028
29029
29030             operation.id = 'rotate';
29031             operation.keys = [_t('operations.rotate.key')];
29032             operation.title = _t('operations.rotate.title');
29033             operation.behavior = behaviorOperation(context).which(operation);
29034
29035             operation.mouseOnly = true;
29036
29037             return operation;
29038         }
29039
29040         function modeMove(context, entityIDs, baseGraph) {
29041             var mode = {
29042                 id: 'move',
29043                 button: 'browse'
29044             };
29045
29046             var keybinding = utilKeybinding('move');
29047             var behaviors = [
29048                 behaviorEdit(context),
29049                 operationCircularize(context, entityIDs).behavior,
29050                 operationDelete(context, entityIDs).behavior,
29051                 operationOrthogonalize(context, entityIDs).behavior,
29052                 operationReflectLong(context, entityIDs).behavior,
29053                 operationReflectShort(context, entityIDs).behavior,
29054                 operationRotate(context, entityIDs).behavior
29055             ];
29056             var annotation = entityIDs.length === 1 ?
29057                 _t('operations.move.annotation.' + context.graph().geometry(entityIDs[0])) :
29058                 _t('operations.move.annotation.multiple');
29059
29060             var _prevGraph;
29061             var _cache;
29062             var _origin;
29063             var _nudgeInterval;
29064
29065
29066             function doMove(nudge) {
29067                 nudge = nudge || [0, 0];
29068
29069                 var fn;
29070                 if (_prevGraph !== context.graph()) {
29071                     _cache = {};
29072                     _origin = context.map().mouseCoordinates();
29073                     fn = context.perform;
29074                 } else {
29075                     fn = context.overwrite;
29076                 }
29077
29078                 var currMouse = context.map().mouse();
29079                 var origMouse = context.projection(_origin);
29080                 var delta = geoVecSubtract(geoVecSubtract(currMouse, origMouse), nudge);
29081
29082                 fn(actionMove(entityIDs, delta, context.projection, _cache));
29083                 _prevGraph = context.graph();
29084             }
29085
29086
29087             function startNudge(nudge) {
29088                 if (_nudgeInterval) window.clearInterval(_nudgeInterval);
29089                 _nudgeInterval = window.setInterval(function() {
29090                     context.map().pan(nudge);
29091                     doMove(nudge);
29092                 }, 50);
29093             }
29094
29095
29096             function stopNudge() {
29097                 if (_nudgeInterval) {
29098                     window.clearInterval(_nudgeInterval);
29099                     _nudgeInterval = null;
29100                 }
29101             }
29102
29103
29104             function move() {
29105                 doMove();
29106                 var nudge = geoViewportEdge(context.map().mouse(), context.map().dimensions());
29107                 if (nudge) {
29108                     startNudge(nudge);
29109                 } else {
29110                     stopNudge();
29111                 }
29112             }
29113
29114
29115             function finish() {
29116                 event.stopPropagation();
29117                 context.replace(actionNoop(), annotation);
29118                 context.enter(modeSelect(context, entityIDs));
29119                 stopNudge();
29120             }
29121
29122
29123             function cancel() {
29124                 if (baseGraph) {
29125                     while (context.graph() !== baseGraph) context.pop();
29126                     context.enter(modeBrowse(context));
29127                 } else {
29128                     context.pop();
29129                     context.enter(modeSelect(context, entityIDs));
29130                 }
29131                 stopNudge();
29132             }
29133
29134
29135             function undone() {
29136                 context.enter(modeBrowse(context));
29137             }
29138
29139
29140             mode.enter = function() {
29141                 _origin = context.map().mouseCoordinates();
29142                 _prevGraph = null;
29143                 _cache = {};
29144
29145                 context.features().forceVisible(entityIDs);
29146
29147                 behaviors.forEach(context.install);
29148
29149                 context.surface()
29150                     .on('mousemove.move', move)
29151                     .on('click.move', finish);
29152
29153                 context.history()
29154                     .on('undone.move', undone);
29155
29156                 keybinding
29157                     .on('⎋', cancel)
29158                     .on('↩', finish);
29159
29160                 select(document)
29161                     .call(keybinding);
29162             };
29163
29164
29165             mode.exit = function() {
29166                 stopNudge();
29167
29168                 behaviors.forEach(function(behavior) {
29169                     context.uninstall(behavior);
29170                 });
29171
29172                 context.surface()
29173                     .on('mousemove.move', null)
29174                     .on('click.move', null);
29175
29176                 context.history()
29177                     .on('undone.move', null);
29178
29179                 select(document)
29180                     .call(keybinding.unbind);
29181
29182                 context.features().forceVisible([]);
29183             };
29184
29185
29186             mode.selectedIDs = function() {
29187                 if (!arguments.length) return entityIDs;
29188                 // no assign
29189                 return mode;
29190             };
29191
29192
29193             return mode;
29194         }
29195
29196         // see also `operationPaste`
29197         function behaviorPaste(context) {
29198
29199             function doPaste() {
29200                 // prevent paste during low zoom selection
29201                 if (!context.map().withinEditableZoom()) return;
29202
29203                 event.preventDefault();
29204
29205                 var baseGraph = context.graph();
29206                 var mouse = context.map().mouse();
29207                 var projection = context.projection;
29208                 var viewport = geoExtent(projection.clipExtent()).polygon();
29209
29210                 if (!geoPointInPolygon(mouse, viewport)) return;
29211
29212                 var oldIDs = context.copyIDs();
29213                 if (!oldIDs.length) return;
29214
29215                 var extent = geoExtent();
29216                 var oldGraph = context.copyGraph();
29217                 var newIDs = [];
29218
29219                 var action = actionCopyEntities(oldIDs, oldGraph);
29220                 context.perform(action);
29221
29222                 var copies = action.copies();
29223                 var originals = new Set();
29224                 Object.values(copies).forEach(function(entity) { originals.add(entity.id); });
29225
29226                 for (var id in copies) {
29227                     var oldEntity = oldGraph.entity(id);
29228                     var newEntity = copies[id];
29229
29230                     extent._extend(oldEntity.extent(oldGraph));
29231
29232                     // Exclude child nodes from newIDs if their parent way was also copied.
29233                     var parents = context.graph().parentWays(newEntity);
29234                     var parentCopied = parents.some(function(parent) {
29235                         return originals.has(parent.id);
29236                     });
29237
29238                     if (!parentCopied) {
29239                         newIDs.push(newEntity.id);
29240                     }
29241                 }
29242
29243                 // Put pasted objects where mouse pointer is..
29244                 var copyPoint = (context.copyLonLat() && projection(context.copyLonLat())) || projection(extent.center());
29245                 var delta = geoVecSubtract(mouse, copyPoint);
29246
29247                 context.perform(actionMove(newIDs, delta, projection));
29248                 context.enter(modeMove(context, newIDs, baseGraph));
29249             }
29250
29251
29252             function behavior() {
29253                 context.keybinding().on(uiCmd('⌘V'), doPaste);
29254                 return behavior;
29255             }
29256
29257
29258             behavior.off = function() {
29259                 context.keybinding().off(uiCmd('⌘V'));
29260             };
29261
29262
29263             return behavior;
29264         }
29265
29266         /*
29267             `behaviorDrag` is like `d3_behavior.drag`, with the following differences:
29268
29269             * The `origin` function is expected to return an [x, y] tuple rather than an
29270               {x, y} object.
29271             * The events are `start`, `move`, and `end`.
29272               (https://github.com/mbostock/d3/issues/563)
29273             * The `start` event is not dispatched until the first cursor movement occurs.
29274               (https://github.com/mbostock/d3/pull/368)
29275             * The `move` event has a `point` and `delta` [x, y] tuple properties rather
29276               than `x`, `y`, `dx`, and `dy` properties.
29277             * The `end` event is not dispatched if no movement occurs.
29278             * An `off` function is available that unbinds the drag's internal event handlers.
29279          */
29280
29281         function behaviorDrag() {
29282             var dispatch$1 = dispatch('start', 'move', 'end');
29283
29284             // see also behaviorSelect
29285             var _tolerancePx = 1; // keep this low to facilitate pixel-perfect micromapping
29286             var _penTolerancePx = 4; // styluses can be touchy so require greater movement - #1981
29287
29288             var _origin = null;
29289             var _selector = '';
29290             var _event;
29291             var _target;
29292             var _surface;
29293             var _pointerId;
29294
29295             // use pointer events on supported platforms; fallback to mouse events
29296             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
29297
29298             var d3_event_userSelectProperty = utilPrefixCSSProperty('UserSelect');
29299             var d3_event_userSelectSuppress = function() {
29300                     var selection$1 = selection();
29301                     var select = selection$1.style(d3_event_userSelectProperty);
29302                     selection$1.style(d3_event_userSelectProperty, 'none');
29303                     return function() {
29304                         selection$1.style(d3_event_userSelectProperty, select);
29305                     };
29306                 };
29307
29308
29309             function eventOf(thiz, argumentz) {
29310                 return function(e1) {
29311                     e1.target = behavior;
29312                     customEvent(e1, dispatch$1.apply, dispatch$1, [e1.type, thiz, argumentz]);
29313                 };
29314             }
29315
29316
29317             function pointerdown() {
29318
29319                 if (_pointerId) return;
29320
29321                 _pointerId = event.pointerId || 'mouse';
29322
29323                 _target = this;
29324                 _event = eventOf(_target, arguments);
29325
29326                 // only force reflow once per drag
29327                 var pointerLocGetter = utilFastMouse(_surface || _target.parentNode);
29328
29329                 var offset;
29330                 var startOrigin = pointerLocGetter(event);
29331                 var started = false;
29332                 var selectEnable = d3_event_userSelectSuppress();
29333
29334                 select(window)
29335                     .on(_pointerPrefix + 'move.drag', pointermove)
29336                     .on(_pointerPrefix + 'up.drag pointercancel.drag', pointerup, true);
29337
29338                 if (_origin) {
29339                     offset = _origin.apply(_target, arguments);
29340                     offset = [offset[0] - startOrigin[0], offset[1] - startOrigin[1]];
29341                 } else {
29342                     offset = [0, 0];
29343                 }
29344
29345                 event.stopPropagation();
29346
29347
29348                 function pointermove() {
29349                     if (_pointerId !== (event.pointerId || 'mouse')) return;
29350
29351                     var p = pointerLocGetter(event);
29352
29353                     if (!started) {
29354                         var dist = geoVecLength(startOrigin,  p);
29355                         var tolerance = event.pointerType === 'pen' ? _penTolerancePx : _tolerancePx;
29356                         // don't start until the drag has actually moved somewhat
29357                         if (dist < tolerance) return;
29358
29359                         started = true;
29360                         _event({ type: 'start' });
29361
29362                     // Don't send a `move` event in the same cycle as `start` since dragging
29363                     // a midpoint will convert the target to a node.
29364                     } else {
29365
29366                         startOrigin = p;
29367                         event.stopPropagation();
29368                         event.preventDefault();
29369
29370                         var dx = p[0] - startOrigin[0];
29371                         var dy = p[1] - startOrigin[1];
29372                         _event({
29373                             type: 'move',
29374                             point: [p[0] + offset[0],  p[1] + offset[1]],
29375                             delta: [dx, dy]
29376                         });
29377                     }
29378                 }
29379
29380
29381                 function pointerup() {
29382                     if (_pointerId !== (event.pointerId || 'mouse')) return;
29383
29384                     _pointerId = null;
29385
29386                     if (started) {
29387                         _event({ type: 'end' });
29388
29389                         event.preventDefault();
29390                     }
29391
29392                     select(window)
29393                         .on(_pointerPrefix + 'move.drag', null)
29394                         .on(_pointerPrefix + 'up.drag pointercancel.drag', null);
29395
29396                     selectEnable();
29397                 }
29398             }
29399
29400
29401             function behavior(selection) {
29402                 _pointerId = null;
29403                 var matchesSelector = utilPrefixDOMProperty('matchesSelector');
29404                 var delegate = pointerdown;
29405
29406                 if (_selector) {
29407                     delegate = function() {
29408                         var root = this;
29409                         var target = event.target;
29410                         for (; target && target !== root; target = target.parentNode) {
29411                             var datum = target.__data__;
29412
29413                             var entity = datum instanceof osmNote ? datum
29414                                 : datum && datum.properties && datum.properties.entity;
29415
29416                             if (entity && target[matchesSelector](_selector)) {
29417                                 return pointerdown.call(target, entity);
29418                             }
29419                         }
29420                     };
29421                 }
29422
29423                 selection
29424                     .on(_pointerPrefix + 'down.drag' + _selector, delegate);
29425             }
29426
29427
29428             behavior.off = function(selection) {
29429                 selection
29430                     .on(_pointerPrefix + 'down.drag' + _selector, null);
29431             };
29432
29433
29434             behavior.selector = function(_) {
29435                 if (!arguments.length) return _selector;
29436                 _selector = _;
29437                 return behavior;
29438             };
29439
29440
29441             behavior.origin = function(_) {
29442                 if (!arguments.length) return _origin;
29443                 _origin = _;
29444                 return behavior;
29445             };
29446
29447
29448             behavior.cancel = function() {
29449                 select(window)
29450                     .on(_pointerPrefix + 'move.drag', null)
29451                     .on(_pointerPrefix + 'up.drag pointercancel.drag', null);
29452                 return behavior;
29453             };
29454
29455
29456             behavior.target = function() {
29457                 if (!arguments.length) return _target;
29458                 _target = arguments[0];
29459                 _event = eventOf(_target, Array.prototype.slice.call(arguments, 1));
29460                 return behavior;
29461             };
29462
29463
29464             behavior.surface = function() {
29465                 if (!arguments.length) return _surface;
29466                 _surface = arguments[0];
29467                 return behavior;
29468             };
29469
29470
29471             return utilRebind(behavior, dispatch$1, 'on');
29472         }
29473
29474         function modeDragNode(context) {
29475             var mode = {
29476                 id: 'drag-node',
29477                 button: 'browse'
29478             };
29479             var hover = behaviorHover(context).altDisables(true)
29480                 .on('hover', context.ui().sidebar.hover);
29481             var edit = behaviorEdit(context);
29482
29483             var _nudgeInterval;
29484             var _restoreSelectedIDs = [];
29485             var _wasMidpoint = false;
29486             var _isCancelled = false;
29487             var _activeEntity;
29488             var _startLoc;
29489             var _lastLoc;
29490
29491
29492             function startNudge(entity, nudge) {
29493                 if (_nudgeInterval) window.clearInterval(_nudgeInterval);
29494                 _nudgeInterval = window.setInterval(function() {
29495                     context.map().pan(nudge);
29496                     doMove(entity, nudge);
29497                 }, 50);
29498             }
29499
29500
29501             function stopNudge() {
29502                 if (_nudgeInterval) {
29503                     window.clearInterval(_nudgeInterval);
29504                     _nudgeInterval = null;
29505                 }
29506             }
29507
29508
29509             function moveAnnotation(entity) {
29510                 return _t('operations.move.annotation.' + entity.geometry(context.graph()));
29511             }
29512
29513
29514             function connectAnnotation(nodeEntity, targetEntity) {
29515                 var nodeGeometry = nodeEntity.geometry(context.graph());
29516                 var targetGeometry = targetEntity.geometry(context.graph());
29517                 if (nodeGeometry === 'vertex' && targetGeometry === 'vertex') {
29518                     var nodeParentWayIDs = context.graph().parentWays(nodeEntity);
29519                     var targetParentWayIDs = context.graph().parentWays(targetEntity);
29520                     var sharedParentWays = utilArrayIntersection(nodeParentWayIDs, targetParentWayIDs);
29521                     // if both vertices are part of the same way
29522                     if (sharedParentWays.length !== 0) {
29523                         // if the nodes are next to each other, they are merged
29524                         if (sharedParentWays[0].areAdjacent(nodeEntity.id, targetEntity.id)) {
29525                             return _t('operations.connect.annotation.from_vertex.to_adjacent_vertex');
29526                         }
29527                         return _t('operations.connect.annotation.from_vertex.to_sibling_vertex');
29528                     }
29529                 }
29530                 return _t('operations.connect.annotation.from_' + nodeGeometry + '.to_' + targetGeometry);
29531             }
29532
29533
29534             function shouldSnapToNode(target) {
29535                 if (!_activeEntity) return false;
29536                 return _activeEntity.geometry(context.graph()) !== 'vertex' ||
29537                     (target.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(target, context.graph()));
29538             }
29539
29540
29541             function origin(entity) {
29542                 return context.projection(entity.loc);
29543             }
29544
29545
29546             function keydown() {
29547                 if (event.keyCode === utilKeybinding.modifierCodes.alt) {
29548                     if (context.surface().classed('nope')) {
29549                         context.surface()
29550                             .classed('nope-suppressed', true);
29551                     }
29552                     context.surface()
29553                         .classed('nope', false)
29554                         .classed('nope-disabled', true);
29555                 }
29556             }
29557
29558
29559             function keyup() {
29560                 if (event.keyCode === utilKeybinding.modifierCodes.alt) {
29561                     if (context.surface().classed('nope-suppressed')) {
29562                         context.surface()
29563                             .classed('nope', true);
29564                     }
29565                     context.surface()
29566                         .classed('nope-suppressed', false)
29567                         .classed('nope-disabled', false);
29568                 }
29569             }
29570
29571
29572             function start(entity) {
29573                 _wasMidpoint = entity.type === 'midpoint';
29574                 var hasHidden = context.features().hasHiddenConnections(entity, context.graph());
29575                 _isCancelled = !context.editable() || event.sourceEvent.shiftKey || hasHidden;
29576
29577
29578                 if (_isCancelled) {
29579                     if (hasHidden) {
29580                         context.ui().flash
29581                             .duration(4000)
29582                             .text(_t('modes.drag_node.connected_to_hidden'))();
29583                     }
29584                     return drag.cancel();
29585                 }
29586
29587                 if (_wasMidpoint) {
29588                     var midpoint = entity;
29589                     entity = osmNode();
29590                     context.perform(actionAddMidpoint(midpoint, entity));
29591                     entity = context.entity(entity.id);   // get post-action entity
29592
29593                     var vertex = context.surface().selectAll('.' + entity.id);
29594                     drag.target(vertex.node(), entity);
29595
29596                 } else {
29597                     context.perform(actionNoop());
29598                 }
29599
29600                 _activeEntity = entity;
29601                 _startLoc = entity.loc;
29602
29603                 hover.ignoreVertex(entity.geometry(context.graph()) === 'vertex');
29604
29605                 context.surface().selectAll('.' + _activeEntity.id)
29606                     .classed('active', true);
29607
29608                 context.enter(mode);
29609             }
29610
29611
29612             // related code
29613             // - `behavior/draw.js` `datum()`
29614             function datum() {
29615                 var event$1 = event && event.sourceEvent;
29616                 if (!event$1 || event$1.altKey) {
29617                     return {};
29618                 } else {
29619                     // When dragging, snap only to touch targets..
29620                     // (this excludes area fills and active drawing elements)
29621                     var d = event$1.target.__data__;
29622                     return (d && d.properties && d.properties.target) ? d : {};
29623                 }
29624             }
29625
29626
29627             function doMove(entity, nudge) {
29628                 nudge = nudge || [0, 0];
29629
29630                 var currPoint = (event && event.point) || context.projection(_lastLoc);
29631                 var currMouse = geoVecSubtract(currPoint, nudge);
29632                 var loc = context.projection.invert(currMouse);
29633
29634                 if (!_nudgeInterval) {   // If not nudging at the edge of the viewport, try to snap..
29635                     // related code
29636                     // - `mode/drag_node.js`     `doMove()`
29637                     // - `behavior/draw.js`      `click()`
29638                     // - `behavior/draw_way.js`  `move()`
29639                     var d = datum();
29640                     var target = d && d.properties && d.properties.entity;
29641                     var targetLoc = target && target.loc;
29642                     var targetNodes = d && d.properties && d.properties.nodes;
29643                     var edge;
29644
29645                     if (targetLoc) {   // snap to node/vertex - a point target with `.loc`
29646                         if (shouldSnapToNode(target)) {
29647                             loc = targetLoc;
29648                         }
29649
29650                     } else if (targetNodes) {   // snap to way - a line target with `.nodes`
29651                         edge = geoChooseEdge(targetNodes, context.map().mouse(), context.projection, end.id);
29652                         if (edge) {
29653                             loc = edge.loc;
29654                         }
29655                     }
29656                 }
29657
29658                 context.replace(
29659                     actionMoveNode(entity.id, loc)
29660                 );
29661
29662                 // Below here: validations
29663                 var isInvalid = false;
29664
29665                 // Check if this connection to `target` could cause relations to break..
29666                 if (target) {
29667                     isInvalid = hasRelationConflict(entity, target, edge, context.graph());
29668                 }
29669
29670                 // Check if this drag causes the geometry to break..
29671                 if (!isInvalid) {
29672                     isInvalid = hasInvalidGeometry(entity, context.graph());
29673                 }
29674
29675
29676                 var nope = context.surface().classed('nope');
29677                 if (isInvalid === 'relation' || isInvalid === 'restriction') {
29678                     if (!nope) {   // about to nope - show hint
29679                         context.ui().flash
29680                             .duration(4000)
29681                             .text(_t('operations.connect.' + isInvalid,
29682                                 { relation: _mainPresetIndex.item('type/restriction').name() }
29683                             ))();
29684                     }
29685                 } else if (isInvalid) {
29686                     var errorID = isInvalid === 'line' ? 'lines' : 'areas';
29687                     context.ui().flash
29688                         .duration(3000)
29689                         .text(_t('self_intersection.error.' + errorID))();
29690                 } else {
29691                     if (nope) {   // about to un-nope, remove hint
29692                         context.ui().flash
29693                             .duration(1)
29694                             .text('')();
29695                     }
29696                 }
29697
29698
29699                 var nopeDisabled = context.surface().classed('nope-disabled');
29700                 if (nopeDisabled) {
29701                     context.surface()
29702                         .classed('nope', false)
29703                         .classed('nope-suppressed', isInvalid);
29704                 } else {
29705                     context.surface()
29706                         .classed('nope', isInvalid)
29707                         .classed('nope-suppressed', false);
29708                 }
29709
29710                 _lastLoc = loc;
29711             }
29712
29713
29714             // Uses `actionConnect.disabled()` to know whether this connection is ok..
29715             function hasRelationConflict(entity, target, edge, graph) {
29716                 var testGraph = graph.update();  // copy
29717
29718                 // if snapping to way - add midpoint there and consider that the target..
29719                 if (edge) {
29720                     var midpoint = osmNode();
29721                     var action = actionAddMidpoint({
29722                         loc: edge.loc,
29723                         edge: [target.nodes[edge.index - 1], target.nodes[edge.index]]
29724                     }, midpoint);
29725
29726                     testGraph = action(testGraph);
29727                     target = midpoint;
29728                 }
29729
29730                 // can we connect to it?
29731                 var ids = [entity.id, target.id];
29732                 return actionConnect(ids).disabled(testGraph);
29733             }
29734
29735
29736             function hasInvalidGeometry(entity, graph) {
29737                 var parents = graph.parentWays(entity);
29738                 var i, j, k;
29739
29740                 for (i = 0; i < parents.length; i++) {
29741                     var parent = parents[i];
29742                     var nodes = [];
29743                     var activeIndex = null;    // which multipolygon ring contains node being dragged
29744
29745                     // test any parent multipolygons for valid geometry
29746                     var relations = graph.parentRelations(parent);
29747                     for (j = 0; j < relations.length; j++) {
29748                         if (!relations[j].isMultipolygon()) continue;
29749
29750                         var rings = osmJoinWays(relations[j].members, graph);
29751
29752                         // find active ring and test it for self intersections
29753                         for (k = 0; k < rings.length; k++) {
29754                             nodes = rings[k].nodes;
29755                             if (nodes.find(function(n) { return n.id === entity.id; })) {
29756                                 activeIndex = k;
29757                                 if (geoHasSelfIntersections(nodes, entity.id)) {
29758                                     return 'multipolygonMember';
29759                                 }
29760                             }
29761                             rings[k].coords = nodes.map(function(n) { return n.loc; });
29762                         }
29763
29764                         // test active ring for intersections with other rings in the multipolygon
29765                         for (k = 0; k < rings.length; k++) {
29766                             if (k === activeIndex) continue;
29767
29768                             // make sure active ring doesnt cross passive rings
29769                             if (geoHasLineIntersections(rings[activeIndex].nodes, rings[k].nodes, entity.id)) {
29770                                 return 'multipolygonRing';
29771                             }
29772                         }
29773                     }
29774
29775
29776                     // If we still haven't tested this node's parent way for self-intersections.
29777                     // (because it's not a member of a multipolygon), test it now.
29778                     if (activeIndex === null) {
29779                         nodes = parent.nodes.map(function(nodeID) { return graph.entity(nodeID); });
29780                         if (nodes.length && geoHasSelfIntersections(nodes, entity.id)) {
29781                             return parent.geometry(graph);
29782                         }
29783                     }
29784
29785                 }
29786
29787                 return false;
29788             }
29789
29790
29791             function move(entity) {
29792                 if (_isCancelled) return;
29793                 event.sourceEvent.stopPropagation();
29794
29795                 context.surface().classed('nope-disabled', event.sourceEvent.altKey);
29796
29797                 _lastLoc = context.projection.invert(event.point);
29798
29799                 doMove(entity);
29800                 var nudge = geoViewportEdge(event.point, context.map().dimensions());
29801                 if (nudge) {
29802                     startNudge(entity, nudge);
29803                 } else {
29804                     stopNudge();
29805                 }
29806             }
29807
29808             function end(entity) {
29809                 if (_isCancelled) return;
29810
29811                 var wasPoint = entity.geometry(context.graph()) === 'point';
29812
29813                 var d = datum();
29814                 var nope = (d && d.properties && d.properties.nope) || context.surface().classed('nope');
29815                 var target = d && d.properties && d.properties.entity;   // entity to snap to
29816
29817                 if (nope) {   // bounce back
29818                     context.perform(
29819                         _actionBounceBack(entity.id, _startLoc)
29820                     );
29821
29822                 } else if (target && target.type === 'way') {
29823                     var choice = geoChooseEdge(context.graph().childNodes(target), context.map().mouse(), context.projection, entity.id);
29824                     context.replace(
29825                         actionAddMidpoint({
29826                             loc: choice.loc,
29827                             edge: [target.nodes[choice.index - 1], target.nodes[choice.index]]
29828                         }, entity),
29829                         connectAnnotation(entity, target)
29830                     );
29831
29832                 } else if (target && target.type === 'node' && shouldSnapToNode(target)) {
29833                     context.replace(
29834                         actionConnect([target.id, entity.id]),
29835                         connectAnnotation(entity, target)
29836                     );
29837
29838                 } else if (_wasMidpoint) {
29839                     context.replace(
29840                         actionNoop(),
29841                         _t('operations.add.annotation.vertex')
29842                     );
29843
29844                 } else {
29845                     context.replace(
29846                         actionNoop(),
29847                         moveAnnotation(entity)
29848                     );
29849                 }
29850
29851                 if (wasPoint) {
29852                     context.enter(modeSelect(context, [entity.id]));
29853
29854                 } else {
29855                     var reselection = _restoreSelectedIDs.filter(function(id) {
29856                         return context.graph().hasEntity(id);
29857                     });
29858
29859                     if (reselection.length) {
29860                         context.enter(modeSelect(context, reselection));
29861                     } else {
29862                         context.enter(modeBrowse(context));
29863                     }
29864                 }
29865             }
29866
29867
29868             function _actionBounceBack(nodeID, toLoc) {
29869                 var moveNode = actionMoveNode(nodeID, toLoc);
29870                 var action = function(graph, t) {
29871                     // last time through, pop off the bounceback perform.
29872                     // it will then overwrite the initial perform with a moveNode that does nothing
29873                     if (t === 1) context.pop();
29874                     return moveNode(graph, t);
29875                 };
29876                 action.transitionable = true;
29877                 return action;
29878             }
29879
29880
29881             function cancel() {
29882                 drag.cancel();
29883                 context.enter(modeBrowse(context));
29884             }
29885
29886
29887             var drag = behaviorDrag()
29888                 .selector('.layer-touch.points .target')
29889                 .surface(context.container().select('.main-map').node())
29890                 .origin(origin)
29891                 .on('start', start)
29892                 .on('move', move)
29893                 .on('end', end);
29894
29895
29896             mode.enter = function() {
29897                 context.install(hover);
29898                 context.install(edit);
29899
29900                 select(window)
29901                     .on('keydown.dragNode', keydown)
29902                     .on('keyup.dragNode', keyup);
29903
29904                 context.history()
29905                     .on('undone.drag-node', cancel);
29906             };
29907
29908
29909             mode.exit = function() {
29910                 context.ui().sidebar.hover.cancel();
29911                 context.uninstall(hover);
29912                 context.uninstall(edit);
29913
29914                 select(window)
29915                     .on('keydown.dragNode', null)
29916                     .on('keyup.dragNode', null);
29917
29918                 context.history()
29919                     .on('undone.drag-node', null);
29920
29921                 _activeEntity = null;
29922
29923                 context.surface()
29924                     .classed('nope', false)
29925                     .classed('nope-suppressed', false)
29926                     .classed('nope-disabled', false)
29927                     .selectAll('.active')
29928                     .classed('active', false);
29929
29930                 stopNudge();
29931             };
29932
29933
29934             mode.selectedIDs = function() {
29935                 if (!arguments.length) return _activeEntity ? [_activeEntity.id] : [];
29936                 // no assign
29937                 return mode;
29938             };
29939
29940
29941             mode.activeID = function() {
29942                 if (!arguments.length) return _activeEntity && _activeEntity.id;
29943                 // no assign
29944                 return mode;
29945             };
29946
29947
29948             mode.restoreSelectedIDs = function(_) {
29949                 if (!arguments.length) return _restoreSelectedIDs;
29950                 _restoreSelectedIDs = _;
29951                 return mode;
29952             };
29953
29954
29955             mode.behavior = drag;
29956
29957
29958             return mode;
29959         }
29960
29961         function quickselect(arr, k, left, right, compare) {
29962             quickselectStep(arr, k, left || 0, right || (arr.length - 1), compare || defaultCompare);
29963         }
29964
29965         function quickselectStep(arr, k, left, right, compare) {
29966
29967             while (right > left) {
29968                 if (right - left > 600) {
29969                     var n = right - left + 1;
29970                     var m = k - left + 1;
29971                     var z = Math.log(n);
29972                     var s = 0.5 * Math.exp(2 * z / 3);
29973                     var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
29974                     var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
29975                     var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
29976                     quickselectStep(arr, k, newLeft, newRight, compare);
29977                 }
29978
29979                 var t = arr[k];
29980                 var i = left;
29981                 var j = right;
29982
29983                 swap(arr, left, k);
29984                 if (compare(arr[right], t) > 0) swap(arr, left, right);
29985
29986                 while (i < j) {
29987                     swap(arr, i, j);
29988                     i++;
29989                     j--;
29990                     while (compare(arr[i], t) < 0) i++;
29991                     while (compare(arr[j], t) > 0) j--;
29992                 }
29993
29994                 if (compare(arr[left], t) === 0) swap(arr, left, j);
29995                 else {
29996                     j++;
29997                     swap(arr, j, right);
29998                 }
29999
30000                 if (j <= k) left = j + 1;
30001                 if (k <= j) right = j - 1;
30002             }
30003         }
30004
30005         function swap(arr, i, j) {
30006             var tmp = arr[i];
30007             arr[i] = arr[j];
30008             arr[j] = tmp;
30009         }
30010
30011         function defaultCompare(a, b) {
30012             return a < b ? -1 : a > b ? 1 : 0;
30013         }
30014
30015         class RBush {
30016             constructor(maxEntries = 9) {
30017                 // max entries in a node is 9 by default; min node fill is 40% for best performance
30018                 this._maxEntries = Math.max(4, maxEntries);
30019                 this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
30020                 this.clear();
30021             }
30022
30023             all() {
30024                 return this._all(this.data, []);
30025             }
30026
30027             search(bbox) {
30028                 let node = this.data;
30029                 const result = [];
30030
30031                 if (!intersects(bbox, node)) return result;
30032
30033                 const toBBox = this.toBBox;
30034                 const nodesToSearch = [];
30035
30036                 while (node) {
30037                     for (let i = 0; i < node.children.length; i++) {
30038                         const child = node.children[i];
30039                         const childBBox = node.leaf ? toBBox(child) : child;
30040
30041                         if (intersects(bbox, childBBox)) {
30042                             if (node.leaf) result.push(child);
30043                             else if (contains$1(bbox, childBBox)) this._all(child, result);
30044                             else nodesToSearch.push(child);
30045                         }
30046                     }
30047                     node = nodesToSearch.pop();
30048                 }
30049
30050                 return result;
30051             }
30052
30053             collides(bbox) {
30054                 let node = this.data;
30055
30056                 if (!intersects(bbox, node)) return false;
30057
30058                 const nodesToSearch = [];
30059                 while (node) {
30060                     for (let i = 0; i < node.children.length; i++) {
30061                         const child = node.children[i];
30062                         const childBBox = node.leaf ? this.toBBox(child) : child;
30063
30064                         if (intersects(bbox, childBBox)) {
30065                             if (node.leaf || contains$1(bbox, childBBox)) return true;
30066                             nodesToSearch.push(child);
30067                         }
30068                     }
30069                     node = nodesToSearch.pop();
30070                 }
30071
30072                 return false;
30073             }
30074
30075             load(data) {
30076                 if (!(data && data.length)) return this;
30077
30078                 if (data.length < this._minEntries) {
30079                     for (let i = 0; i < data.length; i++) {
30080                         this.insert(data[i]);
30081                     }
30082                     return this;
30083                 }
30084
30085                 // recursively build the tree with the given data from scratch using OMT algorithm
30086                 let node = this._build(data.slice(), 0, data.length - 1, 0);
30087
30088                 if (!this.data.children.length) {
30089                     // save as is if tree is empty
30090                     this.data = node;
30091
30092                 } else if (this.data.height === node.height) {
30093                     // split root if trees have the same height
30094                     this._splitRoot(this.data, node);
30095
30096                 } else {
30097                     if (this.data.height < node.height) {
30098                         // swap trees if inserted one is bigger
30099                         const tmpNode = this.data;
30100                         this.data = node;
30101                         node = tmpNode;
30102                     }
30103
30104                     // insert the small tree into the large tree at appropriate level
30105                     this._insert(node, this.data.height - node.height - 1, true);
30106                 }
30107
30108                 return this;
30109             }
30110
30111             insert(item) {
30112                 if (item) this._insert(item, this.data.height - 1);
30113                 return this;
30114             }
30115
30116             clear() {
30117                 this.data = createNode([]);
30118                 return this;
30119             }
30120
30121             remove(item, equalsFn) {
30122                 if (!item) return this;
30123
30124                 let node = this.data;
30125                 const bbox = this.toBBox(item);
30126                 const path = [];
30127                 const indexes = [];
30128                 let i, parent, goingUp;
30129
30130                 // depth-first iterative tree traversal
30131                 while (node || path.length) {
30132
30133                     if (!node) { // go up
30134                         node = path.pop();
30135                         parent = path[path.length - 1];
30136                         i = indexes.pop();
30137                         goingUp = true;
30138                     }
30139
30140                     if (node.leaf) { // check current node
30141                         const index = findItem(item, node.children, equalsFn);
30142
30143                         if (index !== -1) {
30144                             // item found, remove the item and condense tree upwards
30145                             node.children.splice(index, 1);
30146                             path.push(node);
30147                             this._condense(path);
30148                             return this;
30149                         }
30150                     }
30151
30152                     if (!goingUp && !node.leaf && contains$1(node, bbox)) { // go down
30153                         path.push(node);
30154                         indexes.push(i);
30155                         i = 0;
30156                         parent = node;
30157                         node = node.children[0];
30158
30159                     } else if (parent) { // go right
30160                         i++;
30161                         node = parent.children[i];
30162                         goingUp = false;
30163
30164                     } else node = null; // nothing found
30165                 }
30166
30167                 return this;
30168             }
30169
30170             toBBox(item) { return item; }
30171
30172             compareMinX(a, b) { return a.minX - b.minX; }
30173             compareMinY(a, b) { return a.minY - b.minY; }
30174
30175             toJSON() { return this.data; }
30176
30177             fromJSON(data) {
30178                 this.data = data;
30179                 return this;
30180             }
30181
30182             _all(node, result) {
30183                 const nodesToSearch = [];
30184                 while (node) {
30185                     if (node.leaf) result.push(...node.children);
30186                     else nodesToSearch.push(...node.children);
30187
30188                     node = nodesToSearch.pop();
30189                 }
30190                 return result;
30191             }
30192
30193             _build(items, left, right, height) {
30194
30195                 const N = right - left + 1;
30196                 let M = this._maxEntries;
30197                 let node;
30198
30199                 if (N <= M) {
30200                     // reached leaf level; return leaf
30201                     node = createNode(items.slice(left, right + 1));
30202                     calcBBox(node, this.toBBox);
30203                     return node;
30204                 }
30205
30206                 if (!height) {
30207                     // target height of the bulk-loaded tree
30208                     height = Math.ceil(Math.log(N) / Math.log(M));
30209
30210                     // target number of root entries to maximize storage utilization
30211                     M = Math.ceil(N / Math.pow(M, height - 1));
30212                 }
30213
30214                 node = createNode([]);
30215                 node.leaf = false;
30216                 node.height = height;
30217
30218                 // split the items into M mostly square tiles
30219
30220                 const N2 = Math.ceil(N / M);
30221                 const N1 = N2 * Math.ceil(Math.sqrt(M));
30222
30223                 multiSelect(items, left, right, N1, this.compareMinX);
30224
30225                 for (let i = left; i <= right; i += N1) {
30226
30227                     const right2 = Math.min(i + N1 - 1, right);
30228
30229                     multiSelect(items, i, right2, N2, this.compareMinY);
30230
30231                     for (let j = i; j <= right2; j += N2) {
30232
30233                         const right3 = Math.min(j + N2 - 1, right2);
30234
30235                         // pack each entry recursively
30236                         node.children.push(this._build(items, j, right3, height - 1));
30237                     }
30238                 }
30239
30240                 calcBBox(node, this.toBBox);
30241
30242                 return node;
30243             }
30244
30245             _chooseSubtree(bbox, node, level, path) {
30246                 while (true) {
30247                     path.push(node);
30248
30249                     if (node.leaf || path.length - 1 === level) break;
30250
30251                     let minArea = Infinity;
30252                     let minEnlargement = Infinity;
30253                     let targetNode;
30254
30255                     for (let i = 0; i < node.children.length; i++) {
30256                         const child = node.children[i];
30257                         const area = bboxArea(child);
30258                         const enlargement = enlargedArea(bbox, child) - area;
30259
30260                         // choose entry with the least area enlargement
30261                         if (enlargement < minEnlargement) {
30262                             minEnlargement = enlargement;
30263                             minArea = area < minArea ? area : minArea;
30264                             targetNode = child;
30265
30266                         } else if (enlargement === minEnlargement) {
30267                             // otherwise choose one with the smallest area
30268                             if (area < minArea) {
30269                                 minArea = area;
30270                                 targetNode = child;
30271                             }
30272                         }
30273                     }
30274
30275                     node = targetNode || node.children[0];
30276                 }
30277
30278                 return node;
30279             }
30280
30281             _insert(item, level, isNode) {
30282                 const bbox = isNode ? item : this.toBBox(item);
30283                 const insertPath = [];
30284
30285                 // find the best node for accommodating the item, saving all nodes along the path too
30286                 const node = this._chooseSubtree(bbox, this.data, level, insertPath);
30287
30288                 // put the item into the node
30289                 node.children.push(item);
30290                 extend$1(node, bbox);
30291
30292                 // split on node overflow; propagate upwards if necessary
30293                 while (level >= 0) {
30294                     if (insertPath[level].children.length > this._maxEntries) {
30295                         this._split(insertPath, level);
30296                         level--;
30297                     } else break;
30298                 }
30299
30300                 // adjust bboxes along the insertion path
30301                 this._adjustParentBBoxes(bbox, insertPath, level);
30302             }
30303
30304             // split overflowed node into two
30305             _split(insertPath, level) {
30306                 const node = insertPath[level];
30307                 const M = node.children.length;
30308                 const m = this._minEntries;
30309
30310                 this._chooseSplitAxis(node, m, M);
30311
30312                 const splitIndex = this._chooseSplitIndex(node, m, M);
30313
30314                 const newNode = createNode(node.children.splice(splitIndex, node.children.length - splitIndex));
30315                 newNode.height = node.height;
30316                 newNode.leaf = node.leaf;
30317
30318                 calcBBox(node, this.toBBox);
30319                 calcBBox(newNode, this.toBBox);
30320
30321                 if (level) insertPath[level - 1].children.push(newNode);
30322                 else this._splitRoot(node, newNode);
30323             }
30324
30325             _splitRoot(node, newNode) {
30326                 // split root node
30327                 this.data = createNode([node, newNode]);
30328                 this.data.height = node.height + 1;
30329                 this.data.leaf = false;
30330                 calcBBox(this.data, this.toBBox);
30331             }
30332
30333             _chooseSplitIndex(node, m, M) {
30334                 let index;
30335                 let minOverlap = Infinity;
30336                 let minArea = Infinity;
30337
30338                 for (let i = m; i <= M - m; i++) {
30339                     const bbox1 = distBBox(node, 0, i, this.toBBox);
30340                     const bbox2 = distBBox(node, i, M, this.toBBox);
30341
30342                     const overlap = intersectionArea(bbox1, bbox2);
30343                     const area = bboxArea(bbox1) + bboxArea(bbox2);
30344
30345                     // choose distribution with minimum overlap
30346                     if (overlap < minOverlap) {
30347                         minOverlap = overlap;
30348                         index = i;
30349
30350                         minArea = area < minArea ? area : minArea;
30351
30352                     } else if (overlap === minOverlap) {
30353                         // otherwise choose distribution with minimum area
30354                         if (area < minArea) {
30355                             minArea = area;
30356                             index = i;
30357                         }
30358                     }
30359                 }
30360
30361                 return index || M - m;
30362             }
30363
30364             // sorts node children by the best axis for split
30365             _chooseSplitAxis(node, m, M) {
30366                 const compareMinX = node.leaf ? this.compareMinX : compareNodeMinX;
30367                 const compareMinY = node.leaf ? this.compareMinY : compareNodeMinY;
30368                 const xMargin = this._allDistMargin(node, m, M, compareMinX);
30369                 const yMargin = this._allDistMargin(node, m, M, compareMinY);
30370
30371                 // if total distributions margin value is minimal for x, sort by minX,
30372                 // otherwise it's already sorted by minY
30373                 if (xMargin < yMargin) node.children.sort(compareMinX);
30374             }
30375
30376             // total margin of all possible split distributions where each node is at least m full
30377             _allDistMargin(node, m, M, compare) {
30378                 node.children.sort(compare);
30379
30380                 const toBBox = this.toBBox;
30381                 const leftBBox = distBBox(node, 0, m, toBBox);
30382                 const rightBBox = distBBox(node, M - m, M, toBBox);
30383                 let margin = bboxMargin(leftBBox) + bboxMargin(rightBBox);
30384
30385                 for (let i = m; i < M - m; i++) {
30386                     const child = node.children[i];
30387                     extend$1(leftBBox, node.leaf ? toBBox(child) : child);
30388                     margin += bboxMargin(leftBBox);
30389                 }
30390
30391                 for (let i = M - m - 1; i >= m; i--) {
30392                     const child = node.children[i];
30393                     extend$1(rightBBox, node.leaf ? toBBox(child) : child);
30394                     margin += bboxMargin(rightBBox);
30395                 }
30396
30397                 return margin;
30398             }
30399
30400             _adjustParentBBoxes(bbox, path, level) {
30401                 // adjust bboxes along the given tree path
30402                 for (let i = level; i >= 0; i--) {
30403                     extend$1(path[i], bbox);
30404                 }
30405             }
30406
30407             _condense(path) {
30408                 // go through the path, removing empty nodes and updating bboxes
30409                 for (let i = path.length - 1, siblings; i >= 0; i--) {
30410                     if (path[i].children.length === 0) {
30411                         if (i > 0) {
30412                             siblings = path[i - 1].children;
30413                             siblings.splice(siblings.indexOf(path[i]), 1);
30414
30415                         } else this.clear();
30416
30417                     } else calcBBox(path[i], this.toBBox);
30418                 }
30419             }
30420         }
30421
30422         function findItem(item, items, equalsFn) {
30423             if (!equalsFn) return items.indexOf(item);
30424
30425             for (let i = 0; i < items.length; i++) {
30426                 if (equalsFn(item, items[i])) return i;
30427             }
30428             return -1;
30429         }
30430
30431         // calculate node's bbox from bboxes of its children
30432         function calcBBox(node, toBBox) {
30433             distBBox(node, 0, node.children.length, toBBox, node);
30434         }
30435
30436         // min bounding rectangle of node children from k to p-1
30437         function distBBox(node, k, p, toBBox, destNode) {
30438             if (!destNode) destNode = createNode(null);
30439             destNode.minX = Infinity;
30440             destNode.minY = Infinity;
30441             destNode.maxX = -Infinity;
30442             destNode.maxY = -Infinity;
30443
30444             for (let i = k; i < p; i++) {
30445                 const child = node.children[i];
30446                 extend$1(destNode, node.leaf ? toBBox(child) : child);
30447             }
30448
30449             return destNode;
30450         }
30451
30452         function extend$1(a, b) {
30453             a.minX = Math.min(a.minX, b.minX);
30454             a.minY = Math.min(a.minY, b.minY);
30455             a.maxX = Math.max(a.maxX, b.maxX);
30456             a.maxY = Math.max(a.maxY, b.maxY);
30457             return a;
30458         }
30459
30460         function compareNodeMinX(a, b) { return a.minX - b.minX; }
30461         function compareNodeMinY(a, b) { return a.minY - b.minY; }
30462
30463         function bboxArea(a)   { return (a.maxX - a.minX) * (a.maxY - a.minY); }
30464         function bboxMargin(a) { return (a.maxX - a.minX) + (a.maxY - a.minY); }
30465
30466         function enlargedArea(a, b) {
30467             return (Math.max(b.maxX, a.maxX) - Math.min(b.minX, a.minX)) *
30468                    (Math.max(b.maxY, a.maxY) - Math.min(b.minY, a.minY));
30469         }
30470
30471         function intersectionArea(a, b) {
30472             const minX = Math.max(a.minX, b.minX);
30473             const minY = Math.max(a.minY, b.minY);
30474             const maxX = Math.min(a.maxX, b.maxX);
30475             const maxY = Math.min(a.maxY, b.maxY);
30476
30477             return Math.max(0, maxX - minX) *
30478                    Math.max(0, maxY - minY);
30479         }
30480
30481         function contains$1(a, b) {
30482             return a.minX <= b.minX &&
30483                    a.minY <= b.minY &&
30484                    b.maxX <= a.maxX &&
30485                    b.maxY <= a.maxY;
30486         }
30487
30488         function intersects(a, b) {
30489             return b.minX <= a.maxX &&
30490                    b.minY <= a.maxY &&
30491                    b.maxX >= a.minX &&
30492                    b.maxY >= a.minY;
30493         }
30494
30495         function createNode(children) {
30496             return {
30497                 children,
30498                 height: 1,
30499                 leaf: true,
30500                 minX: Infinity,
30501                 minY: Infinity,
30502                 maxX: -Infinity,
30503                 maxY: -Infinity
30504             };
30505         }
30506
30507         // sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
30508         // combines selection algorithm with binary divide & conquer approach
30509
30510         function multiSelect(arr, left, right, n, compare) {
30511             const stack = [left, right];
30512
30513             while (stack.length) {
30514                 right = stack.pop();
30515                 left = stack.pop();
30516
30517                 if (right - left <= n) continue;
30518
30519                 const mid = left + Math.ceil((right - left) / n / 2) * n;
30520                 quickselect(arr, mid, left, right, compare);
30521
30522                 stack.push(left, mid, mid, right);
30523             }
30524         }
30525
30526         const tiler = utilTiler();
30527         const dispatch$1 = dispatch('loaded');
30528         const _tileZoom = 14;
30529         const _krUrlRoot = 'https://www.keepright.at';
30530         let _krData = { errorTypes: {}, localizeStrings: {} };
30531
30532         // This gets reassigned if reset
30533         let _cache;
30534
30535         const _krRuleset = [
30536           // no 20 - multiple node on same spot - these are mostly boundaries overlapping roads
30537           30, 40, 50, 60, 70, 90, 100, 110, 120, 130, 150, 160, 170, 180,
30538           190, 191, 192, 193, 194, 195, 196, 197, 198,
30539           200, 201, 202, 203, 204, 205, 206, 207, 208, 210, 220,
30540           230, 231, 232, 270, 280, 281, 282, 283, 284, 285,
30541           290, 291, 292, 293, 294, 295, 296, 297, 298, 300, 310, 311, 312, 313,
30542           320, 350, 360, 370, 380, 390, 400, 401, 402, 410, 411, 412, 413
30543         ];
30544
30545
30546         function abortRequest(controller) {
30547           if (controller) {
30548             controller.abort();
30549           }
30550         }
30551
30552         function abortUnwantedRequests(cache, tiles) {
30553           Object.keys(cache.inflightTile).forEach(k => {
30554             const wanted = tiles.find(tile => k === tile.id);
30555             if (!wanted) {
30556               abortRequest(cache.inflightTile[k]);
30557               delete cache.inflightTile[k];
30558             }
30559           });
30560         }
30561
30562
30563         function encodeIssueRtree(d) {
30564           return { minX: d.loc[0], minY: d.loc[1], maxX: d.loc[0], maxY: d.loc[1], data: d };
30565         }
30566
30567
30568         // Replace or remove QAItem from rtree
30569         function updateRtree(item, replace) {
30570           _cache.rtree.remove(item, (a, b) => a.data.id === b.data.id);
30571
30572           if (replace) {
30573             _cache.rtree.insert(item);
30574           }
30575         }
30576
30577
30578         function tokenReplacements(d) {
30579           if (!(d instanceof QAItem)) return;
30580
30581           const htmlRegex = new RegExp(/<\/[a-z][\s\S]*>/);
30582           const replacements = {};
30583
30584           const issueTemplate = _krData.errorTypes[d.whichType];
30585           if (!issueTemplate) {
30586             /* eslint-disable no-console */
30587             console.log('No Template: ', d.whichType);
30588             console.log('  ', d.description);
30589             /* eslint-enable no-console */
30590             return;
30591           }
30592
30593           // some descriptions are just fixed text
30594           if (!issueTemplate.regex) return;
30595
30596           // regex pattern should match description with variable details captured
30597           const errorRegex = new RegExp(issueTemplate.regex, 'i');
30598           const errorMatch = errorRegex.exec(d.description);
30599           if (!errorMatch) {
30600             /* eslint-disable no-console */
30601             console.log('Unmatched: ', d.whichType);
30602             console.log('  ', d.description);
30603             console.log('  ', errorRegex);
30604             /* eslint-enable no-console */
30605             return;
30606           }
30607
30608           for (let i = 1; i < errorMatch.length; i++) {   // skip first
30609             let capture = errorMatch[i];
30610             let idType;
30611
30612             idType = 'IDs' in issueTemplate ? issueTemplate.IDs[i-1] : '';
30613             if (idType && capture) {   // link IDs if present in the capture
30614               capture = parseError(capture, idType);
30615             } else if (htmlRegex.test(capture)) {   // escape any html in non-IDs
30616               capture = '\\' +  capture + '\\';
30617             } else {
30618               const compare = capture.toLowerCase();
30619               if (_krData.localizeStrings[compare]) {   // some replacement strings can be localized
30620                 capture = _t('QA.keepRight.error_parts.' + _krData.localizeStrings[compare]);
30621               }
30622             }
30623
30624             replacements['var' + i] = capture;
30625           }
30626
30627           return replacements;
30628         }
30629
30630
30631         function parseError(capture, idType) {
30632           const compare = capture.toLowerCase();
30633           if (_krData.localizeStrings[compare]) {   // some replacement strings can be localized
30634             capture = _t('QA.keepRight.error_parts.' + _krData.localizeStrings[compare]);
30635           }
30636
30637           switch (idType) {
30638             // link a string like "this node"
30639             case 'this':
30640               capture = linkErrorObject(capture);
30641               break;
30642
30643             case 'url':
30644               capture = linkURL(capture);
30645               break;
30646
30647             // link an entity ID
30648             case 'n':
30649             case 'w':
30650             case 'r':
30651               capture = linkEntity(idType + capture);
30652               break;
30653
30654             // some errors have more complex ID lists/variance
30655             case '20':
30656               capture = parse20(capture);
30657               break;
30658             case '211':
30659               capture = parse211(capture);
30660               break;
30661             case '231':
30662               capture = parse231(capture);
30663               break;
30664             case '294':
30665               capture = parse294(capture);
30666               break;
30667             case '370':
30668               capture = parse370(capture);
30669               break;
30670           }
30671
30672           return capture;
30673
30674
30675           function linkErrorObject(d) {
30676             return `<a class="error_object_link">${d}</a>`;
30677           }
30678
30679           function linkEntity(d) {
30680             return `<a class="error_entity_link">${d}</a>`;
30681           }
30682
30683           function linkURL(d) {
30684             return `<a class="kr_external_link" target="_blank" href="${d}">${d}</a>`;
30685           }
30686
30687           // arbitrary node list of form: #ID, #ID, #ID...
30688           function parse211(capture) {
30689             let newList = [];
30690             const items = capture.split(', ');
30691
30692             items.forEach(item => {
30693               // ID has # at the front
30694               let id = linkEntity('n' + item.slice(1));
30695               newList.push(id);
30696             });
30697
30698             return newList.join(', ');
30699           }
30700
30701           // arbitrary way list of form: #ID(layer),#ID(layer),#ID(layer)...
30702           function parse231(capture) {
30703             let newList = [];
30704             // unfortunately 'layer' can itself contain commas, so we split on '),'
30705             const items = capture.split('),');
30706
30707             items.forEach(item => {
30708               const match = item.match(/\#(\d+)\((.+)\)?/);
30709               if (match !== null && match.length > 2) {
30710                 newList.push(linkEntity('w' + match[1]) + ' ' +
30711                   _t('QA.keepRight.errorTypes.231.layer', { layer: match[2] })
30712                 );
30713               }
30714             });
30715
30716             return newList.join(', ');
30717           }
30718
30719           // arbitrary node/relation list of form: from node #ID,to relation #ID,to node #ID...
30720           function parse294(capture) {
30721             let newList = [];
30722             const items = capture.split(',');
30723
30724             items.forEach(item => {
30725               // item of form "from/to node/relation #ID"
30726               item = item.split(' ');
30727
30728               // to/from role is more clear in quotes
30729               const role = `"${item[0]}"`;
30730
30731               // first letter of node/relation provides the type
30732               const idType = item[1].slice(0,1);
30733
30734               // ID has # at the front
30735               let id = item[2].slice(1);
30736               id = linkEntity(idType + id);
30737
30738               newList.push(`${role} ${item[1]} ${id}`);
30739             });
30740
30741             return newList.join(', ');
30742           }
30743
30744           // may or may not include the string "(including the name 'name')"
30745           function parse370(capture) {
30746             if (!capture) return '';
30747
30748             const match = capture.match(/\(including the name (\'.+\')\)/);
30749             if (match && match.length) {
30750               return _t('QA.keepRight.errorTypes.370.including_the_name', { name: match[1] });
30751             }
30752             return '';
30753           }
30754
30755           // arbitrary node list of form: #ID,#ID,#ID...
30756           function parse20(capture) {
30757             let newList = [];
30758             const items = capture.split(',');
30759
30760             items.forEach(item => {
30761               // ID has # at the front
30762               const id = linkEntity('n' + item.slice(1));
30763               newList.push(id);
30764             });
30765
30766             return newList.join(', ');
30767           }
30768         }
30769
30770
30771         var serviceKeepRight = {
30772           title: 'keepRight',
30773
30774           init() {
30775             _mainFileFetcher.get('keepRight')
30776               .then(d => _krData = d);
30777
30778             if (!_cache) {
30779               this.reset();
30780             }
30781
30782             this.event = utilRebind(this, dispatch$1, 'on');
30783           },
30784
30785           reset() {
30786             if (_cache) {
30787               Object.values(_cache.inflightTile).forEach(abortRequest);
30788             }
30789
30790             _cache = {
30791               data: {},
30792               loadedTile: {},
30793               inflightTile: {},
30794               inflightPost: {},
30795               closed: {},
30796               rtree: new RBush()
30797             };
30798           },
30799
30800
30801           // KeepRight API:  http://osm.mueschelsoft.de/keepright/interfacing.php
30802           loadIssues(projection) {
30803             const options = {
30804               format: 'geojson',
30805               ch: _krRuleset
30806             };
30807
30808             // determine the needed tiles to cover the view
30809             const tiles = tiler
30810               .zoomExtent([_tileZoom, _tileZoom])
30811               .getTiles(projection);
30812
30813             // abort inflight requests that are no longer needed
30814             abortUnwantedRequests(_cache, tiles);
30815
30816             // issue new requests..
30817             tiles.forEach(tile => {
30818               if (_cache.loadedTile[tile.id] || _cache.inflightTile[tile.id]) return;
30819
30820               const [ left, top, right, bottom ] = tile.extent.rectangle();
30821               const params = Object.assign({}, options, { left, bottom, right, top });
30822               const url = `${_krUrlRoot}/export.php?` + utilQsString(params);
30823               const controller = new AbortController();
30824
30825               _cache.inflightTile[tile.id] = controller;
30826
30827               d3_json(url, { signal: controller.signal })
30828                 .then(data => {
30829                   delete _cache.inflightTile[tile.id];
30830                   _cache.loadedTile[tile.id] = true;
30831                   if (!data || !data.features || !data.features.length) {
30832                     throw new Error('No Data');
30833                   }
30834
30835                   data.features.forEach(feature => {
30836                     const {
30837                       properties: {
30838                         error_type: itemType,
30839                         error_id: id,
30840                         comment = null,
30841                         object_id: objectId,
30842                         object_type: objectType,
30843                         schema,
30844                         title
30845                       }
30846                     } = feature;
30847                     let {
30848                       geometry: { coordinates: loc },
30849                       properties: { description = '' }
30850                     } = feature;
30851
30852                     // if there is a parent, save its error type e.g.:
30853                     //  Error 191 = "highway-highway"
30854                     //  Error 190 = "intersections without junctions"  (parent)
30855                     const issueTemplate = _krData.errorTypes[itemType];
30856                     const parentIssueType = (Math.floor(itemType / 10) * 10).toString();
30857
30858                     // try to handle error type directly, fallback to parent error type.
30859                     const whichType = issueTemplate ? itemType : parentIssueType;
30860                     const whichTemplate = _krData.errorTypes[whichType];
30861
30862                     // Rewrite a few of the errors at this point..
30863                     // This is done to make them easier to linkify and translate.
30864                     switch (whichType) {
30865                       case '170':
30866                         description = `This feature has a FIXME tag: ${description}`;
30867                         break;
30868                       case '292':
30869                       case '293':
30870                         description = description.replace('A turn-', 'This turn-');
30871                         break;
30872                       case '294':
30873                       case '295':
30874                       case '296':
30875                       case '297':
30876                       case '298':
30877                         description = `This turn-restriction~${description}`;
30878                         break;
30879                       case '300':
30880                         description = 'This highway is missing a maxspeed tag';
30881                         break;
30882                       case '411':
30883                       case '412':
30884                       case '413':
30885                         description = `This feature~${description}`;
30886                         break;
30887                     }
30888
30889                     // move markers slightly so it doesn't obscure the geometry,
30890                     // then move markers away from other coincident markers
30891                     let coincident = false;
30892                     do {
30893                       // first time, move marker up. after that, move marker right.
30894                       let delta = coincident ? [0.00001, 0] : [0, 0.00001];
30895                       loc = geoVecAdd(loc, delta);
30896                       let bbox = geoExtent(loc).bbox();
30897                       coincident = _cache.rtree.search(bbox).length;
30898                     } while (coincident);
30899
30900                     let d = new QAItem(loc, this, itemType, id, {
30901                       comment,
30902                       description,
30903                       whichType,
30904                       parentIssueType,
30905                       severity: whichTemplate.severity || 'error',
30906                       objectId,
30907                       objectType,
30908                       schema,
30909                       title
30910                     });
30911
30912                     d.replacements = tokenReplacements(d);
30913
30914                     _cache.data[id] = d;
30915                     _cache.rtree.insert(encodeIssueRtree(d));
30916                   });
30917
30918                   dispatch$1.call('loaded');
30919                 })
30920                 .catch(() => {
30921                   delete _cache.inflightTile[tile.id];
30922                   _cache.loadedTile[tile.id] = true;
30923                 });
30924
30925             });
30926           },
30927
30928
30929           postUpdate(d, callback) {
30930             if (_cache.inflightPost[d.id]) {
30931               return callback({ message: 'Error update already inflight', status: -2 }, d);
30932             }
30933
30934             const params = { schema: d.schema, id: d.id };
30935
30936             if (d.newStatus) {
30937               params.st = d.newStatus;
30938             }
30939             if (d.newComment !== undefined) {
30940               params.co = d.newComment;
30941             }
30942
30943             // NOTE: This throws a CORS err, but it seems successful.
30944             // We don't care too much about the response, so this is fine.
30945             const url = `${_krUrlRoot}/comment.php?` + utilQsString(params);
30946             const controller = new AbortController();
30947
30948             _cache.inflightPost[d.id] = controller;
30949
30950             // Since this is expected to throw an error just continue as if it worked
30951             // (worst case scenario the request truly fails and issue will show up if iD restarts)
30952             d3_json(url, { signal: controller.signal })
30953               .finally(() => {
30954                 delete _cache.inflightPost[d.id];
30955
30956                 if (d.newStatus === 'ignore') {
30957                   // ignore permanently (false positive)
30958                   this.removeItem(d);
30959                 } else if (d.newStatus === 'ignore_t') {
30960                   // ignore temporarily (error fixed)
30961                   this.removeItem(d);
30962                   _cache.closed[`${d.schema}:${d.id}`] = true;
30963                 } else {
30964                   d = this.replaceItem(d.update({
30965                     comment: d.newComment,
30966                     newComment: undefined,
30967                     newState: undefined
30968                   }));
30969                 }
30970
30971                 if (callback) callback(null, d);
30972               });
30973           },
30974
30975           // Get all cached QAItems covering the viewport
30976           getItems(projection) {
30977             const viewport = projection.clipExtent();
30978             const min = [viewport[0][0], viewport[1][1]];
30979             const max = [viewport[1][0], viewport[0][1]];
30980             const bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
30981
30982             return _cache.rtree.search(bbox).map(d => d.data);
30983           },
30984
30985           // Get a QAItem from cache
30986           // NOTE: Don't change method name until UI v3 is merged
30987           getError(id) {
30988             return _cache.data[id];
30989           },
30990
30991           // Replace a single QAItem in the cache
30992           replaceItem(item) {
30993             if (!(item instanceof QAItem) || !item.id) return;
30994
30995             _cache.data[item.id] = item;
30996             updateRtree(encodeIssueRtree(item), true); // true = replace
30997             return item;
30998           },
30999
31000           // Remove a single QAItem from the cache
31001           removeItem(item) {
31002             if (!(item instanceof QAItem) || !item.id) return;
31003
31004             delete _cache.data[item.id];
31005             updateRtree(encodeIssueRtree(item), false); // false = remove
31006           },
31007
31008           issueURL(item) {
31009             return `${_krUrlRoot}/report_map.php?schema=${item.schema}&error=${item.id}`;
31010           },
31011
31012           // Get an array of issues closed during this session.
31013           // Used to populate `closed:keepright` changeset tag
31014           getClosedIDs() {
31015             return Object.keys(_cache.closed).sort();
31016           }
31017
31018         };
31019
31020         const tiler$1 = utilTiler();
31021         const dispatch$2 = dispatch('loaded');
31022         const _tileZoom$1 = 14;
31023         const _impOsmUrls = {
31024           ow: 'https://grab.community.improve-osm.org/directionOfFlowService',
31025           mr: 'https://grab.community.improve-osm.org/missingGeoService',
31026           tr: 'https://grab.community.improve-osm.org/turnRestrictionService'
31027         };
31028         let _impOsmData = { icons: {} };
31029
31030
31031         // This gets reassigned if reset
31032         let _cache$1;
31033
31034         function abortRequest$1(i) {
31035           Object.values(i).forEach(controller => {
31036             if (controller) {
31037               controller.abort();
31038             }
31039           });
31040         }
31041
31042         function abortUnwantedRequests$1(cache, tiles) {
31043           Object.keys(cache.inflightTile).forEach(k => {
31044             const wanted = tiles.find(tile => k === tile.id);
31045             if (!wanted) {
31046               abortRequest$1(cache.inflightTile[k]);
31047               delete cache.inflightTile[k];
31048             }
31049           });
31050         }
31051
31052         function encodeIssueRtree$1(d) {
31053           return { minX: d.loc[0], minY: d.loc[1], maxX: d.loc[0], maxY: d.loc[1], data: d };
31054         }
31055
31056         // Replace or remove QAItem from rtree
31057         function updateRtree$1(item, replace) {
31058           _cache$1.rtree.remove(item, (a, b) => a.data.id === b.data.id);
31059
31060           if (replace) {
31061             _cache$1.rtree.insert(item);
31062           }
31063         }
31064
31065         function linkErrorObject(d) {
31066           return `<a class="error_object_link">${d}</a>`;
31067         }
31068
31069         function linkEntity(d) {
31070           return `<a class="error_entity_link">${d}</a>`;
31071         }
31072
31073         function pointAverage(points) {
31074           if (points.length) {
31075             const sum = points.reduce(
31076               (acc, point) => geoVecAdd(acc, [point.lon, point.lat]),
31077               [0,0]
31078             );
31079             return geoVecScale(sum, 1 / points.length);
31080           } else {
31081             return [0,0];
31082           }
31083         }
31084
31085         function relativeBearing(p1, p2) {
31086           let angle = Math.atan2(p2.lon - p1.lon, p2.lat - p1.lat);
31087           if (angle < 0) {
31088             angle += 2 * Math.PI;
31089           }
31090
31091           // Return degrees
31092           return angle * 180 / Math.PI;
31093         }
31094
31095         // Assuming range [0,360)
31096         function cardinalDirection(bearing) {
31097           const dir = 45 * Math.round(bearing / 45);
31098           const compass = {
31099             0: 'north',
31100             45: 'northeast',
31101             90: 'east',
31102             135: 'southeast',
31103             180: 'south',
31104             225: 'southwest',
31105             270: 'west',
31106             315: 'northwest',
31107             360: 'north'
31108           };
31109
31110           return _t(`QA.improveOSM.directions.${compass[dir]}`);
31111         }
31112
31113         // Errors shouldn't obscure eachother
31114         function preventCoincident(loc, bumpUp) {
31115           let coincident = false;
31116           do {
31117             // first time, move marker up. after that, move marker right.
31118             let delta = coincident ? [0.00001, 0] : (bumpUp ? [0, 0.00001] : [0, 0]);
31119             loc = geoVecAdd(loc, delta);
31120             let bbox = geoExtent(loc).bbox();
31121             coincident = _cache$1.rtree.search(bbox).length;
31122           } while (coincident);
31123
31124           return loc;
31125         }
31126
31127         var serviceImproveOSM = {
31128           title: 'improveOSM',
31129
31130           init() {
31131             _mainFileFetcher.get('qa_data')
31132               .then(d => _impOsmData = d.improveOSM);
31133
31134             if (!_cache$1) {
31135               this.reset();
31136             }
31137
31138             this.event = utilRebind(this, dispatch$2, 'on');
31139           },
31140
31141           reset() {
31142             if (_cache$1) {
31143               Object.values(_cache$1.inflightTile).forEach(abortRequest$1);
31144             }
31145             _cache$1 = {
31146               data: {},
31147               loadedTile: {},
31148               inflightTile: {},
31149               inflightPost: {},
31150               closed: {},
31151               rtree: new RBush()
31152             };
31153           },
31154
31155           loadIssues(projection) {
31156             const options = {
31157               client: 'iD',
31158               status: 'OPEN',
31159               zoom: '19' // Use a high zoom so that clusters aren't returned
31160             };
31161
31162             // determine the needed tiles to cover the view
31163             const tiles = tiler$1
31164               .zoomExtent([_tileZoom$1, _tileZoom$1])
31165               .getTiles(projection);
31166
31167             // abort inflight requests that are no longer needed
31168             abortUnwantedRequests$1(_cache$1, tiles);
31169
31170             // issue new requests..
31171             tiles.forEach(tile => {
31172               if (_cache$1.loadedTile[tile.id] || _cache$1.inflightTile[tile.id]) return;
31173
31174               const [ east, north, west, south ] = tile.extent.rectangle();
31175               const params = Object.assign({}, options, { east, south, west, north });
31176
31177               // 3 separate requests to store for each tile
31178               const requests = {};
31179
31180               Object.keys(_impOsmUrls).forEach(k => {
31181                 // We exclude WATER from missing geometry as it doesn't seem useful
31182                 // We use most confident one-way and turn restrictions only, still have false positives
31183                 const kParams = Object.assign({},
31184                   params,
31185                   (k === 'mr') ? { type: 'PARKING,ROAD,BOTH,PATH' } : { confidenceLevel: 'C1' }
31186                 );
31187                 const url = `${_impOsmUrls[k]}/search?` + utilQsString(kParams);
31188                 const controller = new AbortController();
31189
31190                 requests[k] = controller;
31191
31192                 d3_json(url, { signal: controller.signal })
31193                   .then(data => {
31194                     delete _cache$1.inflightTile[tile.id][k];
31195                     if (!Object.keys(_cache$1.inflightTile[tile.id]).length) {
31196                       delete _cache$1.inflightTile[tile.id];
31197                       _cache$1.loadedTile[tile.id] = true;
31198                     }
31199
31200                     // Road segments at high zoom == oneways
31201                     if (data.roadSegments) {
31202                       data.roadSegments.forEach(feature => {
31203                         // Position error at the approximate middle of the segment
31204                         const { points, wayId, fromNodeId, toNodeId } = feature;
31205                         const itemId = `${wayId}${fromNodeId}${toNodeId}`;
31206                         let mid = points.length / 2;
31207                         let loc;
31208
31209                         // Even number of points, find midpoint of the middle two
31210                         // Odd number of points, use position of very middle point
31211                         if (mid % 1 === 0) {
31212                           loc = pointAverage([points[mid - 1], points[mid]]);
31213                         } else {
31214                           mid = points[Math.floor(mid)];
31215                           loc = [mid.lon, mid.lat];
31216                         }
31217
31218                         // One-ways can land on same segment in opposite direction
31219                         loc = preventCoincident(loc, false);
31220
31221                         let d = new QAItem(loc, this, k, itemId, {
31222                           issueKey: k, // used as a category
31223                           identifier: { // used to post changes
31224                             wayId,
31225                             fromNodeId,
31226                             toNodeId
31227                           },
31228                           objectId: wayId,
31229                           objectType: 'way'
31230                         });
31231
31232                         // Variables used in the description
31233                         d.replacements = {
31234                           percentage: feature.percentOfTrips,
31235                           num_trips: feature.numberOfTrips,
31236                           highway: linkErrorObject(_t('QA.keepRight.error_parts.highway')),
31237                           from_node: linkEntity('n' + feature.fromNodeId),
31238                           to_node: linkEntity('n' + feature.toNodeId)
31239                         };
31240
31241                         _cache$1.data[d.id] = d;
31242                         _cache$1.rtree.insert(encodeIssueRtree$1(d));
31243                       });
31244                     }
31245
31246                     // Tiles at high zoom == missing roads
31247                     if (data.tiles) {
31248                       data.tiles.forEach(feature => {
31249                         const { type, x, y, numberOfTrips } = feature;
31250                         const geoType = type.toLowerCase();
31251                         const itemId = `${geoType}${x}${y}${numberOfTrips}`;
31252
31253                         // Average of recorded points should land on the missing geometry
31254                         // Missing geometry could happen to land on another error
31255                         let loc = pointAverage(feature.points);
31256                         loc = preventCoincident(loc, false);
31257
31258                         let d = new QAItem(loc, this, `${k}-${geoType}`, itemId, {
31259                           issueKey: k,
31260                           identifier: { x, y }
31261                         });
31262
31263                         d.replacements = {
31264                           num_trips: numberOfTrips,
31265                           geometry_type: _t(`QA.improveOSM.geometry_types.${geoType}`)
31266                         };
31267
31268                         // -1 trips indicates data came from a 3rd party
31269                         if (numberOfTrips === -1) {
31270                           d.desc = _t('QA.improveOSM.error_types.mr.description_alt', d.replacements);
31271                         }
31272
31273                         _cache$1.data[d.id] = d;
31274                         _cache$1.rtree.insert(encodeIssueRtree$1(d));
31275                       });
31276                     }
31277
31278                     // Entities at high zoom == turn restrictions
31279                     if (data.entities) {
31280                       data.entities.forEach(feature => {
31281                         const { point, id, segments, numberOfPasses, turnType } = feature;
31282                         const itemId = `${id.replace(/[,:+#]/g, '_')}`;
31283
31284                         // Turn restrictions could be missing at same junction
31285                         // We also want to bump the error up so node is accessible
31286                         const loc = preventCoincident([point.lon, point.lat], true);
31287
31288                         // Elements are presented in a strange way
31289                         const ids = id.split(',');
31290                         const from_way = ids[0];
31291                         const via_node = ids[3];
31292                         const to_way = ids[2].split(':')[1];
31293
31294                         let d = new QAItem(loc, this, k, itemId, {
31295                           issueKey: k,
31296                           identifier: id,
31297                           objectId: via_node,
31298                           objectType: 'node'
31299                         });
31300
31301                         // Travel direction along from_way clarifies the turn restriction
31302                         const [ p1, p2 ] = segments[0].points;
31303                         const dir_of_travel = cardinalDirection(relativeBearing(p1, p2));
31304
31305                         // Variables used in the description
31306                         d.replacements = {
31307                           num_passed: numberOfPasses,
31308                           num_trips: segments[0].numberOfTrips,
31309                           turn_restriction: turnType.toLowerCase(),
31310                           from_way: linkEntity('w' + from_way),
31311                           to_way: linkEntity('w' + to_way),
31312                           travel_direction: dir_of_travel,
31313                           junction: linkErrorObject(_t('QA.keepRight.error_parts.this_node'))
31314                         };
31315
31316                         _cache$1.data[d.id] = d;
31317                         _cache$1.rtree.insert(encodeIssueRtree$1(d));
31318                         dispatch$2.call('loaded');
31319                       });
31320                     }
31321                   })
31322                   .catch(() => {
31323                     delete _cache$1.inflightTile[tile.id][k];
31324                     if (!Object.keys(_cache$1.inflightTile[tile.id]).length) {
31325                       delete _cache$1.inflightTile[tile.id];
31326                       _cache$1.loadedTile[tile.id] = true;
31327                     }
31328                   });
31329               });
31330
31331               _cache$1.inflightTile[tile.id] = requests;
31332             });
31333           },
31334
31335           getComments(item) {
31336             // If comments already retrieved no need to do so again
31337             if (item.comments) {
31338               return Promise.resolve(item);
31339             }
31340
31341             const key = item.issueKey;
31342             let qParams = {};
31343
31344             if (key === 'ow') {
31345               qParams = item.identifier;
31346             } else if (key === 'mr') {
31347               qParams.tileX = item.identifier.x;
31348               qParams.tileY = item.identifier.y;
31349             } else if (key === 'tr') {
31350               qParams.targetId = item.identifier;
31351             }
31352
31353             const url = `${_impOsmUrls[key]}/retrieveComments?` + utilQsString(qParams);
31354             const cacheComments = data => {
31355               // Assign directly for immediate use afterwards
31356               // comments are served newest to oldest
31357               item.comments = data.comments ? data.comments.reverse() : [];
31358               this.replaceItem(item);
31359             };
31360
31361             return d3_json(url).then(cacheComments).then(() => item);
31362           },
31363
31364           postUpdate(d, callback) {
31365             if (!serviceOsm.authenticated()) { // Username required in payload
31366               return callback({ message: 'Not Authenticated', status: -3}, d);
31367             }
31368             if (_cache$1.inflightPost[d.id]) {
31369               return callback({ message: 'Error update already inflight', status: -2 }, d);
31370             }
31371
31372             // Payload can only be sent once username is established
31373             serviceOsm.userDetails(sendPayload.bind(this));
31374
31375             function sendPayload(err, user) {
31376               if (err) { return callback(err, d); }
31377
31378               const key = d.issueKey;
31379               const url = `${_impOsmUrls[key]}/comment`;
31380               const payload = {
31381                 username: user.display_name,
31382                 targetIds: [ d.identifier ]
31383               };
31384
31385               if (d.newStatus) {
31386                 payload.status = d.newStatus;
31387                 payload.text = 'status changed';
31388               }
31389
31390               // Comment take place of default text
31391               if (d.newComment) {
31392                 payload.text = d.newComment;
31393               }
31394
31395               const controller = new AbortController();
31396               _cache$1.inflightPost[d.id] = controller;
31397
31398               const options = {
31399                 method: 'POST',
31400                 signal: controller.signal,
31401                 body: JSON.stringify(payload)
31402               };
31403
31404               d3_json(url, options)
31405                 .then(() => {
31406                   delete _cache$1.inflightPost[d.id];
31407
31408                   // Just a comment, update error in cache
31409                   if (!d.newStatus) {
31410                     const now = new Date();
31411                     let comments = d.comments ? d.comments : [];
31412
31413                     comments.push({
31414                       username: payload.username,
31415                       text: payload.text,
31416                       timestamp: now.getTime() / 1000
31417                     });
31418
31419                     this.replaceItem(d.update({
31420                       comments: comments,
31421                       newComment: undefined
31422                     }));
31423                   } else {
31424                     this.removeItem(d);
31425                     if (d.newStatus === 'SOLVED') {
31426                       // Keep track of the number of issues closed per type to tag the changeset
31427                       if (!(d.issueKey in _cache$1.closed)) {
31428                         _cache$1.closed[d.issueKey] = 0;
31429                       }
31430                       _cache$1.closed[d.issueKey] += 1;
31431                     }
31432                   }
31433                   if (callback) callback(null, d);
31434                 })
31435                 .catch(err => {
31436                   delete _cache$1.inflightPost[d.id];
31437                   if (callback) callback(err.message);
31438                 });
31439             }
31440           },
31441
31442
31443           // Get all cached QAItems covering the viewport
31444           getItems(projection) {
31445             const viewport = projection.clipExtent();
31446             const min = [viewport[0][0], viewport[1][1]];
31447             const max = [viewport[1][0], viewport[0][1]];
31448             const bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
31449
31450             return _cache$1.rtree.search(bbox).map(d => d.data);
31451           },
31452
31453           // Get a QAItem from cache
31454           // NOTE: Don't change method name until UI v3 is merged
31455           getError(id) {
31456             return _cache$1.data[id];
31457           },
31458
31459           // get the name of the icon to display for this item
31460           getIcon(itemType) {
31461             return _impOsmData.icons[itemType];
31462           },
31463
31464           // Replace a single QAItem in the cache
31465           replaceItem(issue) {
31466             if (!(issue instanceof QAItem) || !issue.id) return;
31467
31468             _cache$1.data[issue.id] = issue;
31469             updateRtree$1(encodeIssueRtree$1(issue), true); // true = replace
31470             return issue;
31471           },
31472
31473           // Remove a single QAItem from the cache
31474           removeItem(issue) {
31475             if (!(issue instanceof QAItem) || !issue.id) return;
31476
31477             delete _cache$1.data[issue.id];
31478             updateRtree$1(encodeIssueRtree$1(issue), false); // false = remove
31479           },
31480
31481           // Used to populate `closed:improveosm:*` changeset tags
31482           getClosedCounts() {
31483             return _cache$1.closed;
31484           }
31485         };
31486
31487         var defaults = createCommonjsModule(function (module) {
31488         function getDefaults() {
31489           return {
31490             baseUrl: null,
31491             breaks: false,
31492             gfm: true,
31493             headerIds: true,
31494             headerPrefix: '',
31495             highlight: null,
31496             langPrefix: 'language-',
31497             mangle: true,
31498             pedantic: false,
31499             renderer: null,
31500             sanitize: false,
31501             sanitizer: null,
31502             silent: false,
31503             smartLists: false,
31504             smartypants: false,
31505             tokenizer: null,
31506             xhtml: false
31507           };
31508         }
31509
31510         function changeDefaults(newDefaults) {
31511           module.exports.defaults = newDefaults;
31512         }
31513
31514         module.exports = {
31515           defaults: getDefaults(),
31516           getDefaults,
31517           changeDefaults
31518         };
31519         });
31520
31521         /**
31522          * Helpers
31523          */
31524         const escapeTest = /[&<>"']/;
31525         const escapeReplace = /[&<>"']/g;
31526         const escapeTestNoEncode = /[<>"']|&(?!#?\w+;)/;
31527         const escapeReplaceNoEncode = /[<>"']|&(?!#?\w+;)/g;
31528         const escapeReplacements = {
31529           '&': '&amp;',
31530           '<': '&lt;',
31531           '>': '&gt;',
31532           '"': '&quot;',
31533           "'": '&#39;'
31534         };
31535         const getEscapeReplacement = (ch) => escapeReplacements[ch];
31536         function escape$1(html, encode) {
31537           if (encode) {
31538             if (escapeTest.test(html)) {
31539               return html.replace(escapeReplace, getEscapeReplacement);
31540             }
31541           } else {
31542             if (escapeTestNoEncode.test(html)) {
31543               return html.replace(escapeReplaceNoEncode, getEscapeReplacement);
31544             }
31545           }
31546
31547           return html;
31548         }
31549
31550         const unescapeTest = /&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig;
31551
31552         function unescape$1(html) {
31553           // explicitly match decimal, hex, and named HTML entities
31554           return html.replace(unescapeTest, (_, n) => {
31555             n = n.toLowerCase();
31556             if (n === 'colon') return ':';
31557             if (n.charAt(0) === '#') {
31558               return n.charAt(1) === 'x'
31559                 ? String.fromCharCode(parseInt(n.substring(2), 16))
31560                 : String.fromCharCode(+n.substring(1));
31561             }
31562             return '';
31563           });
31564         }
31565
31566         const caret = /(^|[^\[])\^/g;
31567         function edit(regex, opt) {
31568           regex = regex.source || regex;
31569           opt = opt || '';
31570           const obj = {
31571             replace: (name, val) => {
31572               val = val.source || val;
31573               val = val.replace(caret, '$1');
31574               regex = regex.replace(name, val);
31575               return obj;
31576             },
31577             getRegex: () => {
31578               return new RegExp(regex, opt);
31579             }
31580           };
31581           return obj;
31582         }
31583
31584         const nonWordAndColonTest = /[^\w:]/g;
31585         const originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;
31586         function cleanUrl(sanitize, base, href) {
31587           if (sanitize) {
31588             let prot;
31589             try {
31590               prot = decodeURIComponent(unescape$1(href))
31591                 .replace(nonWordAndColonTest, '')
31592                 .toLowerCase();
31593             } catch (e) {
31594               return null;
31595             }
31596             if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) {
31597               return null;
31598             }
31599           }
31600           if (base && !originIndependentUrl.test(href)) {
31601             href = resolveUrl(base, href);
31602           }
31603           try {
31604             href = encodeURI(href).replace(/%25/g, '%');
31605           } catch (e) {
31606             return null;
31607           }
31608           return href;
31609         }
31610
31611         const baseUrls = {};
31612         const justDomain = /^[^:]+:\/*[^/]*$/;
31613         const protocol = /^([^:]+:)[\s\S]*$/;
31614         const domain = /^([^:]+:\/*[^/]*)[\s\S]*$/;
31615
31616         function resolveUrl(base, href) {
31617           if (!baseUrls[' ' + base]) {
31618             // we can ignore everything in base after the last slash of its path component,
31619             // but we might need to add _that_
31620             // https://tools.ietf.org/html/rfc3986#section-3
31621             if (justDomain.test(base)) {
31622               baseUrls[' ' + base] = base + '/';
31623             } else {
31624               baseUrls[' ' + base] = rtrim(base, '/', true);
31625             }
31626           }
31627           base = baseUrls[' ' + base];
31628           const relativeBase = base.indexOf(':') === -1;
31629
31630           if (href.substring(0, 2) === '//') {
31631             if (relativeBase) {
31632               return href;
31633             }
31634             return base.replace(protocol, '$1') + href;
31635           } else if (href.charAt(0) === '/') {
31636             if (relativeBase) {
31637               return href;
31638             }
31639             return base.replace(domain, '$1') + href;
31640           } else {
31641             return base + href;
31642           }
31643         }
31644
31645         const noopTest = { exec: function noopTest() {} };
31646
31647         function merge$1(obj) {
31648           let i = 1,
31649             target,
31650             key;
31651
31652           for (; i < arguments.length; i++) {
31653             target = arguments[i];
31654             for (key in target) {
31655               if (Object.prototype.hasOwnProperty.call(target, key)) {
31656                 obj[key] = target[key];
31657               }
31658             }
31659           }
31660
31661           return obj;
31662         }
31663
31664         function splitCells(tableRow, count) {
31665           // ensure that every cell-delimiting pipe has a space
31666           // before it to distinguish it from an escaped pipe
31667           const row = tableRow.replace(/\|/g, (match, offset, str) => {
31668               let escaped = false,
31669                 curr = offset;
31670               while (--curr >= 0 && str[curr] === '\\') escaped = !escaped;
31671               if (escaped) {
31672                 // odd number of slashes means | is escaped
31673                 // so we leave it alone
31674                 return '|';
31675               } else {
31676                 // add space before unescaped |
31677                 return ' |';
31678               }
31679             }),
31680             cells = row.split(/ \|/);
31681           let i = 0;
31682
31683           if (cells.length > count) {
31684             cells.splice(count);
31685           } else {
31686             while (cells.length < count) cells.push('');
31687           }
31688
31689           for (; i < cells.length; i++) {
31690             // leading or trailing whitespace is ignored per the gfm spec
31691             cells[i] = cells[i].trim().replace(/\\\|/g, '|');
31692           }
31693           return cells;
31694         }
31695
31696         // Remove trailing 'c's. Equivalent to str.replace(/c*$/, '').
31697         // /c*$/ is vulnerable to REDOS.
31698         // invert: Remove suffix of non-c chars instead. Default falsey.
31699         function rtrim(str, c, invert) {
31700           const l = str.length;
31701           if (l === 0) {
31702             return '';
31703           }
31704
31705           // Length of suffix matching the invert condition.
31706           let suffLen = 0;
31707
31708           // Step left until we fail to match the invert condition.
31709           while (suffLen < l) {
31710             const currChar = str.charAt(l - suffLen - 1);
31711             if (currChar === c && !invert) {
31712               suffLen++;
31713             } else if (currChar !== c && invert) {
31714               suffLen++;
31715             } else {
31716               break;
31717             }
31718           }
31719
31720           return str.substr(0, l - suffLen);
31721         }
31722
31723         function findClosingBracket(str, b) {
31724           if (str.indexOf(b[1]) === -1) {
31725             return -1;
31726           }
31727           const l = str.length;
31728           let level = 0,
31729             i = 0;
31730           for (; i < l; i++) {
31731             if (str[i] === '\\') {
31732               i++;
31733             } else if (str[i] === b[0]) {
31734               level++;
31735             } else if (str[i] === b[1]) {
31736               level--;
31737               if (level < 0) {
31738                 return i;
31739               }
31740             }
31741           }
31742           return -1;
31743         }
31744
31745         function checkSanitizeDeprecation(opt) {
31746           if (opt && opt.sanitize && !opt.silent) {
31747             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');
31748           }
31749         }
31750
31751         var helpers = {
31752           escape: escape$1,
31753           unescape: unescape$1,
31754           edit,
31755           cleanUrl,
31756           resolveUrl,
31757           noopTest,
31758           merge: merge$1,
31759           splitCells,
31760           rtrim,
31761           findClosingBracket,
31762           checkSanitizeDeprecation
31763         };
31764
31765         const { defaults: defaults$1 } = defaults;
31766         const {
31767           rtrim: rtrim$1,
31768           splitCells: splitCells$1,
31769           escape: escape$2,
31770           findClosingBracket: findClosingBracket$1
31771         } = helpers;
31772
31773         function outputLink(cap, link, raw) {
31774           const href = link.href;
31775           const title = link.title ? escape$2(link.title) : null;
31776
31777           if (cap[0].charAt(0) !== '!') {
31778             return {
31779               type: 'link',
31780               raw,
31781               href,
31782               title,
31783               text: cap[1]
31784             };
31785           } else {
31786             return {
31787               type: 'image',
31788               raw,
31789               text: escape$2(cap[1]),
31790               href,
31791               title
31792             };
31793           }
31794         }
31795
31796         /**
31797          * Tokenizer
31798          */
31799         var Tokenizer_1 = class Tokenizer {
31800           constructor(options) {
31801             this.options = options || defaults$1;
31802           }
31803
31804           space(src) {
31805             const cap = this.rules.block.newline.exec(src);
31806             if (cap) {
31807               if (cap[0].length > 1) {
31808                 return {
31809                   type: 'space',
31810                   raw: cap[0]
31811                 };
31812               }
31813               return { raw: '\n' };
31814             }
31815           }
31816
31817           code(src, tokens) {
31818             const cap = this.rules.block.code.exec(src);
31819             if (cap) {
31820               const lastToken = tokens[tokens.length - 1];
31821               // An indented code block cannot interrupt a paragraph.
31822               if (lastToken && lastToken.type === 'paragraph') {
31823                 tokens.pop();
31824                 lastToken.text += '\n' + cap[0].trimRight();
31825                 lastToken.raw += '\n' + cap[0];
31826                 return lastToken;
31827               } else {
31828                 const text = cap[0].replace(/^ {4}/gm, '');
31829                 return {
31830                   type: 'code',
31831                   raw: cap[0],
31832                   codeBlockStyle: 'indented',
31833                   text: !this.options.pedantic
31834                     ? rtrim$1(text, '\n')
31835                     : text
31836                 };
31837               }
31838             }
31839           }
31840
31841           fences(src) {
31842             const cap = this.rules.block.fences.exec(src);
31843             if (cap) {
31844               return {
31845                 type: 'code',
31846                 raw: cap[0],
31847                 lang: cap[2] ? cap[2].trim() : cap[2],
31848                 text: cap[3] || ''
31849               };
31850             }
31851           }
31852
31853           heading(src) {
31854             const cap = this.rules.block.heading.exec(src);
31855             if (cap) {
31856               return {
31857                 type: 'heading',
31858                 raw: cap[0],
31859                 depth: cap[1].length,
31860                 text: cap[2]
31861               };
31862             }
31863           }
31864
31865           nptable(src) {
31866             const cap = this.rules.block.nptable.exec(src);
31867             if (cap) {
31868               const item = {
31869                 type: 'table',
31870                 header: splitCells$1(cap[1].replace(/^ *| *\| *$/g, '')),
31871                 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
31872                 cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [],
31873                 raw: cap[0]
31874               };
31875
31876               if (item.header.length === item.align.length) {
31877                 let l = item.align.length;
31878                 let i;
31879                 for (i = 0; i < l; i++) {
31880                   if (/^ *-+: *$/.test(item.align[i])) {
31881                     item.align[i] = 'right';
31882                   } else if (/^ *:-+: *$/.test(item.align[i])) {
31883                     item.align[i] = 'center';
31884                   } else if (/^ *:-+ *$/.test(item.align[i])) {
31885                     item.align[i] = 'left';
31886                   } else {
31887                     item.align[i] = null;
31888                   }
31889                 }
31890
31891                 l = item.cells.length;
31892                 for (i = 0; i < l; i++) {
31893                   item.cells[i] = splitCells$1(item.cells[i], item.header.length);
31894                 }
31895
31896                 return item;
31897               }
31898             }
31899           }
31900
31901           hr(src) {
31902             const cap = this.rules.block.hr.exec(src);
31903             if (cap) {
31904               return {
31905                 type: 'hr',
31906                 raw: cap[0]
31907               };
31908             }
31909           }
31910
31911           blockquote(src) {
31912             const cap = this.rules.block.blockquote.exec(src);
31913             if (cap) {
31914               const text = cap[0].replace(/^ *> ?/gm, '');
31915
31916               return {
31917                 type: 'blockquote',
31918                 raw: cap[0],
31919                 text
31920               };
31921             }
31922           }
31923
31924           list(src) {
31925             const cap = this.rules.block.list.exec(src);
31926             if (cap) {
31927               let raw = cap[0];
31928               const bull = cap[2];
31929               const isordered = bull.length > 1;
31930
31931               const list = {
31932                 type: 'list',
31933                 raw,
31934                 ordered: isordered,
31935                 start: isordered ? +bull : '',
31936                 loose: false,
31937                 items: []
31938               };
31939
31940               // Get each top-level item.
31941               const itemMatch = cap[0].match(this.rules.block.item);
31942
31943               let next = false,
31944                 item,
31945                 space,
31946                 b,
31947                 addBack,
31948                 loose,
31949                 istask,
31950                 ischecked;
31951
31952               const l = itemMatch.length;
31953               for (let i = 0; i < l; i++) {
31954                 item = itemMatch[i];
31955                 raw = item;
31956
31957                 // Remove the list item's bullet
31958                 // so it is seen as the next token.
31959                 space = item.length;
31960                 item = item.replace(/^ *([*+-]|\d+\.) */, '');
31961
31962                 // Outdent whatever the
31963                 // list item contains. Hacky.
31964                 if (~item.indexOf('\n ')) {
31965                   space -= item.length;
31966                   item = !this.options.pedantic
31967                     ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
31968                     : item.replace(/^ {1,4}/gm, '');
31969                 }
31970
31971                 // Determine whether the next list item belongs here.
31972                 // Backpedal if it does not belong in this list.
31973                 if (i !== l - 1) {
31974                   b = this.rules.block.bullet.exec(itemMatch[i + 1])[0];
31975                   if (bull.length > 1 ? b.length === 1
31976                     : (b.length > 1 || (this.options.smartLists && b !== bull))) {
31977                     addBack = itemMatch.slice(i + 1).join('\n');
31978                     list.raw = list.raw.substring(0, list.raw.length - addBack.length);
31979                     i = l - 1;
31980                   }
31981                 }
31982
31983                 // Determine whether item is loose or not.
31984                 // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
31985                 // for discount behavior.
31986                 loose = next || /\n\n(?!\s*$)/.test(item);
31987                 if (i !== l - 1) {
31988                   next = item.charAt(item.length - 1) === '\n';
31989                   if (!loose) loose = next;
31990                 }
31991
31992                 if (loose) {
31993                   list.loose = true;
31994                 }
31995
31996                 // Check for task list items
31997                 istask = /^\[[ xX]\] /.test(item);
31998                 ischecked = undefined;
31999                 if (istask) {
32000                   ischecked = item[1] !== ' ';
32001                   item = item.replace(/^\[[ xX]\] +/, '');
32002                 }
32003
32004                 list.items.push({
32005                   raw,
32006                   task: istask,
32007                   checked: ischecked,
32008                   loose: loose,
32009                   text: item
32010                 });
32011               }
32012
32013               return list;
32014             }
32015           }
32016
32017           html(src) {
32018             const cap = this.rules.block.html.exec(src);
32019             if (cap) {
32020               return {
32021                 type: this.options.sanitize
32022                   ? 'paragraph'
32023                   : 'html',
32024                 raw: cap[0],
32025                 pre: !this.options.sanitizer
32026                   && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
32027                 text: this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape$2(cap[0])) : cap[0]
32028               };
32029             }
32030           }
32031
32032           def(src) {
32033             const cap = this.rules.block.def.exec(src);
32034             if (cap) {
32035               if (cap[3]) cap[3] = cap[3].substring(1, cap[3].length - 1);
32036               const tag = cap[1].toLowerCase().replace(/\s+/g, ' ');
32037               return {
32038                 tag,
32039                 raw: cap[0],
32040                 href: cap[2],
32041                 title: cap[3]
32042               };
32043             }
32044           }
32045
32046           table(src) {
32047             const cap = this.rules.block.table.exec(src);
32048             if (cap) {
32049               const item = {
32050                 type: 'table',
32051                 header: splitCells$1(cap[1].replace(/^ *| *\| *$/g, '')),
32052                 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
32053                 cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : []
32054               };
32055
32056               if (item.header.length === item.align.length) {
32057                 item.raw = cap[0];
32058
32059                 let l = item.align.length;
32060                 let i;
32061                 for (i = 0; i < l; i++) {
32062                   if (/^ *-+: *$/.test(item.align[i])) {
32063                     item.align[i] = 'right';
32064                   } else if (/^ *:-+: *$/.test(item.align[i])) {
32065                     item.align[i] = 'center';
32066                   } else if (/^ *:-+ *$/.test(item.align[i])) {
32067                     item.align[i] = 'left';
32068                   } else {
32069                     item.align[i] = null;
32070                   }
32071                 }
32072
32073                 l = item.cells.length;
32074                 for (i = 0; i < l; i++) {
32075                   item.cells[i] = splitCells$1(
32076                     item.cells[i].replace(/^ *\| *| *\| *$/g, ''),
32077                     item.header.length);
32078                 }
32079
32080                 return item;
32081               }
32082             }
32083           }
32084
32085           lheading(src) {
32086             const cap = this.rules.block.lheading.exec(src);
32087             if (cap) {
32088               return {
32089                 type: 'heading',
32090                 raw: cap[0],
32091                 depth: cap[2].charAt(0) === '=' ? 1 : 2,
32092                 text: cap[1]
32093               };
32094             }
32095           }
32096
32097           paragraph(src) {
32098             const cap = this.rules.block.paragraph.exec(src);
32099             if (cap) {
32100               return {
32101                 type: 'paragraph',
32102                 raw: cap[0],
32103                 text: cap[1].charAt(cap[1].length - 1) === '\n'
32104                   ? cap[1].slice(0, -1)
32105                   : cap[1]
32106               };
32107             }
32108           }
32109
32110           text(src) {
32111             const cap = this.rules.block.text.exec(src);
32112             if (cap) {
32113               return {
32114                 type: 'text',
32115                 raw: cap[0],
32116                 text: cap[0]
32117               };
32118             }
32119           }
32120
32121           escape(src) {
32122             const cap = this.rules.inline.escape.exec(src);
32123             if (cap) {
32124               return {
32125                 type: 'escape',
32126                 raw: cap[0],
32127                 text: escape$2(cap[1])
32128               };
32129             }
32130           }
32131
32132           tag(src, inLink, inRawBlock) {
32133             const cap = this.rules.inline.tag.exec(src);
32134             if (cap) {
32135               if (!inLink && /^<a /i.test(cap[0])) {
32136                 inLink = true;
32137               } else if (inLink && /^<\/a>/i.test(cap[0])) {
32138                 inLink = false;
32139               }
32140               if (!inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
32141                 inRawBlock = true;
32142               } else if (inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
32143                 inRawBlock = false;
32144               }
32145
32146               return {
32147                 type: this.options.sanitize
32148                   ? 'text'
32149                   : 'html',
32150                 raw: cap[0],
32151                 inLink,
32152                 inRawBlock,
32153                 text: this.options.sanitize
32154                   ? (this.options.sanitizer
32155                     ? this.options.sanitizer(cap[0])
32156                     : escape$2(cap[0]))
32157                   : cap[0]
32158               };
32159             }
32160           }
32161
32162           link(src) {
32163             const cap = this.rules.inline.link.exec(src);
32164             if (cap) {
32165               const lastParenIndex = findClosingBracket$1(cap[2], '()');
32166               if (lastParenIndex > -1) {
32167                 const start = cap[0].indexOf('!') === 0 ? 5 : 4;
32168                 const linkLen = start + cap[1].length + lastParenIndex;
32169                 cap[2] = cap[2].substring(0, lastParenIndex);
32170                 cap[0] = cap[0].substring(0, linkLen).trim();
32171                 cap[3] = '';
32172               }
32173               let href = cap[2];
32174               let title = '';
32175               if (this.options.pedantic) {
32176                 const link = /^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(href);
32177
32178                 if (link) {
32179                   href = link[1];
32180                   title = link[3];
32181                 } else {
32182                   title = '';
32183                 }
32184               } else {
32185                 title = cap[3] ? cap[3].slice(1, -1) : '';
32186               }
32187               href = href.trim().replace(/^<([\s\S]*)>$/, '$1');
32188               const token = outputLink(cap, {
32189                 href: href ? href.replace(this.rules.inline._escapes, '$1') : href,
32190                 title: title ? title.replace(this.rules.inline._escapes, '$1') : title
32191               }, cap[0]);
32192               return token;
32193             }
32194           }
32195
32196           reflink(src, links) {
32197             let cap;
32198             if ((cap = this.rules.inline.reflink.exec(src))
32199                 || (cap = this.rules.inline.nolink.exec(src))) {
32200               let link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
32201               link = links[link.toLowerCase()];
32202               if (!link || !link.href) {
32203                 const text = cap[0].charAt(0);
32204                 return {
32205                   type: 'text',
32206                   raw: text,
32207                   text
32208                 };
32209               }
32210               const token = outputLink(cap, link, cap[0]);
32211               return token;
32212             }
32213           }
32214
32215           strong(src) {
32216             const cap = this.rules.inline.strong.exec(src);
32217             if (cap) {
32218               return {
32219                 type: 'strong',
32220                 raw: cap[0],
32221                 text: cap[4] || cap[3] || cap[2] || cap[1]
32222               };
32223             }
32224           }
32225
32226           em(src) {
32227             const cap = this.rules.inline.em.exec(src);
32228             if (cap) {
32229               return {
32230                 type: 'em',
32231                 raw: cap[0],
32232                 text: cap[6] || cap[5] || cap[4] || cap[3] || cap[2] || cap[1]
32233               };
32234             }
32235           }
32236
32237           codespan(src) {
32238             const cap = this.rules.inline.code.exec(src);
32239             if (cap) {
32240               return {
32241                 type: 'codespan',
32242                 raw: cap[0],
32243                 text: escape$2(cap[2].trim(), true)
32244               };
32245             }
32246           }
32247
32248           br(src) {
32249             const cap = this.rules.inline.br.exec(src);
32250             if (cap) {
32251               return {
32252                 type: 'br',
32253                 raw: cap[0]
32254               };
32255             }
32256           }
32257
32258           del(src) {
32259             const cap = this.rules.inline.del.exec(src);
32260             if (cap) {
32261               return {
32262                 type: 'del',
32263                 raw: cap[0],
32264                 text: cap[1]
32265               };
32266             }
32267           }
32268
32269           autolink(src, mangle) {
32270             const cap = this.rules.inline.autolink.exec(src);
32271             if (cap) {
32272               let text, href;
32273               if (cap[2] === '@') {
32274                 text = escape$2(this.options.mangle ? mangle(cap[1]) : cap[1]);
32275                 href = 'mailto:' + text;
32276               } else {
32277                 text = escape$2(cap[1]);
32278                 href = text;
32279               }
32280
32281               return {
32282                 type: 'link',
32283                 raw: cap[0],
32284                 text,
32285                 href,
32286                 tokens: [
32287                   {
32288                     type: 'text',
32289                     raw: text,
32290                     text
32291                   }
32292                 ]
32293               };
32294             }
32295           }
32296
32297           url(src, mangle) {
32298             let cap;
32299             if (cap = this.rules.inline.url.exec(src)) {
32300               let text, href;
32301               if (cap[2] === '@') {
32302                 text = escape$2(this.options.mangle ? mangle(cap[0]) : cap[0]);
32303                 href = 'mailto:' + text;
32304               } else {
32305                 // do extended autolink path validation
32306                 let prevCapZero;
32307                 do {
32308                   prevCapZero = cap[0];
32309                   cap[0] = this.rules.inline._backpedal.exec(cap[0])[0];
32310                 } while (prevCapZero !== cap[0]);
32311                 text = escape$2(cap[0]);
32312                 if (cap[1] === 'www.') {
32313                   href = 'http://' + text;
32314                 } else {
32315                   href = text;
32316                 }
32317               }
32318               return {
32319                 type: 'link',
32320                 raw: cap[0],
32321                 text,
32322                 href,
32323                 tokens: [
32324                   {
32325                     type: 'text',
32326                     raw: text,
32327                     text
32328                   }
32329                 ]
32330               };
32331             }
32332           }
32333
32334           inlineText(src, inRawBlock, smartypants) {
32335             const cap = this.rules.inline.text.exec(src);
32336             if (cap) {
32337               let text;
32338               if (inRawBlock) {
32339                 text = this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape$2(cap[0])) : cap[0];
32340               } else {
32341                 text = escape$2(this.options.smartypants ? smartypants(cap[0]) : cap[0]);
32342               }
32343               return {
32344                 type: 'text',
32345                 raw: cap[0],
32346                 text
32347               };
32348             }
32349           }
32350         };
32351
32352         const {
32353           noopTest: noopTest$1,
32354           edit: edit$1,
32355           merge: merge$2
32356         } = helpers;
32357
32358         /**
32359          * Block-Level Grammar
32360          */
32361         const block = {
32362           newline: /^\n+/,
32363           code: /^( {4}[^\n]+\n*)+/,
32364           fences: /^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/,
32365           hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,
32366           heading: /^ {0,3}(#{1,6}) +([^\n]*?)(?: +#+)? *(?:\n+|$)/,
32367           blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,
32368           list: /^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
32369           html: '^ {0,3}(?:' // optional indentation
32370             + '<(script|pre|style)[\\s>][\\s\\S]*?(?:</\\1>[^\\n]*\\n+|$)' // (1)
32371             + '|comment[^\\n]*(\\n+|$)' // (2)
32372             + '|<\\?[\\s\\S]*?\\?>\\n*' // (3)
32373             + '|<![A-Z][\\s\\S]*?>\\n*' // (4)
32374             + '|<!\\[CDATA\\[[\\s\\S]*?\\]\\]>\\n*' // (5)
32375             + '|</?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:\\n{2,}|$)' // (6)
32376             + '|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)' // (7) open tag
32377             + '|</(?!script|pre|style)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)' // (7) closing tag
32378             + ')',
32379           def: /^ {0,3}\[(label)\]: *\n? *<?([^\s>]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/,
32380           nptable: noopTest$1,
32381           table: noopTest$1,
32382           lheading: /^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/,
32383           // regex template, placeholders will be replaced according to different paragraph
32384           // interruption rules of commonmark and the original markdown spec:
32385           _paragraph: /^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html)[^\n]+)*)/,
32386           text: /^[^\n]+/
32387         };
32388
32389         block._label = /(?!\s*\])(?:\\[\[\]]|[^\[\]])+/;
32390         block._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/;
32391         block.def = edit$1(block.def)
32392           .replace('label', block._label)
32393           .replace('title', block._title)
32394           .getRegex();
32395
32396         block.bullet = /(?:[*+-]|\d{1,9}\.)/;
32397         block.item = /^( *)(bull) ?[^\n]*(?:\n(?!\1bull ?)[^\n]*)*/;
32398         block.item = edit$1(block.item, 'gm')
32399           .replace(/bull/g, block.bullet)
32400           .getRegex();
32401
32402         block.list = edit$1(block.list)
32403           .replace(/bull/g, block.bullet)
32404           .replace('hr', '\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))')
32405           .replace('def', '\\n+(?=' + block.def.source + ')')
32406           .getRegex();
32407
32408         block._tag = 'address|article|aside|base|basefont|blockquote|body|caption'
32409           + '|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption'
32410           + '|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe'
32411           + '|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option'
32412           + '|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr'
32413           + '|track|ul';
32414         block._comment = /<!--(?!-?>)[\s\S]*?-->/;
32415         block.html = edit$1(block.html, 'i')
32416           .replace('comment', block._comment)
32417           .replace('tag', block._tag)
32418           .replace('attribute', / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/)
32419           .getRegex();
32420
32421         block.paragraph = edit$1(block._paragraph)
32422           .replace('hr', block.hr)
32423           .replace('heading', ' {0,3}#{1,6} ')
32424           .replace('|lheading', '') // setex headings don't interrupt commonmark paragraphs
32425           .replace('blockquote', ' {0,3}>')
32426           .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n')
32427           .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
32428           .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)')
32429           .replace('tag', block._tag) // pars can be interrupted by type (6) html blocks
32430           .getRegex();
32431
32432         block.blockquote = edit$1(block.blockquote)
32433           .replace('paragraph', block.paragraph)
32434           .getRegex();
32435
32436         /**
32437          * Normal Block Grammar
32438          */
32439
32440         block.normal = merge$2({}, block);
32441
32442         /**
32443          * GFM Block Grammar
32444          */
32445
32446         block.gfm = merge$2({}, block.normal, {
32447           nptable: '^ *([^|\\n ].*\\|.*)\\n' // Header
32448             + ' *([-:]+ *\\|[-| :]*)' // Align
32449             + '(?:\\n((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)', // Cells
32450           table: '^ *\\|(.+)\\n' // Header
32451             + ' *\\|?( *[-:]+[-| :]*)' // Align
32452             + '(?:\\n *((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)' // Cells
32453         });
32454
32455         block.gfm.nptable = edit$1(block.gfm.nptable)
32456           .replace('hr', block.hr)
32457           .replace('heading', ' {0,3}#{1,6} ')
32458           .replace('blockquote', ' {0,3}>')
32459           .replace('code', ' {4}[^\\n]')
32460           .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n')
32461           .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
32462           .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)')
32463           .replace('tag', block._tag) // tables can be interrupted by type (6) html blocks
32464           .getRegex();
32465
32466         block.gfm.table = edit$1(block.gfm.table)
32467           .replace('hr', block.hr)
32468           .replace('heading', ' {0,3}#{1,6} ')
32469           .replace('blockquote', ' {0,3}>')
32470           .replace('code', ' {4}[^\\n]')
32471           .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n')
32472           .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
32473           .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)')
32474           .replace('tag', block._tag) // tables can be interrupted by type (6) html blocks
32475           .getRegex();
32476
32477         /**
32478          * Pedantic grammar (original John Gruber's loose markdown specification)
32479          */
32480
32481         block.pedantic = merge$2({}, block.normal, {
32482           html: edit$1(
32483             '^ *(?:comment *(?:\\n|\\s*$)'
32484             + '|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)' // closed tag
32485             + '|<tag(?:"[^"]*"|\'[^\']*\'|\\s[^\'"/>\\s]*)*?/?> *(?:\\n{2,}|\\s*$))')
32486             .replace('comment', block._comment)
32487             .replace(/tag/g, '(?!(?:'
32488               + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub'
32489               + '|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)'
32490               + '\\b)\\w+(?!:|[^\\w\\s@]*@)\\b')
32491             .getRegex(),
32492           def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,
32493           heading: /^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/,
32494           fences: noopTest$1, // fences not supported
32495           paragraph: edit$1(block.normal._paragraph)
32496             .replace('hr', block.hr)
32497             .replace('heading', ' *#{1,6} *[^\n]')
32498             .replace('lheading', block.lheading)
32499             .replace('blockquote', ' {0,3}>')
32500             .replace('|fences', '')
32501             .replace('|list', '')
32502             .replace('|html', '')
32503             .getRegex()
32504         });
32505
32506         /**
32507          * Inline-Level Grammar
32508          */
32509         const inline = {
32510           escape: /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,
32511           autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/,
32512           url: noopTest$1,
32513           tag: '^comment'
32514             + '|^</[a-zA-Z][\\w:-]*\\s*>' // self-closing tag
32515             + '|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>' // open tag
32516             + '|^<\\?[\\s\\S]*?\\?>' // processing instruction, e.g. <?php ?>
32517             + '|^<![a-zA-Z]+\\s[\\s\\S]*?>' // declaration, e.g. <!DOCTYPE html>
32518             + '|^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>', // CDATA section
32519           link: /^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/,
32520           reflink: /^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,
32521           nolink: /^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,
32522           strong: /^__([^\s_])__(?!_)|^\*\*([^\s*])\*\*(?!\*)|^__([^\s][\s\S]*?[^\s])__(?!_)|^\*\*([^\s][\s\S]*?[^\s])\*\*(?!\*)/,
32523           em: /^_([^\s_])_(?!_)|^_([^\s_<][\s\S]*?[^\s_])_(?!_|[^\spunctuation])|^_([^\s_<][\s\S]*?[^\s])_(?!_|[^\spunctuation])|^\*([^\s*<\[])\*(?!\*)|^\*([^\s<"][\s\S]*?[^\s\[\*])\*(?![\]`punctuation])|^\*([^\s*"<\[][\s\S]*[^\s])\*(?!\*)/,
32524           code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,
32525           br: /^( {2,}|\\)\n(?!\s*$)/,
32526           del: noopTest$1,
32527           text: /^(`+|[^`])(?:[\s\S]*?(?:(?=[\\<!\[`*]|\b_|$)|[^ ](?= {2,}\n))|(?= {2,}\n))/
32528         };
32529
32530         // list of punctuation marks from common mark spec
32531         // without ` and ] to workaround Rule 17 (inline code blocks/links)
32532         inline._punctuation = '!"#$%&\'()*+\\-./:;<=>?@\\[^_{|}~';
32533         inline.em = edit$1(inline.em).replace(/punctuation/g, inline._punctuation).getRegex();
32534
32535         inline._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g;
32536
32537         inline._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/;
32538         inline._email = /[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/;
32539         inline.autolink = edit$1(inline.autolink)
32540           .replace('scheme', inline._scheme)
32541           .replace('email', inline._email)
32542           .getRegex();
32543
32544         inline._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/;
32545
32546         inline.tag = edit$1(inline.tag)
32547           .replace('comment', block._comment)
32548           .replace('attribute', inline._attribute)
32549           .getRegex();
32550
32551         inline._label = /(?:\[[^\[\]]*\]|\\.|`[^`]*`|[^\[\]\\`])*?/;
32552         inline._href = /<(?:\\[<>]?|[^\s<>\\])*>|[^\s\x00-\x1f]*/;
32553         inline._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/;
32554
32555         inline.link = edit$1(inline.link)
32556           .replace('label', inline._label)
32557           .replace('href', inline._href)
32558           .replace('title', inline._title)
32559           .getRegex();
32560
32561         inline.reflink = edit$1(inline.reflink)
32562           .replace('label', inline._label)
32563           .getRegex();
32564
32565         /**
32566          * Normal Inline Grammar
32567          */
32568
32569         inline.normal = merge$2({}, inline);
32570
32571         /**
32572          * Pedantic Inline Grammar
32573          */
32574
32575         inline.pedantic = merge$2({}, inline.normal, {
32576           strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
32577           em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/,
32578           link: edit$1(/^!?\[(label)\]\((.*?)\)/)
32579             .replace('label', inline._label)
32580             .getRegex(),
32581           reflink: edit$1(/^!?\[(label)\]\s*\[([^\]]*)\]/)
32582             .replace('label', inline._label)
32583             .getRegex()
32584         });
32585
32586         /**
32587          * GFM Inline Grammar
32588          */
32589
32590         inline.gfm = merge$2({}, inline.normal, {
32591           escape: edit$1(inline.escape).replace('])', '~|])').getRegex(),
32592           _extended_email: /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,
32593           url: /^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,
32594           _backpedal: /(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,
32595           del: /^~+(?=\S)([\s\S]*?\S)~+/,
32596           text: /^(`+|[^`])(?:[\s\S]*?(?:(?=[\\<!\[`*~]|\b_|https?:\/\/|ftp:\/\/|www\.|$)|[^ ](?= {2,}\n)|[^a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-](?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@))|(?= {2,}\n|[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@))/
32597         });
32598
32599         inline.gfm.url = edit$1(inline.gfm.url, 'i')
32600           .replace('email', inline.gfm._extended_email)
32601           .getRegex();
32602         /**
32603          * GFM + Line Breaks Inline Grammar
32604          */
32605
32606         inline.breaks = merge$2({}, inline.gfm, {
32607           br: edit$1(inline.br).replace('{2,}', '*').getRegex(),
32608           text: edit$1(inline.gfm.text)
32609             .replace('\\b_', '\\b_| {2,}\\n')
32610             .replace(/\{2,\}/g, '*')
32611             .getRegex()
32612         });
32613
32614         var rules = {
32615           block,
32616           inline
32617         };
32618
32619         const { defaults: defaults$2 } = defaults;
32620         const { block: block$1, inline: inline$1 } = rules;
32621
32622         /**
32623          * smartypants text replacement
32624          */
32625         function smartypants(text) {
32626           return text
32627             // em-dashes
32628             .replace(/---/g, '\u2014')
32629             // en-dashes
32630             .replace(/--/g, '\u2013')
32631             // opening singles
32632             .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
32633             // closing singles & apostrophes
32634             .replace(/'/g, '\u2019')
32635             // opening doubles
32636             .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
32637             // closing doubles
32638             .replace(/"/g, '\u201d')
32639             // ellipses
32640             .replace(/\.{3}/g, '\u2026');
32641         }
32642
32643         /**
32644          * mangle email addresses
32645          */
32646         function mangle(text) {
32647           let out = '',
32648             i,
32649             ch;
32650
32651           const l = text.length;
32652           for (i = 0; i < l; i++) {
32653             ch = text.charCodeAt(i);
32654             if (Math.random() > 0.5) {
32655               ch = 'x' + ch.toString(16);
32656             }
32657             out += '&#' + ch + ';';
32658           }
32659
32660           return out;
32661         }
32662
32663         /**
32664          * Block Lexer
32665          */
32666         var Lexer_1 = class Lexer {
32667           constructor(options) {
32668             this.tokens = [];
32669             this.tokens.links = Object.create(null);
32670             this.options = options || defaults$2;
32671             this.options.tokenizer = this.options.tokenizer || new Tokenizer_1();
32672             this.tokenizer = this.options.tokenizer;
32673             this.tokenizer.options = this.options;
32674
32675             const rules = {
32676               block: block$1.normal,
32677               inline: inline$1.normal
32678             };
32679
32680             if (this.options.pedantic) {
32681               rules.block = block$1.pedantic;
32682               rules.inline = inline$1.pedantic;
32683             } else if (this.options.gfm) {
32684               rules.block = block$1.gfm;
32685               if (this.options.breaks) {
32686                 rules.inline = inline$1.breaks;
32687               } else {
32688                 rules.inline = inline$1.gfm;
32689               }
32690             }
32691             this.tokenizer.rules = rules;
32692           }
32693
32694           /**
32695            * Expose Rules
32696            */
32697           static get rules() {
32698             return {
32699               block: block$1,
32700               inline: inline$1
32701             };
32702           }
32703
32704           /**
32705            * Static Lex Method
32706            */
32707           static lex(src, options) {
32708             const lexer = new Lexer(options);
32709             return lexer.lex(src);
32710           }
32711
32712           /**
32713            * Preprocessing
32714            */
32715           lex(src) {
32716             src = src
32717               .replace(/\r\n|\r/g, '\n')
32718               .replace(/\t/g, '    ');
32719
32720             this.blockTokens(src, this.tokens, true);
32721
32722             this.inline(this.tokens);
32723
32724             return this.tokens;
32725           }
32726
32727           /**
32728            * Lexing
32729            */
32730           blockTokens(src, tokens = [], top = true) {
32731             src = src.replace(/^ +$/gm, '');
32732             let token, i, l;
32733
32734             while (src) {
32735               // newline
32736               if (token = this.tokenizer.space(src)) {
32737                 src = src.substring(token.raw.length);
32738                 if (token.type) {
32739                   tokens.push(token);
32740                 }
32741                 continue;
32742               }
32743
32744               // code
32745               if (token = this.tokenizer.code(src, tokens)) {
32746                 src = src.substring(token.raw.length);
32747                 tokens.push(token);
32748                 continue;
32749               }
32750
32751               // fences
32752               if (token = this.tokenizer.fences(src)) {
32753                 src = src.substring(token.raw.length);
32754                 tokens.push(token);
32755                 continue;
32756               }
32757
32758               // heading
32759               if (token = this.tokenizer.heading(src)) {
32760                 src = src.substring(token.raw.length);
32761                 tokens.push(token);
32762                 continue;
32763               }
32764
32765               // table no leading pipe (gfm)
32766               if (token = this.tokenizer.nptable(src)) {
32767                 src = src.substring(token.raw.length);
32768                 tokens.push(token);
32769                 continue;
32770               }
32771
32772               // hr
32773               if (token = this.tokenizer.hr(src)) {
32774                 src = src.substring(token.raw.length);
32775                 tokens.push(token);
32776                 continue;
32777               }
32778
32779               // blockquote
32780               if (token = this.tokenizer.blockquote(src)) {
32781                 src = src.substring(token.raw.length);
32782                 token.tokens = this.blockTokens(token.text, [], top);
32783                 tokens.push(token);
32784                 continue;
32785               }
32786
32787               // list
32788               if (token = this.tokenizer.list(src)) {
32789                 src = src.substring(token.raw.length);
32790                 l = token.items.length;
32791                 for (i = 0; i < l; i++) {
32792                   token.items[i].tokens = this.blockTokens(token.items[i].text, [], false);
32793                 }
32794                 tokens.push(token);
32795                 continue;
32796               }
32797
32798               // html
32799               if (token = this.tokenizer.html(src)) {
32800                 src = src.substring(token.raw.length);
32801                 tokens.push(token);
32802                 continue;
32803               }
32804
32805               // def
32806               if (top && (token = this.tokenizer.def(src))) {
32807                 src = src.substring(token.raw.length);
32808                 if (!this.tokens.links[token.tag]) {
32809                   this.tokens.links[token.tag] = {
32810                     href: token.href,
32811                     title: token.title
32812                   };
32813                 }
32814                 continue;
32815               }
32816
32817               // table (gfm)
32818               if (token = this.tokenizer.table(src)) {
32819                 src = src.substring(token.raw.length);
32820                 tokens.push(token);
32821                 continue;
32822               }
32823
32824               // lheading
32825               if (token = this.tokenizer.lheading(src)) {
32826                 src = src.substring(token.raw.length);
32827                 tokens.push(token);
32828                 continue;
32829               }
32830
32831               // top-level paragraph
32832               if (top && (token = this.tokenizer.paragraph(src))) {
32833                 src = src.substring(token.raw.length);
32834                 tokens.push(token);
32835                 continue;
32836               }
32837
32838               // text
32839               if (token = this.tokenizer.text(src)) {
32840                 src = src.substring(token.raw.length);
32841                 tokens.push(token);
32842                 continue;
32843               }
32844
32845               if (src) {
32846                 const errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0);
32847                 if (this.options.silent) {
32848                   console.error(errMsg);
32849                   break;
32850                 } else {
32851                   throw new Error(errMsg);
32852                 }
32853               }
32854             }
32855
32856             return tokens;
32857           }
32858
32859           inline(tokens) {
32860             let i,
32861               j,
32862               k,
32863               l2,
32864               row,
32865               token;
32866
32867             const l = tokens.length;
32868             for (i = 0; i < l; i++) {
32869               token = tokens[i];
32870               switch (token.type) {
32871                 case 'paragraph':
32872                 case 'text':
32873                 case 'heading': {
32874                   token.tokens = [];
32875                   this.inlineTokens(token.text, token.tokens);
32876                   break;
32877                 }
32878                 case 'table': {
32879                   token.tokens = {
32880                     header: [],
32881                     cells: []
32882                   };
32883
32884                   // header
32885                   l2 = token.header.length;
32886                   for (j = 0; j < l2; j++) {
32887                     token.tokens.header[j] = [];
32888                     this.inlineTokens(token.header[j], token.tokens.header[j]);
32889                   }
32890
32891                   // cells
32892                   l2 = token.cells.length;
32893                   for (j = 0; j < l2; j++) {
32894                     row = token.cells[j];
32895                     token.tokens.cells[j] = [];
32896                     for (k = 0; k < row.length; k++) {
32897                       token.tokens.cells[j][k] = [];
32898                       this.inlineTokens(row[k], token.tokens.cells[j][k]);
32899                     }
32900                   }
32901
32902                   break;
32903                 }
32904                 case 'blockquote': {
32905                   this.inline(token.tokens);
32906                   break;
32907                 }
32908                 case 'list': {
32909                   l2 = token.items.length;
32910                   for (j = 0; j < l2; j++) {
32911                     this.inline(token.items[j].tokens);
32912                   }
32913                   break;
32914                 }
32915               }
32916             }
32917
32918             return tokens;
32919           }
32920
32921           /**
32922            * Lexing/Compiling
32923            */
32924           inlineTokens(src, tokens = [], inLink = false, inRawBlock = false) {
32925             let token;
32926
32927             while (src) {
32928               // escape
32929               if (token = this.tokenizer.escape(src)) {
32930                 src = src.substring(token.raw.length);
32931                 tokens.push(token);
32932                 continue;
32933               }
32934
32935               // tag
32936               if (token = this.tokenizer.tag(src, inLink, inRawBlock)) {
32937                 src = src.substring(token.raw.length);
32938                 inLink = token.inLink;
32939                 inRawBlock = token.inRawBlock;
32940                 tokens.push(token);
32941                 continue;
32942               }
32943
32944               // link
32945               if (token = this.tokenizer.link(src)) {
32946                 src = src.substring(token.raw.length);
32947                 if (token.type === 'link') {
32948                   token.tokens = this.inlineTokens(token.text, [], true, inRawBlock);
32949                 }
32950                 tokens.push(token);
32951                 continue;
32952               }
32953
32954               // reflink, nolink
32955               if (token = this.tokenizer.reflink(src, this.tokens.links)) {
32956                 src = src.substring(token.raw.length);
32957                 if (token.type === 'link') {
32958                   token.tokens = this.inlineTokens(token.text, [], true, inRawBlock);
32959                 }
32960                 tokens.push(token);
32961                 continue;
32962               }
32963
32964               // strong
32965               if (token = this.tokenizer.strong(src)) {
32966                 src = src.substring(token.raw.length);
32967                 token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock);
32968                 tokens.push(token);
32969                 continue;
32970               }
32971
32972               // em
32973               if (token = this.tokenizer.em(src)) {
32974                 src = src.substring(token.raw.length);
32975                 token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock);
32976                 tokens.push(token);
32977                 continue;
32978               }
32979
32980               // code
32981               if (token = this.tokenizer.codespan(src)) {
32982                 src = src.substring(token.raw.length);
32983                 tokens.push(token);
32984                 continue;
32985               }
32986
32987               // br
32988               if (token = this.tokenizer.br(src)) {
32989                 src = src.substring(token.raw.length);
32990                 tokens.push(token);
32991                 continue;
32992               }
32993
32994               // del (gfm)
32995               if (token = this.tokenizer.del(src)) {
32996                 src = src.substring(token.raw.length);
32997                 token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock);
32998                 tokens.push(token);
32999                 continue;
33000               }
33001
33002               // autolink
33003               if (token = this.tokenizer.autolink(src, mangle)) {
33004                 src = src.substring(token.raw.length);
33005                 tokens.push(token);
33006                 continue;
33007               }
33008
33009               // url (gfm)
33010               if (!inLink && (token = this.tokenizer.url(src, mangle))) {
33011                 src = src.substring(token.raw.length);
33012                 tokens.push(token);
33013                 continue;
33014               }
33015
33016               // text
33017               if (token = this.tokenizer.inlineText(src, inRawBlock, smartypants)) {
33018                 src = src.substring(token.raw.length);
33019                 tokens.push(token);
33020                 continue;
33021               }
33022
33023               if (src) {
33024                 const errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0);
33025                 if (this.options.silent) {
33026                   console.error(errMsg);
33027                   break;
33028                 } else {
33029                   throw new Error(errMsg);
33030                 }
33031               }
33032             }
33033
33034             return tokens;
33035           }
33036         };
33037
33038         const { defaults: defaults$3 } = defaults;
33039         const {
33040           cleanUrl: cleanUrl$1,
33041           escape: escape$3
33042         } = helpers;
33043
33044         /**
33045          * Renderer
33046          */
33047         var Renderer_1 = class Renderer {
33048           constructor(options) {
33049             this.options = options || defaults$3;
33050           }
33051
33052           code(code, infostring, escaped) {
33053             const lang = (infostring || '').match(/\S*/)[0];
33054             if (this.options.highlight) {
33055               const out = this.options.highlight(code, lang);
33056               if (out != null && out !== code) {
33057                 escaped = true;
33058                 code = out;
33059               }
33060             }
33061
33062             if (!lang) {
33063               return '<pre><code>'
33064                 + (escaped ? code : escape$3(code, true))
33065                 + '</code></pre>';
33066             }
33067
33068             return '<pre><code class="'
33069               + this.options.langPrefix
33070               + escape$3(lang, true)
33071               + '">'
33072               + (escaped ? code : escape$3(code, true))
33073               + '</code></pre>\n';
33074           }
33075
33076           blockquote(quote) {
33077             return '<blockquote>\n' + quote + '</blockquote>\n';
33078           }
33079
33080           html(html) {
33081             return html;
33082           }
33083
33084           heading(text, level, raw, slugger) {
33085             if (this.options.headerIds) {
33086               return '<h'
33087                 + level
33088                 + ' id="'
33089                 + this.options.headerPrefix
33090                 + slugger.slug(raw)
33091                 + '">'
33092                 + text
33093                 + '</h'
33094                 + level
33095                 + '>\n';
33096             }
33097             // ignore IDs
33098             return '<h' + level + '>' + text + '</h' + level + '>\n';
33099           }
33100
33101           hr() {
33102             return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
33103           }
33104
33105           list(body, ordered, start) {
33106             const type = ordered ? 'ol' : 'ul',
33107               startatt = (ordered && start !== 1) ? (' start="' + start + '"') : '';
33108             return '<' + type + startatt + '>\n' + body + '</' + type + '>\n';
33109           }
33110
33111           listitem(text) {
33112             return '<li>' + text + '</li>\n';
33113           }
33114
33115           checkbox(checked) {
33116             return '<input '
33117               + (checked ? 'checked="" ' : '')
33118               + 'disabled="" type="checkbox"'
33119               + (this.options.xhtml ? ' /' : '')
33120               + '> ';
33121           }
33122
33123           paragraph(text) {
33124             return '<p>' + text + '</p>\n';
33125           }
33126
33127           table(header, body) {
33128             if (body) body = '<tbody>' + body + '</tbody>';
33129
33130             return '<table>\n'
33131               + '<thead>\n'
33132               + header
33133               + '</thead>\n'
33134               + body
33135               + '</table>\n';
33136           }
33137
33138           tablerow(content) {
33139             return '<tr>\n' + content + '</tr>\n';
33140           }
33141
33142           tablecell(content, flags) {
33143             const type = flags.header ? 'th' : 'td';
33144             const tag = flags.align
33145               ? '<' + type + ' align="' + flags.align + '">'
33146               : '<' + type + '>';
33147             return tag + content + '</' + type + '>\n';
33148           }
33149
33150           // span level renderer
33151           strong(text) {
33152             return '<strong>' + text + '</strong>';
33153           }
33154
33155           em(text) {
33156             return '<em>' + text + '</em>';
33157           }
33158
33159           codespan(text) {
33160             return '<code>' + text + '</code>';
33161           }
33162
33163           br() {
33164             return this.options.xhtml ? '<br/>' : '<br>';
33165           }
33166
33167           del(text) {
33168             return '<del>' + text + '</del>';
33169           }
33170
33171           link(href, title, text) {
33172             href = cleanUrl$1(this.options.sanitize, this.options.baseUrl, href);
33173             if (href === null) {
33174               return text;
33175             }
33176             let out = '<a href="' + escape$3(href) + '"';
33177             if (title) {
33178               out += ' title="' + title + '"';
33179             }
33180             out += '>' + text + '</a>';
33181             return out;
33182           }
33183
33184           image(href, title, text) {
33185             href = cleanUrl$1(this.options.sanitize, this.options.baseUrl, href);
33186             if (href === null) {
33187               return text;
33188             }
33189
33190             let out = '<img src="' + href + '" alt="' + text + '"';
33191             if (title) {
33192               out += ' title="' + title + '"';
33193             }
33194             out += this.options.xhtml ? '/>' : '>';
33195             return out;
33196           }
33197
33198           text(text) {
33199             return text;
33200           }
33201         };
33202
33203         /**
33204          * TextRenderer
33205          * returns only the textual part of the token
33206          */
33207         var TextRenderer_1 = class TextRenderer {
33208           // no need for block level renderers
33209           strong(text) {
33210             return text;
33211           }
33212
33213           em(text) {
33214             return text;
33215           }
33216
33217           codespan(text) {
33218             return text;
33219           }
33220
33221           del(text) {
33222             return text;
33223           }
33224
33225           html(text) {
33226             return text;
33227           }
33228
33229           text(text) {
33230             return text;
33231           }
33232
33233           link(href, title, text) {
33234             return '' + text;
33235           }
33236
33237           image(href, title, text) {
33238             return '' + text;
33239           }
33240
33241           br() {
33242             return '';
33243           }
33244         };
33245
33246         /**
33247          * Slugger generates header id
33248          */
33249         var Slugger_1 = class Slugger {
33250           constructor() {
33251             this.seen = {};
33252           }
33253
33254           /**
33255            * Convert string to unique id
33256            */
33257           slug(value) {
33258             let slug = value
33259               .toLowerCase()
33260               .trim()
33261               // remove html tags
33262               .replace(/<[!\/a-z].*?>/ig, '')
33263               // remove unwanted chars
33264               .replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g, '')
33265               .replace(/\s/g, '-');
33266
33267             if (this.seen.hasOwnProperty(slug)) {
33268               const originalSlug = slug;
33269               do {
33270                 this.seen[originalSlug]++;
33271                 slug = originalSlug + '-' + this.seen[originalSlug];
33272               } while (this.seen.hasOwnProperty(slug));
33273             }
33274             this.seen[slug] = 0;
33275
33276             return slug;
33277           }
33278         };
33279
33280         const { defaults: defaults$4 } = defaults;
33281         const {
33282           unescape: unescape$2
33283         } = helpers;
33284
33285         /**
33286          * Parsing & Compiling
33287          */
33288         var Parser_1 = class Parser {
33289           constructor(options) {
33290             this.options = options || defaults$4;
33291             this.options.renderer = this.options.renderer || new Renderer_1();
33292             this.renderer = this.options.renderer;
33293             this.renderer.options = this.options;
33294             this.textRenderer = new TextRenderer_1();
33295             this.slugger = new Slugger_1();
33296           }
33297
33298           /**
33299            * Static Parse Method
33300            */
33301           static parse(tokens, options) {
33302             const parser = new Parser(options);
33303             return parser.parse(tokens);
33304           }
33305
33306           /**
33307            * Parse Loop
33308            */
33309           parse(tokens, top = true) {
33310             let out = '',
33311               i,
33312               j,
33313               k,
33314               l2,
33315               l3,
33316               row,
33317               cell,
33318               header,
33319               body,
33320               token,
33321               ordered,
33322               start,
33323               loose,
33324               itemBody,
33325               item,
33326               checked,
33327               task,
33328               checkbox;
33329
33330             const l = tokens.length;
33331             for (i = 0; i < l; i++) {
33332               token = tokens[i];
33333               switch (token.type) {
33334                 case 'space': {
33335                   continue;
33336                 }
33337                 case 'hr': {
33338                   out += this.renderer.hr();
33339                   continue;
33340                 }
33341                 case 'heading': {
33342                   out += this.renderer.heading(
33343                     this.parseInline(token.tokens),
33344                     token.depth,
33345                     unescape$2(this.parseInline(token.tokens, this.textRenderer)),
33346                     this.slugger);
33347                   continue;
33348                 }
33349                 case 'code': {
33350                   out += this.renderer.code(token.text,
33351                     token.lang,
33352                     token.escaped);
33353                   continue;
33354                 }
33355                 case 'table': {
33356                   header = '';
33357
33358                   // header
33359                   cell = '';
33360                   l2 = token.header.length;
33361                   for (j = 0; j < l2; j++) {
33362                     cell += this.renderer.tablecell(
33363                       this.parseInline(token.tokens.header[j]),
33364                       { header: true, align: token.align[j] }
33365                     );
33366                   }
33367                   header += this.renderer.tablerow(cell);
33368
33369                   body = '';
33370                   l2 = token.cells.length;
33371                   for (j = 0; j < l2; j++) {
33372                     row = token.tokens.cells[j];
33373
33374                     cell = '';
33375                     l3 = row.length;
33376                     for (k = 0; k < l3; k++) {
33377                       cell += this.renderer.tablecell(
33378                         this.parseInline(row[k]),
33379                         { header: false, align: token.align[k] }
33380                       );
33381                     }
33382
33383                     body += this.renderer.tablerow(cell);
33384                   }
33385                   out += this.renderer.table(header, body);
33386                   continue;
33387                 }
33388                 case 'blockquote': {
33389                   body = this.parse(token.tokens);
33390                   out += this.renderer.blockquote(body);
33391                   continue;
33392                 }
33393                 case 'list': {
33394                   ordered = token.ordered;
33395                   start = token.start;
33396                   loose = token.loose;
33397                   l2 = token.items.length;
33398
33399                   body = '';
33400                   for (j = 0; j < l2; j++) {
33401                     item = token.items[j];
33402                     checked = item.checked;
33403                     task = item.task;
33404
33405                     itemBody = '';
33406                     if (item.task) {
33407                       checkbox = this.renderer.checkbox(checked);
33408                       if (loose) {
33409                         if (item.tokens[0].type === 'text') {
33410                           item.tokens[0].text = checkbox + ' ' + item.tokens[0].text;
33411                           if (item.tokens[0].tokens && item.tokens[0].tokens.length > 0 && item.tokens[0].tokens[0].type === 'text') {
33412                             item.tokens[0].tokens[0].text = checkbox + ' ' + item.tokens[0].tokens[0].text;
33413                           }
33414                         } else {
33415                           item.tokens.unshift({
33416                             type: 'text',
33417                             text: checkbox
33418                           });
33419                         }
33420                       } else {
33421                         itemBody += checkbox;
33422                       }
33423                     }
33424
33425                     itemBody += this.parse(item.tokens, loose);
33426                     body += this.renderer.listitem(itemBody, task, checked);
33427                   }
33428
33429                   out += this.renderer.list(body, ordered, start);
33430                   continue;
33431                 }
33432                 case 'html': {
33433                   // TODO parse inline content if parameter markdown=1
33434                   out += this.renderer.html(token.text);
33435                   continue;
33436                 }
33437                 case 'paragraph': {
33438                   out += this.renderer.paragraph(this.parseInline(token.tokens));
33439                   continue;
33440                 }
33441                 case 'text': {
33442                   body = token.tokens ? this.parseInline(token.tokens) : token.text;
33443                   while (i + 1 < l && tokens[i + 1].type === 'text') {
33444                     token = tokens[++i];
33445                     body += '\n' + (token.tokens ? this.parseInline(token.tokens) : token.text);
33446                   }
33447                   out += top ? this.renderer.paragraph(body) : body;
33448                   continue;
33449                 }
33450                 default: {
33451                   const errMsg = 'Token with "' + token.type + '" type was not found.';
33452                   if (this.options.silent) {
33453                     console.error(errMsg);
33454                     return;
33455                   } else {
33456                     throw new Error(errMsg);
33457                   }
33458                 }
33459               }
33460             }
33461
33462             return out;
33463           }
33464
33465           /**
33466            * Parse Inline Tokens
33467            */
33468           parseInline(tokens, renderer) {
33469             renderer = renderer || this.renderer;
33470             let out = '',
33471               i,
33472               token;
33473
33474             const l = tokens.length;
33475             for (i = 0; i < l; i++) {
33476               token = tokens[i];
33477               switch (token.type) {
33478                 case 'escape': {
33479                   out += renderer.text(token.text);
33480                   break;
33481                 }
33482                 case 'html': {
33483                   out += renderer.html(token.text);
33484                   break;
33485                 }
33486                 case 'link': {
33487                   out += renderer.link(token.href, token.title, this.parseInline(token.tokens, renderer));
33488                   break;
33489                 }
33490                 case 'image': {
33491                   out += renderer.image(token.href, token.title, token.text);
33492                   break;
33493                 }
33494                 case 'strong': {
33495                   out += renderer.strong(this.parseInline(token.tokens, renderer));
33496                   break;
33497                 }
33498                 case 'em': {
33499                   out += renderer.em(this.parseInline(token.tokens, renderer));
33500                   break;
33501                 }
33502                 case 'codespan': {
33503                   out += renderer.codespan(token.text);
33504                   break;
33505                 }
33506                 case 'br': {
33507                   out += renderer.br();
33508                   break;
33509                 }
33510                 case 'del': {
33511                   out += renderer.del(this.parseInline(token.tokens, renderer));
33512                   break;
33513                 }
33514                 case 'text': {
33515                   out += renderer.text(token.text);
33516                   break;
33517                 }
33518                 default: {
33519                   const errMsg = 'Token with "' + token.type + '" type was not found.';
33520                   if (this.options.silent) {
33521                     console.error(errMsg);
33522                     return;
33523                   } else {
33524                     throw new Error(errMsg);
33525                   }
33526                 }
33527               }
33528             }
33529             return out;
33530           }
33531         };
33532
33533         const {
33534           merge: merge$3,
33535           checkSanitizeDeprecation: checkSanitizeDeprecation$1,
33536           escape: escape$4
33537         } = helpers;
33538         const {
33539           getDefaults,
33540           changeDefaults,
33541           defaults: defaults$5
33542         } = defaults;
33543
33544         /**
33545          * Marked
33546          */
33547         function marked(src, opt, callback) {
33548           // throw error in case of non string input
33549           if (typeof src === 'undefined' || src === null) {
33550             throw new Error('marked(): input parameter is undefined or null');
33551           }
33552           if (typeof src !== 'string') {
33553             throw new Error('marked(): input parameter is of type '
33554               + Object.prototype.toString.call(src) + ', string expected');
33555           }
33556
33557           if (callback || typeof opt === 'function') {
33558             if (!callback) {
33559               callback = opt;
33560               opt = null;
33561             }
33562
33563             opt = merge$3({}, marked.defaults, opt || {});
33564             checkSanitizeDeprecation$1(opt);
33565             const highlight = opt.highlight;
33566             let tokens,
33567               pending,
33568               i = 0;
33569
33570             try {
33571               tokens = Lexer_1.lex(src, opt);
33572             } catch (e) {
33573               return callback(e);
33574             }
33575
33576             pending = tokens.length;
33577
33578             const done = function(err) {
33579               if (err) {
33580                 opt.highlight = highlight;
33581                 return callback(err);
33582               }
33583
33584               let out;
33585
33586               try {
33587                 out = Parser_1.parse(tokens, opt);
33588               } catch (e) {
33589                 err = e;
33590               }
33591
33592               opt.highlight = highlight;
33593
33594               return err
33595                 ? callback(err)
33596                 : callback(null, out);
33597             };
33598
33599             if (!highlight || highlight.length < 3) {
33600               return done();
33601             }
33602
33603             delete opt.highlight;
33604
33605             if (!pending) return done();
33606
33607             for (; i < tokens.length; i++) {
33608               (function(token) {
33609                 if (token.type !== 'code') {
33610                   return --pending || done();
33611                 }
33612                 return highlight(token.text, token.lang, function(err, code) {
33613                   if (err) return done(err);
33614                   if (code == null || code === token.text) {
33615                     return --pending || done();
33616                   }
33617                   token.text = code;
33618                   token.escaped = true;
33619                   --pending || done();
33620                 });
33621               })(tokens[i]);
33622             }
33623
33624             return;
33625           }
33626           try {
33627             opt = merge$3({}, marked.defaults, opt || {});
33628             checkSanitizeDeprecation$1(opt);
33629             return Parser_1.parse(Lexer_1.lex(src, opt), opt);
33630           } catch (e) {
33631             e.message += '\nPlease report this to https://github.com/markedjs/marked.';
33632             if ((opt || marked.defaults).silent) {
33633               return '<p>An error occurred:</p><pre>'
33634                 + escape$4(e.message + '', true)
33635                 + '</pre>';
33636             }
33637             throw e;
33638           }
33639         }
33640
33641         /**
33642          * Options
33643          */
33644
33645         marked.options =
33646         marked.setOptions = function(opt) {
33647           merge$3(marked.defaults, opt);
33648           changeDefaults(marked.defaults);
33649           return marked;
33650         };
33651
33652         marked.getDefaults = getDefaults;
33653
33654         marked.defaults = defaults$5;
33655
33656         /**
33657          * Use Extension
33658          */
33659
33660         marked.use = function(extension) {
33661           const opts = merge$3({}, extension);
33662           if (extension.renderer) {
33663             const renderer = marked.defaults.renderer || new Renderer_1();
33664             for (const prop in extension.renderer) {
33665               const prevRenderer = renderer[prop];
33666               renderer[prop] = (...args) => {
33667                 let ret = extension.renderer[prop].apply(renderer, args);
33668                 if (ret === false) {
33669                   ret = prevRenderer.apply(renderer, args);
33670                 }
33671                 return ret;
33672               };
33673             }
33674             opts.renderer = renderer;
33675           }
33676           if (extension.tokenizer) {
33677             const tokenizer = marked.defaults.tokenizer || new Tokenizer_1();
33678             for (const prop in extension.tokenizer) {
33679               const prevTokenizer = tokenizer[prop];
33680               tokenizer[prop] = (...args) => {
33681                 let ret = extension.tokenizer[prop].apply(tokenizer, args);
33682                 if (ret === false) {
33683                   ret = prevTokenizer.apply(tokenizer, args);
33684                 }
33685                 return ret;
33686               };
33687             }
33688             opts.tokenizer = tokenizer;
33689           }
33690           marked.setOptions(opts);
33691         };
33692
33693         /**
33694          * Expose
33695          */
33696
33697         marked.Parser = Parser_1;
33698         marked.parser = Parser_1.parse;
33699
33700         marked.Renderer = Renderer_1;
33701         marked.TextRenderer = TextRenderer_1;
33702
33703         marked.Lexer = Lexer_1;
33704         marked.lexer = Lexer_1.lex;
33705
33706         marked.Tokenizer = Tokenizer_1;
33707
33708         marked.Slugger = Slugger_1;
33709
33710         marked.parse = marked;
33711
33712         var marked_1 = marked;
33713
33714         const tiler$2 = utilTiler();
33715         const dispatch$3 = dispatch('loaded');
33716         const _tileZoom$2 = 14;
33717         const _osmoseUrlRoot = 'https://osmose.openstreetmap.fr/api/0.3';
33718         let _osmoseData = { icons: {}, items: [] };
33719
33720         // This gets reassigned if reset
33721         let _cache$2;
33722
33723         function abortRequest$2(controller) {
33724           if (controller) {
33725             controller.abort();
33726           }
33727         }
33728
33729         function abortUnwantedRequests$2(cache, tiles) {
33730           Object.keys(cache.inflightTile).forEach(k => {
33731             let wanted = tiles.find(tile => k === tile.id);
33732             if (!wanted) {
33733               abortRequest$2(cache.inflightTile[k]);
33734               delete cache.inflightTile[k];
33735             }
33736           });
33737         }
33738
33739         function encodeIssueRtree$2(d) {
33740           return { minX: d.loc[0], minY: d.loc[1], maxX: d.loc[0], maxY: d.loc[1], data: d };
33741         }
33742
33743         // Replace or remove QAItem from rtree
33744         function updateRtree$2(item, replace) {
33745           _cache$2.rtree.remove(item, (a, b) => a.data.id === b.data.id);
33746
33747           if (replace) {
33748             _cache$2.rtree.insert(item);
33749           }
33750         }
33751
33752         // Issues shouldn't obscure eachother
33753         function preventCoincident$1(loc) {
33754           let coincident = false;
33755           do {
33756             // first time, move marker up. after that, move marker right.
33757             let delta = coincident ? [0.00001, 0] : [0, 0.00001];
33758             loc = geoVecAdd(loc, delta);
33759             let bbox = geoExtent(loc).bbox();
33760             coincident = _cache$2.rtree.search(bbox).length;
33761           } while (coincident);
33762
33763           return loc;
33764         }
33765
33766         var serviceOsmose = {
33767           title: 'osmose',
33768
33769           init() {
33770             _mainFileFetcher.get('qa_data')
33771               .then(d => {
33772                 _osmoseData = d.osmose;
33773                 _osmoseData.items = Object.keys(d.osmose.icons)
33774                   .map(s => s.split('-')[0])
33775                   .reduce((unique, item) => unique.indexOf(item) !== -1 ? unique : [...unique, item], []);
33776               });
33777
33778             if (!_cache$2) {
33779               this.reset();
33780             }
33781
33782             this.event = utilRebind(this, dispatch$3, 'on');
33783           },
33784
33785           reset() {
33786             let _strings = {};
33787             let _colors = {};
33788             if (_cache$2) {
33789               Object.values(_cache$2.inflightTile).forEach(abortRequest$2);
33790               // Strings and colors are static and should not be re-populated
33791               _strings = _cache$2.strings;
33792               _colors = _cache$2.colors;
33793             }
33794             _cache$2 = {
33795               data: {},
33796               loadedTile: {},
33797               inflightTile: {},
33798               inflightPost: {},
33799               closed: {},
33800               rtree: new RBush(),
33801               strings: _strings,
33802               colors: _colors
33803             };
33804           },
33805
33806           loadIssues(projection) {
33807             let params = {
33808               // Tiles return a maximum # of issues
33809               // So we want to filter our request for only types iD supports
33810               item: _osmoseData.items
33811             };
33812
33813             // determine the needed tiles to cover the view
33814             let tiles = tiler$2
33815               .zoomExtent([_tileZoom$2, _tileZoom$2])
33816               .getTiles(projection);
33817
33818             // abort inflight requests that are no longer needed
33819             abortUnwantedRequests$2(_cache$2, tiles);
33820
33821             // issue new requests..
33822             tiles.forEach(tile => {
33823               if (_cache$2.loadedTile[tile.id] || _cache$2.inflightTile[tile.id]) return;
33824
33825               let [ x, y, z ] = tile.xyz;
33826               let url = `${_osmoseUrlRoot}/issues/${z}/${x}/${y}.json?` + utilQsString(params);
33827
33828               let controller = new AbortController();
33829               _cache$2.inflightTile[tile.id] = controller;
33830
33831               d3_json(url, { signal: controller.signal })
33832                 .then(data => {
33833                   delete _cache$2.inflightTile[tile.id];
33834                   _cache$2.loadedTile[tile.id] = true;
33835
33836                   if (data.features) {
33837                     data.features.forEach(issue => {
33838                       const { item, class: cl, uuid: id } = issue.properties;
33839                       /* Osmose issues are uniquely identified by a unique
33840                         `item` and `class` combination (both integer values) */
33841                       const itemType = `${item}-${cl}`;
33842
33843                       // Filter out unsupported issue types (some are too specific or advanced)
33844                       if (itemType in _osmoseData.icons) {
33845                         let loc = issue.geometry.coordinates; // lon, lat
33846                         loc = preventCoincident$1(loc);
33847
33848                         let d = new QAItem(loc, this, itemType, id, { item });
33849
33850                         // Setting elems here prevents UI detail requests
33851                         if (item === 8300 || item === 8360) {
33852                           d.elems = [];
33853                         }
33854
33855                         _cache$2.data[d.id] = d;
33856                         _cache$2.rtree.insert(encodeIssueRtree$2(d));
33857                       }
33858                     });
33859                   }
33860
33861                   dispatch$3.call('loaded');
33862                 })
33863                 .catch(() => {
33864                   delete _cache$2.inflightTile[tile.id];
33865                   _cache$2.loadedTile[tile.id] = true;
33866                 });
33867             });
33868           },
33869
33870           loadIssueDetail(issue) {
33871             // Issue details only need to be fetched once
33872             if (issue.elems !== undefined) {
33873               return Promise.resolve(issue);
33874             }
33875
33876             const url = `${_osmoseUrlRoot}/issue/${issue.id}?langs=${_mainLocalizer.localeCode()}`;
33877             const cacheDetails = data => {
33878               // Associated elements used for highlighting
33879               // Assign directly for immediate use in the callback
33880               issue.elems = data.elems.map(e => e.type.substring(0,1) + e.id);
33881
33882               // Some issues have instance specific detail in a subtitle
33883               issue.detail = data.subtitle ? marked_1(data.subtitle.auto) : '';
33884
33885               this.replaceItem(issue);
33886             };
33887
33888             return d3_json(url).then(cacheDetails).then(() => issue);
33889           },
33890
33891           loadStrings(locale=_mainLocalizer.localeCode()) {
33892             const items = Object.keys(_osmoseData.icons);
33893
33894             if (
33895               locale in _cache$2.strings
33896               && Object.keys(_cache$2.strings[locale]).length === items.length
33897             ) {
33898                 return Promise.resolve(_cache$2.strings[locale]);
33899             }
33900
33901             // May be partially populated already if some requests were successful
33902             if (!(locale in _cache$2.strings)) {
33903               _cache$2.strings[locale] = {};
33904             }
33905
33906             // Only need to cache strings for supported issue types
33907             // Using multiple individual item + class requests to reduce fetched data size
33908             const allRequests = items.map(itemType => {
33909               // No need to request data we already have
33910               if (itemType in _cache$2.strings[locale]) return;
33911
33912               const cacheData = data => {
33913                 // Bunch of nested single value arrays of objects
33914                 const [ cat = {items:[]} ] = data.categories;
33915                 const [ item = {class:[]} ] = cat.items;
33916                 const [ cl = null ] = item.class;
33917
33918                 // If null default value is reached, data wasn't as expected (or was empty)
33919                 if (!cl) {
33920                   /* eslint-disable no-console */
33921                   console.log(`Osmose strings request (${itemType}) had unexpected data`);
33922                   /* eslint-enable no-console */
33923                   return;
33924                 }
33925
33926                 // Cache served item colors to automatically style issue markers later
33927                 const { item: itemInt, color } = item;
33928                 if (/^#[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}/.test(color)) {
33929                   _cache$2.colors[itemInt] = color;
33930                 }
33931
33932                 // Value of root key will be null if no string exists
33933                 // If string exists, value is an object with key 'auto' for string
33934                 const { title, detail, fix, trap } = cl;
33935
33936                 // Osmose titles shouldn't contain markdown
33937                 let issueStrings = {};
33938                 if (title) issueStrings.title = title.auto;
33939                 if (detail) issueStrings.detail = marked_1(detail.auto);
33940                 if (trap) issueStrings.trap = marked_1(trap.auto);
33941                 if (fix) issueStrings.fix = marked_1(fix.auto);
33942
33943                 _cache$2.strings[locale][itemType] = issueStrings;
33944               };
33945
33946               const [ item, cl ] = itemType.split('-');
33947
33948               // Osmose API falls back to English strings where untranslated or if locale doesn't exist
33949               const url = `${_osmoseUrlRoot}/items/${item}/class/${cl}?langs=${locale}`;
33950
33951               return d3_json(url).then(cacheData);
33952             });
33953
33954             return Promise.all(allRequests).then(() => _cache$2.strings[locale]);
33955           },
33956
33957           getStrings(itemType, locale=_mainLocalizer.localeCode()) {
33958             // No need to fallback to English, Osmose API handles this for us
33959             return (locale in _cache$2.strings) ? _cache$2.strings[locale][itemType] : {};
33960           },
33961
33962           getColor(itemType) {
33963             return (itemType in _cache$2.colors) ? _cache$2.colors[itemType] : '#FFFFFF';
33964           },
33965
33966           postUpdate(issue, callback) {
33967             if (_cache$2.inflightPost[issue.id]) {
33968               return callback({ message: 'Issue update already inflight', status: -2 }, issue);
33969             }
33970
33971             // UI sets the status to either 'done' or 'false'
33972             const url = `${_osmoseUrlRoot}/issue/${issue.id}/${issue.newStatus}`;
33973             const controller = new AbortController();
33974             const after = () => {
33975               delete _cache$2.inflightPost[issue.id];
33976
33977               this.removeItem(issue);
33978               if (issue.newStatus === 'done') {
33979                 // Keep track of the number of issues closed per `item` to tag the changeset
33980                 if (!(issue.item in _cache$2.closed)) {
33981                   _cache$2.closed[issue.item] = 0;
33982                 }
33983                 _cache$2.closed[issue.item] += 1;
33984               }
33985               if (callback) callback(null, issue);
33986             };
33987
33988             _cache$2.inflightPost[issue.id] = controller;
33989
33990             fetch(url, { signal: controller.signal })
33991               .then(after)
33992               .catch(err => {
33993                 delete _cache$2.inflightPost[issue.id];
33994                 if (callback) callback(err.message);
33995               });
33996           },
33997
33998           // Get all cached QAItems covering the viewport
33999           getItems(projection) {
34000             const viewport = projection.clipExtent();
34001             const min = [viewport[0][0], viewport[1][1]];
34002             const max = [viewport[1][0], viewport[0][1]];
34003             const bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
34004
34005             return _cache$2.rtree.search(bbox).map(d => d.data);
34006           },
34007
34008           // Get a QAItem from cache
34009           // NOTE: Don't change method name until UI v3 is merged
34010           getError(id) {
34011             return _cache$2.data[id];
34012           },
34013
34014           // get the name of the icon to display for this item
34015           getIcon(itemType) {
34016             return _osmoseData.icons[itemType];
34017           },
34018
34019           // Replace a single QAItem in the cache
34020           replaceItem(item) {
34021             if (!(item instanceof QAItem) || !item.id) return;
34022
34023             _cache$2.data[item.id] = item;
34024             updateRtree$2(encodeIssueRtree$2(item), true); // true = replace
34025             return item;
34026           },
34027
34028           // Remove a single QAItem from the cache
34029           removeItem(item) {
34030             if (!(item instanceof QAItem) || !item.id) return;
34031
34032             delete _cache$2.data[item.id];
34033             updateRtree$2(encodeIssueRtree$2(item), false); // false = remove
34034           },
34035
34036           // Used to populate `closed:osmose:*` changeset tags
34037           getClosedCounts() {
34038             return _cache$2.closed;
34039           },
34040
34041           itemURL(item) {
34042             return `https://osmose.openstreetmap.fr/en/error/${item.id}`;
34043           }
34044         };
34045
34046         /*
34047             A standalone SVG element that contains only a `defs` sub-element. To be
34048             used once globally, since defs IDs must be unique within a document.
34049         */
34050         function svgDefs(context) {
34051
34052             function drawDefs(selection) {
34053                 var defs = selection.append('defs');
34054
34055                 // add markers
34056                 defs
34057                     .append('marker')
34058                     .attr('id', 'ideditor-oneway-marker')
34059                     .attr('viewBox', '0 0 10 5')
34060                     .attr('refX', 2.5)
34061                     .attr('refY', 2.5)
34062                     .attr('markerWidth', 2)
34063                     .attr('markerHeight', 2)
34064                     .attr('markerUnits', 'strokeWidth')
34065                     .attr('orient', 'auto')
34066                     .append('path')
34067                     .attr('class', 'oneway-marker-path')
34068                     .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')
34069                     .attr('stroke', 'none')
34070                     .attr('fill', '#000')
34071                     .attr('opacity', '0.75');
34072
34073                 // SVG markers have to be given a colour where they're defined
34074                 // (they can't inherit it from the line they're attached to),
34075                 // so we need to manually define markers for each color of tag
34076                 // (also, it's slightly nicer if we can control the
34077                 // positioning for different tags)
34078                 function addSidedMarker(name, color, offset) {
34079                     defs
34080                         .append('marker')
34081                         .attr('id', 'ideditor-sided-marker-' + name)
34082                         .attr('viewBox', '0 0 2 2')
34083                         .attr('refX', 1)
34084                         .attr('refY', -offset)
34085                         .attr('markerWidth', 1.5)
34086                         .attr('markerHeight', 1.5)
34087                         .attr('markerUnits', 'strokeWidth')
34088                         .attr('orient', 'auto')
34089                         .append('path')
34090                         .attr('class', 'sided-marker-path sided-marker-' + name + '-path')
34091                         .attr('d', 'M 0,0 L 1,1 L 2,0 z')
34092                         .attr('stroke', 'none')
34093                         .attr('fill', color);
34094                 }
34095                 addSidedMarker('natural', 'rgb(170, 170, 170)', 0);
34096                 // for a coastline, the arrows are (somewhat unintuitively) on
34097                 // the water side, so let's color them blue (with a gap) to
34098                 // give a stronger indication
34099                 addSidedMarker('coastline', '#77dede', 1);
34100                 addSidedMarker('waterway', '#77dede', 1);
34101                 // barriers have a dashed line, and separating the triangle
34102                 // from the line visually suits that
34103                 addSidedMarker('barrier', '#ddd', 1);
34104                 addSidedMarker('man_made', '#fff', 0);
34105
34106                 defs
34107                     .append('marker')
34108                     .attr('id', 'ideditor-viewfield-marker')
34109                     .attr('viewBox', '0 0 16 16')
34110                     .attr('refX', 8)
34111                     .attr('refY', 16)
34112                     .attr('markerWidth', 4)
34113                     .attr('markerHeight', 4)
34114                     .attr('markerUnits', 'strokeWidth')
34115                     .attr('orient', 'auto')
34116                     .append('path')
34117                     .attr('class', 'viewfield-marker-path')
34118                     .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')
34119                     .attr('fill', '#333')
34120                     .attr('fill-opacity', '0.75')
34121                     .attr('stroke', '#fff')
34122                     .attr('stroke-width', '0.5px')
34123                     .attr('stroke-opacity', '0.75');
34124
34125                 defs
34126                     .append('marker')
34127                     .attr('id', 'ideditor-viewfield-marker-wireframe')
34128                     .attr('viewBox', '0 0 16 16')
34129                     .attr('refX', 8)
34130                     .attr('refY', 16)
34131                     .attr('markerWidth', 4)
34132                     .attr('markerHeight', 4)
34133                     .attr('markerUnits', 'strokeWidth')
34134                     .attr('orient', 'auto')
34135                     .append('path')
34136                     .attr('class', 'viewfield-marker-path')
34137                     .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')
34138                     .attr('fill', 'none')
34139                     .attr('stroke', '#fff')
34140                     .attr('stroke-width', '0.5px')
34141                     .attr('stroke-opacity', '0.75');
34142
34143                 // add patterns
34144                 var patterns = defs.selectAll('pattern')
34145                     .data([
34146                         // pattern name, pattern image name
34147                         ['beach', 'dots'],
34148                         ['construction', 'construction'],
34149                         ['cemetery', 'cemetery'],
34150                         ['cemetery_christian', 'cemetery_christian'],
34151                         ['cemetery_buddhist', 'cemetery_buddhist'],
34152                         ['cemetery_muslim', 'cemetery_muslim'],
34153                         ['cemetery_jewish', 'cemetery_jewish'],
34154                         ['farmland', 'farmland'],
34155                         ['farmyard', 'farmyard'],
34156                         ['forest', 'forest'],
34157                         ['forest_broadleaved', 'forest_broadleaved'],
34158                         ['forest_needleleaved', 'forest_needleleaved'],
34159                         ['forest_leafless', 'forest_leafless'],
34160                         ['golf_green', 'grass'],
34161                         ['grass', 'grass'],
34162                         ['landfill', 'landfill'],
34163                         ['meadow', 'grass'],
34164                         ['orchard', 'orchard'],
34165                         ['pond', 'pond'],
34166                         ['quarry', 'quarry'],
34167                         ['scrub', 'bushes'],
34168                         ['vineyard', 'vineyard'],
34169                         ['water_standing', 'lines'],
34170                         ['waves', 'waves'],
34171                         ['wetland', 'wetland'],
34172                         ['wetland_marsh', 'wetland_marsh'],
34173                         ['wetland_swamp', 'wetland_swamp'],
34174                         ['wetland_bog', 'wetland_bog'],
34175                         ['wetland_reedbed', 'wetland_reedbed']
34176                     ])
34177                     .enter()
34178                     .append('pattern')
34179                     .attr('id', function (d) { return 'ideditor-pattern-' + d[0]; })
34180                     .attr('width', 32)
34181                     .attr('height', 32)
34182                     .attr('patternUnits', 'userSpaceOnUse');
34183
34184                 patterns
34185                     .append('rect')
34186                     .attr('x', 0)
34187                     .attr('y', 0)
34188                     .attr('width', 32)
34189                     .attr('height', 32)
34190                     .attr('class', function (d) { return 'pattern-color-' + d[0]; });
34191
34192                 patterns
34193                     .append('image')
34194                     .attr('x', 0)
34195                     .attr('y', 0)
34196                     .attr('width', 32)
34197                     .attr('height', 32)
34198                     .attr('xlink:href', function (d) {
34199                         return context.imagePath('pattern/' + d[1] + '.png');
34200                     });
34201
34202                 // add clip paths
34203                 defs.selectAll('clipPath')
34204                     .data([12, 18, 20, 32, 45])
34205                     .enter()
34206                     .append('clipPath')
34207                     .attr('id', function (d) { return 'ideditor-clip-square-' + d; })
34208                     .append('rect')
34209                     .attr('x', 0)
34210                     .attr('y', 0)
34211                     .attr('width', function (d) { return d; })
34212                     .attr('height', function (d) { return d; });
34213
34214                 // add symbol spritesheets
34215                 defs
34216                     .call(drawDefs.addSprites, [
34217                         'iD-sprite', 'maki-sprite', 'temaki-sprite', 'fa-sprite', 'tnp-sprite', 'community-sprite'
34218                     ], true);
34219             }
34220
34221
34222             drawDefs.addSprites = function(selection, ids, overrideColors) {
34223                 var spritesheets = selection.selectAll('.spritesheet');
34224                 var currData = spritesheets.data();
34225                 var data = utilArrayUniq(currData.concat(ids));
34226
34227                 spritesheets
34228                     .data(data)
34229                     .enter()
34230                     .append('g')
34231                     .attr('class', function(d) { return 'spritesheet spritesheet-' + d; })
34232                     .each(function(d) {
34233                         var url = context.imagePath(d + '.svg');
34234                         var node = select(this).node();
34235
34236                         svg(url)
34237                             .then(function(svg) {
34238                                 node.appendChild(
34239                                     select(svg.documentElement).attr('id', 'ideditor-' + d).node()
34240                                 );
34241                                 if (overrideColors && d !== 'iD-sprite') {   // allow icon colors to be overridden..
34242                                     select(node).selectAll('path')
34243                                         .attr('fill', 'currentColor');
34244                                 }
34245                             })
34246                             .catch(function() {
34247                                 /* ignore */
34248                             });
34249                     });
34250             };
34251
34252
34253             return drawDefs;
34254         }
34255
34256         /* global Mapillary:false */
34257
34258
34259         var apibase = 'https://a.mapillary.com/v3/';
34260         var viewercss = 'mapillary-js/mapillary.min.css';
34261         var viewerjs = 'mapillary-js/mapillary.min.js';
34262         var clientId = 'NzNRM2otQkR2SHJzaXJmNmdQWVQ0dzo1ZWYyMmYwNjdmNDdlNmVi';
34263         var mapFeatureConfig = {
34264             values: [
34265                 'construction--flat--crosswalk-plain',
34266                 'marking--discrete--crosswalk-zebra',
34267                 'object--banner',
34268                 'object--bench',
34269                 'object--bike-rack',
34270                 'object--billboard',
34271                 'object--catch-basin',
34272                 'object--cctv-camera',
34273                 'object--fire-hydrant',
34274                 'object--mailbox',
34275                 'object--manhole',
34276                 'object--phone-booth',
34277                 'object--sign--advertisement',
34278                 'object--sign--information',
34279                 'object--sign--store',
34280                 'object--street-light',
34281                 'object--support--utility-pole',
34282                 'object--traffic-light--*',
34283                 'object--traffic-light--pedestrians',
34284                 'object--trash-can'
34285             ].join(',')
34286         };
34287         var maxResults = 1000;
34288         var tileZoom = 14;
34289         var tiler$3 = utilTiler().zoomExtent([tileZoom, tileZoom]).skipNullIsland(true);
34290         var dispatch$4 = dispatch('loadedImages', 'loadedSigns', 'loadedMapFeatures', 'bearingChanged');
34291         var _mlyFallback = false;
34292         var _mlyCache;
34293         var _mlyClicks;
34294         var _mlySelectedImageKey;
34295         var _mlyViewer;
34296
34297
34298         function abortRequest$3(controller) {
34299             controller.abort();
34300         }
34301
34302
34303         function maxPageAtZoom(z) {
34304             if (z < 15)   return 2;
34305             if (z === 15) return 5;
34306             if (z === 16) return 10;
34307             if (z === 17) return 20;
34308             if (z === 18) return 40;
34309             if (z > 18)   return 80;
34310         }
34311
34312
34313         function loadTiles(which, url, projection) {
34314             var currZoom = Math.floor(geoScaleToZoom(projection.scale()));
34315             var tiles = tiler$3.getTiles(projection);
34316
34317             // abort inflight requests that are no longer needed
34318             var cache = _mlyCache[which];
34319             Object.keys(cache.inflight).forEach(function(k) {
34320                 var wanted = tiles.find(function(tile) { return k.indexOf(tile.id + ',') === 0; });
34321                 if (!wanted) {
34322                     abortRequest$3(cache.inflight[k]);
34323                     delete cache.inflight[k];
34324                 }
34325             });
34326
34327             tiles.forEach(function(tile) {
34328                 loadNextTilePage(which, currZoom, url, tile);
34329             });
34330         }
34331
34332
34333         function loadNextTilePage(which, currZoom, url, tile) {
34334             var cache = _mlyCache[which];
34335             var rect = tile.extent.rectangle();
34336             var maxPages = maxPageAtZoom(currZoom);
34337             var nextPage = cache.nextPage[tile.id] || 0;
34338             var nextURL = cache.nextURL[tile.id] || url +
34339                 utilQsString({
34340                     per_page: maxResults,
34341                     page: nextPage,
34342                     client_id: clientId,
34343                     bbox: [rect[0], rect[1], rect[2], rect[3]].join(','),
34344                 });
34345
34346             if (nextPage > maxPages) return;
34347
34348             var id = tile.id + ',' + String(nextPage);
34349             if (cache.loaded[id] || cache.inflight[id]) return;
34350
34351             var controller = new AbortController();
34352             cache.inflight[id] = controller;
34353
34354             var options = {
34355                 method: 'GET',
34356                 signal: controller.signal,
34357                 headers: { 'Content-Type': 'application/json' }
34358             };
34359
34360             fetch(nextURL, options)
34361                 .then(function(response) {
34362                     if (!response.ok) {
34363                         throw new Error(response.status + ' ' + response.statusText);
34364                     }
34365                     var linkHeader = response.headers.get('Link');
34366                     if (linkHeader) {
34367                         var pagination = parsePagination(linkHeader);
34368                         if (pagination.next) {
34369                             cache.nextURL[tile.id] = pagination.next;
34370                         }
34371                     }
34372                     return response.json();
34373                 })
34374                 .then(function(data) {
34375                     cache.loaded[id] = true;
34376                     delete cache.inflight[id];
34377                     if (!data || !data.features || !data.features.length) {
34378                         throw new Error('No Data');
34379                     }
34380
34381                     var features = data.features.map(function(feature) {
34382                         var loc = feature.geometry.coordinates;
34383                         var d;
34384
34385                         // An image (shown as a green dot on the map) is a single street photo with extra
34386                         // information such as location, camera angle (CA), camera model, and so on.
34387                         // Each image feature is a GeoJSON Point
34388                         if (which === 'images') {
34389                             d = {
34390                                 loc: loc,
34391                                 key: feature.properties.key,
34392                                 ca: feature.properties.ca,
34393                                 captured_at: feature.properties.captured_at,
34394                                 captured_by: feature.properties.username,
34395                                 pano: feature.properties.pano
34396                             };
34397
34398                             cache.forImageKey[d.key] = d;     // cache imageKey -> image
34399
34400                         // Mapillary organizes images as sequences. A sequence of images are continuously captured
34401                         // by a user at a give time. Sequences are shown on the map as green lines.
34402                         // Each sequence feature is a GeoJSON LineString
34403                         } else if (which === 'sequences') {
34404                             var sequenceKey = feature.properties.key;
34405                             cache.lineString[sequenceKey] = feature;           // cache sequenceKey -> lineString
34406                             feature.properties.coordinateProperties.image_keys.forEach(function(imageKey) {
34407                                 cache.forImageKey[imageKey] = sequenceKey;     // cache imageKey -> sequenceKey
34408                             });
34409                             return false;    // because no `d` data worth loading into an rbush
34410
34411                         // An image detection is a semantic pixel area on an image. The area could indicate
34412                         // sky, trees, sidewalk in the image. A detection can be a polygon, a bounding box, or a point.
34413                         // Each image_detection feature is a GeoJSON Point (located where the image was taken)
34414                         } else if (which === 'image_detections') {
34415                             d = {
34416                                 key: feature.properties.key,
34417                                 image_key: feature.properties.image_key,
34418                                 value: feature.properties.value,
34419                                 package: feature.properties.package,
34420                                 shape: feature.properties.shape
34421                             };
34422
34423                             // cache imageKey -> image_detections
34424                             if (!cache.forImageKey[d.image_key]) {
34425                                 cache.forImageKey[d.image_key] = [];
34426                             }
34427                             cache.forImageKey[d.image_key].push(d);
34428                             return false;    // because no `d` data worth loading into an rbush
34429
34430
34431                         // A map feature is a real world object that can be shown on a map. It could be any object
34432                         // recognized from images, manually added in images, or added on the map.
34433                         // Each map feature is a GeoJSON Point (located where the feature is)
34434                         } else if (which === 'map_features' || which === 'points') {
34435                             d = {
34436                                 loc: loc,
34437                                 key: feature.properties.key,
34438                                 value: feature.properties.value,
34439                                 package: feature.properties.package,
34440                                 detections: feature.properties.detections
34441                             };
34442                         }
34443
34444                         return {
34445                             minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1], data: d
34446                         };
34447
34448                     }).filter(Boolean);
34449
34450                     if (cache.rtree && features) {
34451                         cache.rtree.load(features);
34452                     }
34453
34454                     if (data.features.length === maxResults) {  // more pages to load
34455                         cache.nextPage[tile.id] = nextPage + 1;
34456                         loadNextTilePage(which, currZoom, url, tile);
34457                     } else {
34458                         cache.nextPage[tile.id] = Infinity;     // no more pages to load
34459                     }
34460
34461                     if (which === 'images' || which === 'sequences') {
34462                         dispatch$4.call('loadedImages');
34463                     } else if (which === 'map_features') {
34464                         dispatch$4.call('loadedSigns');
34465                     } else if (which === 'points') {
34466                         dispatch$4.call('loadedMapFeatures');
34467                     }
34468                 })
34469                 .catch(function() {
34470                     cache.loaded[id] = true;
34471                     delete cache.inflight[id];
34472                 });
34473         }
34474
34475         // extract links to pages of API results
34476         function parsePagination(links) {
34477             return links.split(',').map(function(rel) {
34478                 var elements = rel.split(';');
34479                 if (elements.length === 2) {
34480                     return [
34481                         /<(.+)>/.exec(elements[0])[1],
34482                         /rel="(.+)"/.exec(elements[1])[1]
34483                     ];
34484                 } else {
34485                     return ['',''];
34486                 }
34487             }).reduce(function(pagination, val) {
34488                 pagination[val[1]] = val[0];
34489                 return pagination;
34490             }, {});
34491         }
34492
34493
34494         // partition viewport into higher zoom tiles
34495         function partitionViewport(projection) {
34496             var z = geoScaleToZoom(projection.scale());
34497             var z2 = (Math.ceil(z * 2) / 2) + 2.5;   // round to next 0.5 and add 2.5
34498             var tiler = utilTiler().zoomExtent([z2, z2]);
34499
34500             return tiler.getTiles(projection)
34501                 .map(function(tile) { return tile.extent; });
34502         }
34503
34504
34505         // no more than `limit` results per partition.
34506         function searchLimited(limit, projection, rtree) {
34507             limit = limit || 5;
34508
34509             return partitionViewport(projection)
34510                 .reduce(function(result, extent) {
34511                     var found = rtree.search(extent.bbox())
34512                         .slice(0, limit)
34513                         .map(function(d) { return d.data; });
34514
34515                     return (found.length ? result.concat(found) : result);
34516                 }, []);
34517         }
34518
34519
34520
34521         var serviceMapillary = {
34522
34523             init: function() {
34524                 if (!_mlyCache) {
34525                     this.reset();
34526                 }
34527
34528                 this.event = utilRebind(this, dispatch$4, 'on');
34529             },
34530
34531             reset: function() {
34532                 if (_mlyCache) {
34533                     Object.values(_mlyCache.images.inflight).forEach(abortRequest$3);
34534                     Object.values(_mlyCache.image_detections.inflight).forEach(abortRequest$3);
34535                     Object.values(_mlyCache.map_features.inflight).forEach(abortRequest$3);
34536                     Object.values(_mlyCache.points.inflight).forEach(abortRequest$3);
34537                     Object.values(_mlyCache.sequences.inflight).forEach(abortRequest$3);
34538                 }
34539
34540                 _mlyCache = {
34541                     images: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: new RBush(), forImageKey: {} },
34542                     image_detections: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, forImageKey: {} },
34543                     map_features: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: new RBush() },
34544                     points: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: new RBush() },
34545                     sequences: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: new RBush(), forImageKey: {}, lineString: {} }
34546                 };
34547
34548                 _mlySelectedImageKey = null;
34549                 _mlyClicks = [];
34550             },
34551
34552
34553             images: function(projection) {
34554                 var limit = 5;
34555                 return searchLimited(limit, projection, _mlyCache.images.rtree);
34556             },
34557
34558
34559             signs: function(projection) {
34560                 var limit = 5;
34561                 return searchLimited(limit, projection, _mlyCache.map_features.rtree);
34562             },
34563
34564
34565             mapFeatures: function(projection) {
34566                 var limit = 5;
34567                 return searchLimited(limit, projection, _mlyCache.points.rtree);
34568             },
34569
34570
34571             cachedImage: function(imageKey) {
34572                 return _mlyCache.images.forImageKey[imageKey];
34573             },
34574
34575
34576             sequences: function(projection) {
34577                 var viewport = projection.clipExtent();
34578                 var min = [viewport[0][0], viewport[1][1]];
34579                 var max = [viewport[1][0], viewport[0][1]];
34580                 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
34581                 var sequenceKeys = {};
34582
34583                 // all sequences for images in viewport
34584                 _mlyCache.images.rtree.search(bbox)
34585                     .forEach(function(d) {
34586                         var sequenceKey = _mlyCache.sequences.forImageKey[d.data.key];
34587                         if (sequenceKey) {
34588                             sequenceKeys[sequenceKey] = true;
34589                         }
34590                     });
34591
34592                 // Return lineStrings for the sequences
34593                 return Object.keys(sequenceKeys).map(function(sequenceKey) {
34594                     return _mlyCache.sequences.lineString[sequenceKey];
34595                 });
34596             },
34597
34598
34599             signsSupported: function() {
34600                 return true;
34601             },
34602
34603
34604             loadImages: function(projection) {
34605                 loadTiles('images', apibase + 'images?sort_by=key&', projection);
34606                 loadTiles('sequences', apibase + 'sequences?sort_by=key&', projection);
34607             },
34608
34609
34610             loadSigns: function(projection) {
34611                 // if we are looking at signs, we'll actually need to fetch images too
34612                 loadTiles('images', apibase + 'images?sort_by=key&', projection);
34613                 loadTiles('map_features', apibase + 'map_features?layers=trafficsigns&min_nbr_image_detections=2&sort_by=key&', projection);
34614                 loadTiles('image_detections', apibase + 'image_detections?layers=trafficsigns&sort_by=key&', projection);
34615             },
34616
34617
34618             loadMapFeatures: function(projection) {
34619                 // if we are looking at signs, we'll actually need to fetch images too
34620                 loadTiles('images', apibase + 'images?sort_by=key', projection);
34621                 loadTiles('points', apibase + 'map_features?layers=points&min_nbr_image_detections=2&sort_by=key&values=' + mapFeatureConfig.values + '&', projection);
34622                 loadTiles('image_detections', apibase + 'image_detections?layers=points&sort_by=key&values=' + mapFeatureConfig.values + '&', projection);
34623             },
34624
34625
34626             loadViewer: function(context) {
34627                 // add mly-wrapper
34628                 var wrap = context.container().select('.photoviewer')
34629                     .selectAll('.mly-wrapper')
34630                     .data([0]);
34631
34632                 wrap.enter()
34633                     .append('div')
34634                     .attr('id', 'ideditor-mly')
34635                     .attr('class', 'photo-wrapper mly-wrapper')
34636                     .classed('hide', true);
34637
34638                 // load mapillary-viewercss
34639                 select('head').selectAll('#ideditor-mapillary-viewercss')
34640                     .data([0])
34641                     .enter()
34642                     .append('link')
34643                     .attr('id', 'ideditor-mapillary-viewercss')
34644                     .attr('rel', 'stylesheet')
34645                     .attr('href', context.asset(viewercss));
34646
34647                 // load mapillary-viewerjs
34648                 select('head').selectAll('#ideditor-mapillary-viewerjs')
34649                     .data([0])
34650                     .enter()
34651                     .append('script')
34652                     .attr('id', 'ideditor-mapillary-viewerjs')
34653                     .attr('src', context.asset(viewerjs));
34654
34655                 // load mapillary signs sprite
34656                 var defs = context.container().select('defs');
34657                 defs.call(svgDefs(context).addSprites, ['mapillary-sprite', 'mapillary-object-sprite'], false /* don't override colors */ );
34658
34659                 // Register viewer resize handler
34660                 context.ui().photoviewer.on('resize.mapillary', function() {
34661                     if (_mlyViewer) {
34662                         _mlyViewer.resize();
34663                     }
34664                 });
34665             },
34666
34667
34668             showViewer: function(context) {
34669                 var wrap = context.container().select('.photoviewer')
34670                     .classed('hide', false);
34671
34672                 var isHidden = wrap.selectAll('.photo-wrapper.mly-wrapper.hide').size();
34673
34674                 if (isHidden && _mlyViewer) {
34675                     wrap
34676                         .selectAll('.photo-wrapper:not(.mly-wrapper)')
34677                         .classed('hide', true);
34678
34679                     wrap
34680                         .selectAll('.photo-wrapper.mly-wrapper')
34681                         .classed('hide', false);
34682
34683                     _mlyViewer.resize();
34684                 }
34685
34686                 return this;
34687             },
34688
34689
34690             hideViewer: function(context) {
34691                 _mlySelectedImageKey = null;
34692
34693                 if (!_mlyFallback && _mlyViewer) {
34694                     _mlyViewer.getComponent('sequence').stop();
34695                 }
34696
34697                 var viewer = context.container().select('.photoviewer');
34698                 if (!viewer.empty()) viewer.datum(null);
34699
34700                 viewer
34701                     .classed('hide', true)
34702                     .selectAll('.photo-wrapper')
34703                     .classed('hide', true);
34704
34705                 context.container().selectAll('.viewfield-group, .sequence, .icon-detected')
34706                     .classed('currentView', false);
34707
34708                 return this.setStyles(context, null, true);
34709             },
34710
34711
34712             parsePagination: parsePagination,
34713
34714
34715             updateViewer: function(context, imageKey) {
34716                 if (!imageKey) return this;
34717
34718                 if (!_mlyViewer) {
34719                     this.initViewer(context, imageKey);
34720                 } else {
34721                     _mlyViewer.moveToKey(imageKey)
34722                         .catch(function(e) { console.error('mly3', e); });  // eslint-disable-line no-console
34723                 }
34724
34725                 return this;
34726             },
34727
34728
34729             initViewer: function(context, imageKey) {
34730                 var that = this;
34731                 if (window.Mapillary && imageKey) {
34732                     var opts = {
34733                         baseImageSize: 320,
34734                         component: {
34735                             cover: false,
34736                             keyboard: false,
34737                             tag: true
34738                         }
34739                     };
34740
34741                     // Disable components requiring WebGL support
34742                     if (!Mapillary.isSupported() && Mapillary.isFallbackSupported()) {
34743                         _mlyFallback = true;
34744                         opts.component = {
34745                             cover: false,
34746                             direction: false,
34747                             imagePlane: false,
34748                             keyboard: false,
34749                             mouse: false,
34750                             sequence: false,
34751                             tag: false,
34752                             image: true,        // fallback
34753                             navigation: true    // fallback
34754                         };
34755                     }
34756
34757                     _mlyViewer = new Mapillary.Viewer('ideditor-mly', clientId, null, opts);
34758                     _mlyViewer.on('nodechanged', nodeChanged);
34759                     _mlyViewer.on('bearingchanged', bearingChanged);
34760                     _mlyViewer.moveToKey(imageKey)
34761                         .catch(function(e) { console.error('mly3', e); });  // eslint-disable-line no-console
34762                 }
34763
34764                 // nodeChanged: called after the viewer has changed images and is ready.
34765                 //
34766                 // There is some logic here to batch up clicks into a _mlyClicks array
34767                 // because the user might click on a lot of markers quickly and nodechanged
34768                 // may be called out of order asychronously.
34769                 //
34770                 // Clicks are added to the array in `selectedImage` and removed here.
34771                 //
34772                 function nodeChanged(node) {
34773                     if (!_mlyFallback) {
34774                         _mlyViewer.getComponent('tag').removeAll();  // remove previous detections
34775                     }
34776
34777                     var clicks = _mlyClicks;
34778                     var index = clicks.indexOf(node.key);
34779                     var selectedKey = _mlySelectedImageKey;
34780
34781                     if (index > -1) {              // `nodechanged` initiated from clicking on a marker..
34782                         clicks.splice(index, 1);   // remove the click
34783                         // If `node.key` matches the current _mlySelectedImageKey, call `selectImage()`
34784                         // one more time to update the detections and attribution..
34785                         if (node.key === selectedKey) {
34786                             that.selectImage(context, _mlySelectedImageKey, true);
34787                         }
34788                     } else {             // `nodechanged` initiated from the Mapillary viewer controls..
34789                         var loc = node.computedLatLon ? [node.computedLatLon.lon, node.computedLatLon.lat] : [node.latLon.lon, node.latLon.lat];
34790                         context.map().centerEase(loc);
34791                         that.selectImage(context, node.key, true);
34792                     }
34793                 }
34794
34795                 function bearingChanged(e) {
34796                     dispatch$4.call('bearingChanged', undefined, e);
34797                 }
34798             },
34799
34800
34801             // Pass in the image key string as `imageKey`.
34802             // This allows images to be selected from places that dont have access
34803             // to the full image datum (like the street signs layer or the js viewer)
34804             selectImage: function(context, imageKey, fromViewer) {
34805
34806                 _mlySelectedImageKey = imageKey;
34807
34808                 // Note the datum could be missing, but we'll try to carry on anyway.
34809                 // There just might be a delay before user sees detections, captured_at, etc.
34810                 var d = _mlyCache.images.forImageKey[imageKey];
34811
34812                 var viewer = context.container().select('.photoviewer');
34813                 if (!viewer.empty()) viewer.datum(d);
34814
34815                 imageKey = (d && d.key) || imageKey;
34816                 if (!fromViewer && imageKey) {
34817                     _mlyClicks.push(imageKey);
34818                 }
34819
34820                 this.setStyles(context, null, true);
34821
34822                 // if signs signs are shown, highlight the ones that appear in this image
34823                 context.container().selectAll('.layer-mapillary-signs .icon-detected')
34824                     .classed('currentView', function(d) {
34825                         return d.detections.some(function(detection) {
34826                             return detection.image_key === imageKey;
34827                         });
34828                     });
34829
34830                 if (d) {
34831                     this.updateDetections(d);
34832                 }
34833
34834                 return this;
34835             },
34836
34837
34838             getSelectedImageKey: function() {
34839                 return _mlySelectedImageKey;
34840             },
34841
34842
34843             getSequenceKeyForImageKey: function(imageKey) {
34844                 return _mlyCache.sequences.forImageKey[imageKey];
34845             },
34846
34847
34848             // Updates the currently highlighted sequence and selected bubble.
34849             // Reset is only necessary when interacting with the viewport because
34850             // this implicitly changes the currently selected bubble/sequence
34851             setStyles: function(context, hovered, reset) {
34852                 if (reset) {  // reset all layers
34853                     context.container().selectAll('.viewfield-group')
34854                         .classed('highlighted', false)
34855                         .classed('hovered', false)
34856                         .classed('currentView', false);
34857
34858                     context.container().selectAll('.sequence')
34859                         .classed('highlighted', false)
34860                         .classed('currentView', false);
34861                 }
34862
34863                 var hoveredImageKey = hovered && hovered.key;
34864                 var hoveredSequenceKey = hoveredImageKey && this.getSequenceKeyForImageKey(hoveredImageKey);
34865                 var hoveredLineString = hoveredSequenceKey && _mlyCache.sequences.lineString[hoveredSequenceKey];
34866                 var hoveredImageKeys = (hoveredLineString && hoveredLineString.properties.coordinateProperties.image_keys) || [];
34867
34868                 var selectedImageKey = _mlySelectedImageKey;
34869                 var selectedSequenceKey = selectedImageKey && this.getSequenceKeyForImageKey(selectedImageKey);
34870                 var selectedLineString = selectedSequenceKey && _mlyCache.sequences.lineString[selectedSequenceKey];
34871                 var selectedImageKeys = (selectedLineString && selectedLineString.properties.coordinateProperties.image_keys) || [];
34872
34873                 // highlight sibling viewfields on either the selected or the hovered sequences
34874                 var highlightedImageKeys = utilArrayUnion(hoveredImageKeys, selectedImageKeys);
34875
34876                 context.container().selectAll('.layer-mapillary .viewfield-group')
34877                     .classed('highlighted', function(d) { return highlightedImageKeys.indexOf(d.key) !== -1; })
34878                     .classed('hovered', function(d) { return d.key === hoveredImageKey; })
34879                     .classed('currentView', function(d) { return d.key === selectedImageKey; });
34880
34881                 context.container().selectAll('.layer-mapillary .sequence')
34882                     .classed('highlighted', function(d) { return d.properties.key === hoveredSequenceKey; })
34883                     .classed('currentView', function(d) { return d.properties.key === selectedSequenceKey; });
34884
34885                 // update viewfields if needed
34886                 context.container().selectAll('.viewfield-group .viewfield')
34887                     .attr('d', viewfieldPath);
34888
34889                 function viewfieldPath() {
34890                     var d = this.parentNode.__data__;
34891                     if (d.pano && d.key !== selectedImageKey) {
34892                         return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
34893                     } else {
34894                         return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
34895                     }
34896                 }
34897
34898                 return this;
34899             },
34900
34901
34902             updateDetections: function(d) {
34903                 if (!_mlyViewer || _mlyFallback) return;
34904
34905                 var imageKey = d && d.key;
34906                 if (!imageKey) return;
34907
34908                 var detections = _mlyCache.image_detections.forImageKey[imageKey] || [];
34909                 detections.forEach(function(data) {
34910                     var tag = makeTag(data);
34911                     if (tag) {
34912                         var tagComponent = _mlyViewer.getComponent('tag');
34913                         tagComponent.add([tag]);
34914                     }
34915                 });
34916
34917                 function makeTag(data) {
34918                     var valueParts = data.value.split('--');
34919                     if (valueParts.length !== 3) return;
34920
34921                     var text = valueParts[1].replace(/-/g, ' ');
34922                     var tag;
34923
34924                     // Currently only two shapes <Polygon|Point>
34925                     if (data.shape.type === 'Polygon') {
34926                         var polygonGeometry = new Mapillary
34927                             .TagComponent
34928                             .PolygonGeometry(data.shape.coordinates[0]);
34929
34930                         tag = new Mapillary.TagComponent.OutlineTag(
34931                             data.key,
34932                             polygonGeometry,
34933                             {
34934                                 text: text,
34935                                 textColor: 0xffff00,
34936                                 lineColor: 0xffff00,
34937                                 lineWidth: 2,
34938                                 fillColor: 0xffff00,
34939                                 fillOpacity: 0.3,
34940                             }
34941                         );
34942
34943                     } else if (data.shape.type === 'Point') {
34944                         var pointGeometry = new Mapillary
34945                             .TagComponent
34946                             .PointGeometry(data.shape.coordinates[0]);
34947
34948                         tag = new Mapillary.TagComponent.SpotTag(
34949                             data.key,
34950                             pointGeometry,
34951                             {
34952                                 text: text,
34953                                 color: 0xffff00,
34954                                 textColor: 0xffff00
34955                             }
34956                         );
34957                     }
34958
34959                     return tag;
34960                 }
34961             },
34962
34963
34964             cache: function() {
34965                 return _mlyCache;
34966             }
34967
34968         };
34969
34970         function validationIssue(attrs) {
34971             this.type = attrs.type;                // required - name of rule that created the issue (e.g. 'missing_tag')
34972             this.subtype = attrs.subtype;          // optional - category of the issue within the type (e.g. 'relation_type' under 'missing_tag')
34973             this.severity = attrs.severity;        // required - 'warning' or 'error'
34974             this.message = attrs.message;          // required - function returning localized string
34975             this.reference = attrs.reference;      // optional - function(selection) to render reference information
34976             this.entityIds = attrs.entityIds;      // optional - array of IDs of entities involved in the issue
34977             this.loc = attrs.loc;                  // optional - [lon, lat] to zoom in on to see the issue
34978             this.data = attrs.data;                // optional - object containing extra data for the fixes
34979             this.dynamicFixes = attrs.dynamicFixes;// optional - function(context) returning fixes
34980             this.hash = attrs.hash;                // optional - string to further differentiate the issue
34981
34982             this.id = generateID.apply(this);      // generated - see below
34983             this.autoFix = null;                   // generated - if autofix exists, will be set below
34984
34985             // A unique, deterministic string hash.
34986             // Issues with identical id values are considered identical.
34987             function generateID() {
34988                 var parts = [this.type];
34989
34990                 if (this.hash) {   // subclasses can pass in their own differentiator
34991                     parts.push(this.hash);
34992                 }
34993
34994                 if (this.subtype) {
34995                     parts.push(this.subtype);
34996                 }
34997
34998                 // include the entities this issue is for
34999                 // (sort them so the id is deterministic)
35000                 if (this.entityIds) {
35001                     var entityKeys = this.entityIds.slice().sort();
35002                     parts.push.apply(parts, entityKeys);
35003                 }
35004
35005                 return parts.join(':');
35006             }
35007
35008             this.extent = function(resolver) {
35009                 if (this.loc) {
35010                     return geoExtent(this.loc);
35011                 }
35012                 if (this.entityIds && this.entityIds.length) {
35013                     return this.entityIds.reduce(function(extent, entityId) {
35014                         return extent.extend(resolver.entity(entityId).extent(resolver));
35015                     }, geoExtent());
35016                 }
35017                 return null;
35018             };
35019
35020             this.fixes = function(context) {
35021                 var fixes = this.dynamicFixes ? this.dynamicFixes(context) : [];
35022                 var issue = this;
35023
35024                 if (issue.severity === 'warning') {
35025                     // allow ignoring any issue that's not an error
35026                     fixes.push(new validationIssueFix({
35027                         title: _t('issues.fix.ignore_issue.title'),
35028                         icon: 'iD-icon-close',
35029                         onClick: function() {
35030                             context.validator().ignoreIssue(this.issue.id);
35031                         }
35032                     }));
35033                 }
35034
35035                 fixes.forEach(function(fix) {
35036                     fix.id = fix.title;
35037                     // add a reference to the issue for use in actions
35038                     fix.issue = issue;
35039                     if (fix.autoArgs) {
35040                         issue.autoFix = fix;
35041                     }
35042                 });
35043                 return fixes;
35044             };
35045
35046         }
35047
35048
35049         function validationIssueFix(attrs) {
35050             this.title = attrs.title;                   // Required
35051             this.onClick = attrs.onClick;               // Optional - the function to run to apply the fix
35052             this.disabledReason = attrs.disabledReason; // Optional - a string explaining why the fix is unavailable, if any
35053             this.icon = attrs.icon;                     // Optional - shows 'iD-icon-wrench' if not set
35054             this.entityIds = attrs.entityIds || [];     // Optional - used for hover-higlighting.
35055             this.autoArgs = attrs.autoArgs;             // Optional - pass [actions, annotation] arglist if this fix can automatically run
35056
35057             this.issue = null;    // Generated link - added by validationIssue
35058         }
35059
35060         var buildRuleChecks = function() {
35061             return {
35062                 equals: function (equals) {
35063                     return function(tags) {
35064                         return Object.keys(equals).every(function(k) {
35065                             return equals[k] === tags[k];
35066                         });
35067                     };
35068                 },
35069                 notEquals: function (notEquals) {
35070                     return function(tags) {
35071                         return Object.keys(notEquals).some(function(k) {
35072                             return notEquals[k] !== tags[k];
35073                         });
35074                     };
35075                 },
35076                 absence: function(absence) {
35077                     return function(tags) {
35078                         return Object.keys(tags).indexOf(absence) === -1;
35079                     };
35080                 },
35081                 presence: function(presence) {
35082                     return function(tags) {
35083                         return Object.keys(tags).indexOf(presence) > -1;
35084                     };
35085                 },
35086                 greaterThan: function(greaterThan) {
35087                     var key = Object.keys(greaterThan)[0];
35088                     var value = greaterThan[key];
35089
35090                     return function(tags) {
35091                         return tags[key] > value;
35092                     };
35093                 },
35094                 greaterThanEqual: function(greaterThanEqual) {
35095                     var key = Object.keys(greaterThanEqual)[0];
35096                     var value = greaterThanEqual[key];
35097
35098                     return function(tags) {
35099                         return tags[key] >= value;
35100                     };
35101                 },
35102                 lessThan: function(lessThan) {
35103                     var key = Object.keys(lessThan)[0];
35104                     var value = lessThan[key];
35105
35106                     return function(tags) {
35107                         return tags[key] < value;
35108                     };
35109                 },
35110                 lessThanEqual: function(lessThanEqual) {
35111                     var key = Object.keys(lessThanEqual)[0];
35112                     var value = lessThanEqual[key];
35113
35114                     return function(tags) {
35115                         return tags[key] <= value;
35116                     };
35117                 },
35118                 positiveRegex: function(positiveRegex) {
35119                     var tagKey = Object.keys(positiveRegex)[0];
35120                     var expression = positiveRegex[tagKey].join('|');
35121                     var regex = new RegExp(expression);
35122
35123                     return function(tags) {
35124                         return regex.test(tags[tagKey]);
35125                     };
35126                 },
35127                 negativeRegex: function(negativeRegex) {
35128                     var tagKey = Object.keys(negativeRegex)[0];
35129                     var expression = negativeRegex[tagKey].join('|');
35130                     var regex = new RegExp(expression);
35131
35132                     return function(tags) {
35133                         return !regex.test(tags[tagKey]);
35134                     };
35135                 }
35136             };
35137         };
35138
35139         var buildLineKeys = function() {
35140             return {
35141                 highway: {
35142                     rest_area: true,
35143                     services: true
35144                 },
35145                 railway: {
35146                     roundhouse: true,
35147                     station: true,
35148                     traverser: true,
35149                     turntable: true,
35150                     wash: true
35151                 }
35152             };
35153         };
35154
35155         var serviceMapRules = {
35156             init: function() {
35157                 this._ruleChecks  = buildRuleChecks();
35158                 this._validationRules = [];
35159                 this._areaKeys = osmAreaKeys;
35160                 this._lineKeys = buildLineKeys();
35161             },
35162
35163             // list of rules only relevant to tag checks...
35164             filterRuleChecks: function(selector) {
35165                 var _ruleChecks = this._ruleChecks;
35166                 return Object.keys(selector).reduce(function(rules, key) {
35167                     if (['geometry', 'error', 'warning'].indexOf(key) === -1) {
35168                         rules.push(_ruleChecks[key](selector[key]));
35169                     }
35170                     return rules;
35171                 }, []);
35172             },
35173
35174             // builds tagMap from mapcss-parse selector object...
35175             buildTagMap: function(selector) {
35176                 var getRegexValues = function(regexes) {
35177                     return regexes.map(function(regex) {
35178                         return regex.replace(/\$|\^/g, '');
35179                     });
35180                 };
35181
35182                 var tagMap = Object.keys(selector).reduce(function (expectedTags, key) {
35183                     var values;
35184                     var isRegex = /regex/gi.test(key);
35185                     var isEqual = /equals/gi.test(key);
35186
35187                     if (isRegex || isEqual) {
35188                         Object.keys(selector[key]).forEach(function(selectorKey) {
35189                             values = isEqual ? [selector[key][selectorKey]] : getRegexValues(selector[key][selectorKey]);
35190
35191                             if (expectedTags.hasOwnProperty(selectorKey)) {
35192                                 values = values.concat(expectedTags[selectorKey]);
35193                             }
35194
35195                             expectedTags[selectorKey] = values;
35196                         });
35197
35198                     } else if (/(greater|less)Than(Equal)?|presence/g.test(key)) {
35199                         var tagKey = /presence/.test(key) ? selector[key] : Object.keys(selector[key])[0];
35200
35201                         values = [selector[key][tagKey]];
35202
35203                         if (expectedTags.hasOwnProperty(tagKey)) {
35204                             values = values.concat(expectedTags[tagKey]);
35205                         }
35206
35207                         expectedTags[tagKey] = values;
35208                     }
35209
35210                     return expectedTags;
35211                 }, {});
35212
35213                 return tagMap;
35214             },
35215
35216             // inspired by osmWay#isArea()
35217             inferGeometry: function(tagMap) {
35218                 var _lineKeys = this._lineKeys;
35219                 var _areaKeys = this._areaKeys;
35220
35221                 var keyValueDoesNotImplyArea = function(key) {
35222                     return utilArrayIntersection(tagMap[key], Object.keys(_areaKeys[key])).length > 0;
35223                 };
35224                 var keyValueImpliesLine = function(key) {
35225                     return utilArrayIntersection(tagMap[key], Object.keys(_lineKeys[key])).length > 0;
35226                 };
35227
35228                 if (tagMap.hasOwnProperty('area')) {
35229                     if (tagMap.area.indexOf('yes') > -1) {
35230                         return 'area';
35231                     }
35232                     if (tagMap.area.indexOf('no') > -1) {
35233                         return 'line';
35234                     }
35235                 }
35236
35237                 for (var key in tagMap) {
35238                     if (key in _areaKeys && !keyValueDoesNotImplyArea(key)) {
35239                         return 'area';
35240                     }
35241                     if (key in _lineKeys && keyValueImpliesLine(key)) {
35242                         return 'area';
35243                     }
35244                 }
35245
35246                 return 'line';
35247             },
35248
35249             // adds from mapcss-parse selector check...
35250             addRule: function(selector) {
35251                 var rule = {
35252                     // checks relevant to mapcss-selector
35253                     checks: this.filterRuleChecks(selector),
35254                     // true if all conditions for a tag error are true..
35255                     matches: function(entity) {
35256                         return this.checks.every(function(check) {
35257                             return check(entity.tags);
35258                         });
35259                     },
35260                     // borrowed from Way#isArea()
35261                     inferredGeometry: this.inferGeometry(this.buildTagMap(selector), this._areaKeys),
35262                     geometryMatches: function(entity, graph) {
35263                         if (entity.type === 'node' || entity.type === 'relation') {
35264                             return selector.geometry === entity.type;
35265                         } else if (entity.type === 'way') {
35266                             return this.inferredGeometry === entity.geometry(graph);
35267                         }
35268                     },
35269                     // when geometries match and tag matches are present, return a warning...
35270                     findIssues: function (entity, graph, issues) {
35271                         if (this.geometryMatches(entity, graph) && this.matches(entity)) {
35272                             var severity = Object.keys(selector).indexOf('error') > -1
35273                                     ? 'error'
35274                                     : 'warning';
35275                             var message = selector[severity];
35276                             issues.push(new validationIssue({
35277                                 type: 'maprules',
35278                                 severity: severity,
35279                                 message: function() {
35280                                     return message;
35281                                 },
35282                                 entityIds: [entity.id]
35283                             }));
35284                         }
35285                     }
35286                 };
35287                 this._validationRules.push(rule);
35288             },
35289
35290             clearRules: function() { this._validationRules = []; },
35291
35292             // returns validationRules...
35293             validationRules: function() { return this._validationRules; },
35294
35295             // returns ruleChecks
35296             ruleChecks: function() { return this._ruleChecks; }
35297         };
35298
35299         var apibase$1 = 'https://nominatim.openstreetmap.org/';
35300         var _inflight = {};
35301         var _nominatimCache;
35302
35303
35304         var serviceNominatim = {
35305
35306             init: function() {
35307                 _inflight = {};
35308                 _nominatimCache = new RBush();
35309             },
35310
35311             reset: function() {
35312                 Object.values(_inflight).forEach(function(controller) { controller.abort(); });
35313                 _inflight = {};
35314                 _nominatimCache = new RBush();
35315             },
35316
35317
35318             countryCode: function (location, callback) {
35319                 this.reverse(location, function(err, result) {
35320                     if (err) {
35321                         return callback(err);
35322                     } else if (result.address) {
35323                         return callback(null, result.address.country_code);
35324                     } else {
35325                         return callback('Unable to geocode', null);
35326                     }
35327                 });
35328             },
35329
35330
35331             reverse: function (loc, callback) {
35332                 var cached = _nominatimCache.search(
35333                     { minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1] }
35334                 );
35335
35336                 if (cached.length > 0) {
35337                     if (callback) callback(null, cached[0].data);
35338                     return;
35339                 }
35340
35341                 var params = { zoom: 13, format: 'json', addressdetails: 1, lat: loc[1], lon: loc[0] };
35342                 var url = apibase$1 + 'reverse?' + utilQsString(params);
35343
35344                 if (_inflight[url]) return;
35345                 var controller = new AbortController();
35346                 _inflight[url] = controller;
35347
35348                 d3_json(url, { signal: controller.signal })
35349                     .then(function(result) {
35350                         delete _inflight[url];
35351                         if (result && result.error) {
35352                             throw new Error(result.error);
35353                         }
35354                         var extent = geoExtent(loc).padByMeters(200);
35355                         _nominatimCache.insert(Object.assign(extent.bbox(), {data: result}));
35356                         if (callback) callback(null, result);
35357                     })
35358                     .catch(function(err) {
35359                         delete _inflight[url];
35360                         if (err.name === 'AbortError') return;
35361                         if (callback) callback(err.message);
35362                     });
35363             },
35364
35365
35366             search: function (val, callback) {
35367                 var searchVal = encodeURIComponent(val);
35368                 var url = apibase$1 + 'search/' + searchVal + '?limit=10&format=json';
35369
35370                 if (_inflight[url]) return;
35371                 var controller = new AbortController();
35372                 _inflight[url] = controller;
35373
35374                 d3_json(url, { signal: controller.signal })
35375                     .then(function(result) {
35376                         delete _inflight[url];
35377                         if (result && result.error) {
35378                             throw new Error(result.error);
35379                         }
35380                         if (callback) callback(null, result);
35381                     })
35382                     .catch(function(err) {
35383                         delete _inflight[url];
35384                         if (err.name === 'AbortError') return;
35385                         if (callback) callback(err.message);
35386                     });
35387             }
35388
35389         };
35390
35391         var apibase$2 = 'https://openstreetcam.org';
35392         var maxResults$1 = 1000;
35393         var tileZoom$1 = 14;
35394         var tiler$4 = utilTiler().zoomExtent([tileZoom$1, tileZoom$1]).skipNullIsland(true);
35395         var dispatch$5 = dispatch('loadedImages');
35396         var imgZoom = d3_zoom()
35397             .extent([[0, 0], [320, 240]])
35398             .translateExtent([[0, 0], [320, 240]])
35399             .scaleExtent([1, 15]);
35400         var _oscCache;
35401         var _oscSelectedImage;
35402
35403
35404         function abortRequest$4(controller) {
35405             controller.abort();
35406         }
35407
35408
35409         function maxPageAtZoom$1(z) {
35410             if (z < 15)   return 2;
35411             if (z === 15) return 5;
35412             if (z === 16) return 10;
35413             if (z === 17) return 20;
35414             if (z === 18) return 40;
35415             if (z > 18)   return 80;
35416         }
35417
35418
35419         function loadTiles$1(which, url, projection) {
35420             var currZoom = Math.floor(geoScaleToZoom(projection.scale()));
35421             var tiles = tiler$4.getTiles(projection);
35422
35423             // abort inflight requests that are no longer needed
35424             var cache = _oscCache[which];
35425             Object.keys(cache.inflight).forEach(function(k) {
35426                 var wanted = tiles.find(function(tile) { return k.indexOf(tile.id + ',') === 0; });
35427                 if (!wanted) {
35428                     abortRequest$4(cache.inflight[k]);
35429                     delete cache.inflight[k];
35430                 }
35431             });
35432
35433             tiles.forEach(function(tile) {
35434                 loadNextTilePage$1(which, currZoom, url, tile);
35435             });
35436         }
35437
35438
35439         function loadNextTilePage$1(which, currZoom, url, tile) {
35440             var cache = _oscCache[which];
35441             var bbox = tile.extent.bbox();
35442             var maxPages = maxPageAtZoom$1(currZoom);
35443             var nextPage = cache.nextPage[tile.id] || 1;
35444             var params = utilQsString({
35445                 ipp: maxResults$1,
35446                 page: nextPage,
35447                 // client_id: clientId,
35448                 bbTopLeft: [bbox.maxY, bbox.minX].join(','),
35449                 bbBottomRight: [bbox.minY, bbox.maxX].join(',')
35450             }, true);
35451
35452             if (nextPage > maxPages) return;
35453
35454             var id = tile.id + ',' + String(nextPage);
35455             if (cache.loaded[id] || cache.inflight[id]) return;
35456
35457             var controller = new AbortController();
35458             cache.inflight[id] = controller;
35459
35460             var options = {
35461                 method: 'POST',
35462                 signal: controller.signal,
35463                 body: params,
35464                 headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
35465             };
35466
35467             d3_json(url, options)
35468                 .then(function(data) {
35469                     cache.loaded[id] = true;
35470                     delete cache.inflight[id];
35471                     if (!data || !data.currentPageItems || !data.currentPageItems.length) {
35472                         throw new Error('No Data');
35473                     }
35474
35475                     var features = data.currentPageItems.map(function(item) {
35476                         var loc = [+item.lng, +item.lat];
35477                         var d;
35478
35479                         if (which === 'images') {
35480                             d = {
35481                                 loc: loc,
35482                                 key: item.id,
35483                                 ca: +item.heading,
35484                                 captured_at: (item.shot_date || item.date_added),
35485                                 captured_by: item.username,
35486                                 imagePath: item.lth_name,
35487                                 sequence_id: item.sequence_id,
35488                                 sequence_index: +item.sequence_index
35489                             };
35490
35491                             // cache sequence info
35492                             var seq = _oscCache.sequences[d.sequence_id];
35493                             if (!seq) {
35494                                 seq = { rotation: 0, images: [] };
35495                                 _oscCache.sequences[d.sequence_id] = seq;
35496                             }
35497                             seq.images[d.sequence_index] = d;
35498                         }
35499
35500                         return {
35501                             minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1], data: d
35502                         };
35503                     });
35504
35505                     cache.rtree.load(features);
35506
35507                     if (data.currentPageItems.length === maxResults$1) {  // more pages to load
35508                         cache.nextPage[tile.id] = nextPage + 1;
35509                         loadNextTilePage$1(which, currZoom, url, tile);
35510                     } else {
35511                         cache.nextPage[tile.id] = Infinity;     // no more pages to load
35512                     }
35513
35514                     if (which === 'images') {
35515                         dispatch$5.call('loadedImages');
35516                     }
35517                 })
35518                 .catch(function() {
35519                     cache.loaded[id] = true;
35520                     delete cache.inflight[id];
35521                 });
35522         }
35523
35524
35525         // partition viewport into higher zoom tiles
35526         function partitionViewport$1(projection) {
35527             var z = geoScaleToZoom(projection.scale());
35528             var z2 = (Math.ceil(z * 2) / 2) + 2.5;   // round to next 0.5 and add 2.5
35529             var tiler = utilTiler().zoomExtent([z2, z2]);
35530
35531             return tiler.getTiles(projection)
35532                 .map(function(tile) { return tile.extent; });
35533         }
35534
35535
35536         // no more than `limit` results per partition.
35537         function searchLimited$1(limit, projection, rtree) {
35538             limit = limit || 5;
35539
35540             return partitionViewport$1(projection)
35541                 .reduce(function(result, extent) {
35542                     var found = rtree.search(extent.bbox())
35543                         .slice(0, limit)
35544                         .map(function(d) { return d.data; });
35545
35546                     return (found.length ? result.concat(found) : result);
35547                 }, []);
35548         }
35549
35550
35551         var serviceOpenstreetcam = {
35552
35553             init: function() {
35554                 if (!_oscCache) {
35555                     this.reset();
35556                 }
35557
35558                 this.event = utilRebind(this, dispatch$5, 'on');
35559             },
35560
35561             reset: function() {
35562                 if (_oscCache) {
35563                     Object.values(_oscCache.images.inflight).forEach(abortRequest$4);
35564                 }
35565
35566                 _oscCache = {
35567                     images: { inflight: {}, loaded: {}, nextPage: {}, rtree: new RBush() },
35568                     sequences: {}
35569                 };
35570
35571                 _oscSelectedImage = null;
35572             },
35573
35574
35575             images: function(projection) {
35576                 var limit = 5;
35577                 return searchLimited$1(limit, projection, _oscCache.images.rtree);
35578             },
35579
35580
35581             sequences: function(projection) {
35582                 var viewport = projection.clipExtent();
35583                 var min = [viewport[0][0], viewport[1][1]];
35584                 var max = [viewport[1][0], viewport[0][1]];
35585                 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
35586                 var sequenceKeys = {};
35587
35588                 // all sequences for images in viewport
35589                 _oscCache.images.rtree.search(bbox)
35590                     .forEach(function(d) { sequenceKeys[d.data.sequence_id] = true; });
35591
35592                 // make linestrings from those sequences
35593                 var lineStrings = [];
35594                 Object.keys(sequenceKeys)
35595                     .forEach(function(sequenceKey) {
35596                         var seq = _oscCache.sequences[sequenceKey];
35597                         var images = seq && seq.images;
35598                         if (images) {
35599                             lineStrings.push({
35600                                 type: 'LineString',
35601                                 coordinates: images.map(function (d) { return d.loc; }).filter(Boolean),
35602                                 properties: { key: sequenceKey }
35603                             });
35604                         }
35605                     });
35606                 return lineStrings;
35607             },
35608
35609
35610             loadImages: function(projection) {
35611                 var url = apibase$2 + '/1.0/list/nearby-photos/';
35612                 loadTiles$1('images', url, projection);
35613             },
35614
35615
35616             loadViewer: function(context) {
35617                 var that = this;
35618
35619                 // add osc-wrapper
35620                 var wrap = context.container().select('.photoviewer').selectAll('.osc-wrapper')
35621                     .data([0]);
35622
35623                 var wrapEnter = wrap.enter()
35624                     .append('div')
35625                     .attr('class', 'photo-wrapper osc-wrapper')
35626                     .classed('hide', true)
35627                     .call(imgZoom.on('zoom', zoomPan))
35628                     .on('dblclick.zoom', null);
35629
35630                 wrapEnter
35631                     .append('div')
35632                     .attr('class', 'photo-attribution fillD');
35633
35634                 var controlsEnter = wrapEnter
35635                     .append('div')
35636                     .attr('class', 'photo-controls-wrap')
35637                     .append('div')
35638                     .attr('class', 'photo-controls');
35639
35640                 controlsEnter
35641                     .append('button')
35642                     .on('click.back', step(-1))
35643                     .text('◄');
35644
35645                 controlsEnter
35646                     .append('button')
35647                     .on('click.rotate-ccw', rotate(-90))
35648                     .text('⤿');
35649
35650                 controlsEnter
35651                     .append('button')
35652                     .on('click.rotate-cw', rotate(90))
35653                     .text('⤾');
35654
35655                 controlsEnter
35656                     .append('button')
35657                     .on('click.forward', step(1))
35658                     .text('►');
35659
35660                 wrapEnter
35661                     .append('div')
35662                     .attr('class', 'osc-image-wrap');
35663
35664
35665                 // Register viewer resize handler
35666                 context.ui().photoviewer.on('resize.openstreetcam', function(dimensions) {
35667                     imgZoom = d3_zoom()
35668                         .extent([[0, 0], dimensions])
35669                         .translateExtent([[0, 0], dimensions])
35670                         .scaleExtent([1, 15])
35671                         .on('zoom', zoomPan);
35672                 });
35673
35674
35675                 function zoomPan() {
35676                     var t = event.transform;
35677                     context.container().select('.photoviewer .osc-image-wrap')
35678                         .call(utilSetTransform, t.x, t.y, t.k);
35679                 }
35680
35681
35682                 function rotate(deg) {
35683                     return function() {
35684                         if (!_oscSelectedImage) return;
35685                         var sequenceKey = _oscSelectedImage.sequence_id;
35686                         var sequence = _oscCache.sequences[sequenceKey];
35687                         if (!sequence) return;
35688
35689                         var r = sequence.rotation || 0;
35690                         r += deg;
35691
35692                         if (r > 180) r -= 360;
35693                         if (r < -180) r += 360;
35694                         sequence.rotation = r;
35695
35696                         var wrap = context.container().select('.photoviewer .osc-wrapper');
35697
35698                         wrap
35699                             .transition()
35700                             .duration(100)
35701                             .call(imgZoom.transform, identity$2);
35702
35703                         wrap.selectAll('.osc-image')
35704                             .transition()
35705                             .duration(100)
35706                             .style('transform', 'rotate(' + r + 'deg)');
35707                     };
35708                 }
35709
35710                 function step(stepBy) {
35711                     return function() {
35712                         if (!_oscSelectedImage) return;
35713                         var sequenceKey = _oscSelectedImage.sequence_id;
35714                         var sequence = _oscCache.sequences[sequenceKey];
35715                         if (!sequence) return;
35716
35717                         var nextIndex = _oscSelectedImage.sequence_index + stepBy;
35718                         var nextImage = sequence.images[nextIndex];
35719                         if (!nextImage) return;
35720
35721                         context.map().centerEase(nextImage.loc);
35722
35723                         that
35724                             .selectImage(context, nextImage)
35725                             .updateViewer(context, nextImage);
35726                     };
35727                 }
35728             },
35729
35730
35731             showViewer: function(context) {
35732                 var viewer = context.container().select('.photoviewer')
35733                     .classed('hide', false);
35734
35735                 var isHidden = viewer.selectAll('.photo-wrapper.osc-wrapper.hide').size();
35736
35737                 if (isHidden) {
35738                     viewer
35739                         .selectAll('.photo-wrapper:not(.osc-wrapper)')
35740                         .classed('hide', true);
35741
35742                     viewer
35743                         .selectAll('.photo-wrapper.osc-wrapper')
35744                         .classed('hide', false);
35745                 }
35746
35747                 return this;
35748             },
35749
35750
35751             hideViewer: function(context) {
35752                 _oscSelectedImage = null;
35753
35754                 var viewer = context.container().select('.photoviewer');
35755                 if (!viewer.empty()) viewer.datum(null);
35756
35757                 viewer
35758                     .classed('hide', true)
35759                     .selectAll('.photo-wrapper')
35760                     .classed('hide', true);
35761
35762                 context.container().selectAll('.viewfield-group, .sequence, .icon-sign')
35763                     .classed('currentView', false);
35764
35765                 return this.setStyles(context, null, true);
35766             },
35767
35768
35769             updateViewer: function(context, d) {
35770                 var wrap = context.container().select('.photoviewer .osc-wrapper');
35771                 var imageWrap = wrap.selectAll('.osc-image-wrap');
35772                 var attribution = wrap.selectAll('.photo-attribution').html('');
35773
35774                 wrap
35775                     .transition()
35776                     .duration(100)
35777                     .call(imgZoom.transform, identity$2);
35778
35779                 imageWrap
35780                     .selectAll('.osc-image')
35781                     .remove();
35782
35783                 if (d) {
35784                     var sequence = _oscCache.sequences[d.sequence_id];
35785                     var r = (sequence && sequence.rotation) || 0;
35786
35787                     imageWrap
35788                         .append('img')
35789                         .attr('class', 'osc-image')
35790                         .attr('src', apibase$2 + '/' + d.imagePath)
35791                         .style('transform', 'rotate(' + r + 'deg)');
35792
35793                     if (d.captured_by) {
35794                         attribution
35795                             .append('a')
35796                             .attr('class', 'captured_by')
35797                             .attr('target', '_blank')
35798                             .attr('href', 'https://openstreetcam.org/user/' + encodeURIComponent(d.captured_by))
35799                             .text('@' + d.captured_by);
35800
35801                         attribution
35802                             .append('span')
35803                             .text('|');
35804                     }
35805
35806                     if (d.captured_at) {
35807                         attribution
35808                             .append('span')
35809                             .attr('class', 'captured_at')
35810                             .text(localeDateString(d.captured_at));
35811
35812                         attribution
35813                             .append('span')
35814                             .text('|');
35815                     }
35816
35817                     attribution
35818                         .append('a')
35819                         .attr('class', 'image-link')
35820                         .attr('target', '_blank')
35821                         .attr('href', 'https://openstreetcam.org/details/' + d.sequence_id + '/' + d.sequence_index)
35822                         .text('openstreetcam.org');
35823                 }
35824
35825                 return this;
35826
35827
35828                 function localeDateString(s) {
35829                     if (!s) return null;
35830                     var options = { day: 'numeric', month: 'short', year: 'numeric' };
35831                     var d = new Date(s);
35832                     if (isNaN(d.getTime())) return null;
35833                     return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
35834                 }
35835             },
35836
35837
35838             selectImage: function(context, d) {
35839                 _oscSelectedImage = d;
35840                 var viewer = context.container().select('.photoviewer');
35841                 if (!viewer.empty()) viewer.datum(d);
35842
35843                 this.setStyles(context, null, true);
35844
35845                 context.container().selectAll('.icon-sign')
35846                     .classed('currentView', false);
35847
35848                 return this;
35849             },
35850
35851
35852             getSelectedImage: function() {
35853                 return _oscSelectedImage;
35854             },
35855
35856
35857             getSequenceKeyForImage: function(d) {
35858                 return d && d.sequence_id;
35859             },
35860
35861
35862             // Updates the currently highlighted sequence and selected bubble.
35863             // Reset is only necessary when interacting with the viewport because
35864             // this implicitly changes the currently selected bubble/sequence
35865             setStyles: function(context, hovered, reset) {
35866                 if (reset) {  // reset all layers
35867                     context.container().selectAll('.viewfield-group')
35868                         .classed('highlighted', false)
35869                         .classed('hovered', false)
35870                         .classed('currentView', false);
35871
35872                     context.container().selectAll('.sequence')
35873                         .classed('highlighted', false)
35874                         .classed('currentView', false);
35875                 }
35876
35877                 var hoveredImageKey = hovered && hovered.key;
35878                 var hoveredSequenceKey = this.getSequenceKeyForImage(hovered);
35879                 var hoveredSequence = hoveredSequenceKey && _oscCache.sequences[hoveredSequenceKey];
35880                 var hoveredImageKeys = (hoveredSequence && hoveredSequence.images.map(function (d) { return d.key; })) || [];
35881
35882                 var viewer = context.container().select('.photoviewer');
35883                 var selected = viewer.empty() ? undefined : viewer.datum();
35884                 var selectedImageKey = selected && selected.key;
35885                 var selectedSequenceKey = this.getSequenceKeyForImage(selected);
35886                 var selectedSequence = selectedSequenceKey && _oscCache.sequences[selectedSequenceKey];
35887                 var selectedImageKeys = (selectedSequence && selectedSequence.images.map(function (d) { return d.key; })) || [];
35888
35889                 // highlight sibling viewfields on either the selected or the hovered sequences
35890                 var highlightedImageKeys = utilArrayUnion(hoveredImageKeys, selectedImageKeys);
35891
35892                 context.container().selectAll('.layer-openstreetcam .viewfield-group')
35893                     .classed('highlighted', function(d) { return highlightedImageKeys.indexOf(d.key) !== -1; })
35894                     .classed('hovered', function(d) { return d.key === hoveredImageKey; })
35895                     .classed('currentView', function(d) { return d.key === selectedImageKey; });
35896
35897                 context.container().selectAll('.layer-openstreetcam .sequence')
35898                     .classed('highlighted', function(d) { return d.properties.key === hoveredSequenceKey; })
35899                     .classed('currentView', function(d) { return d.properties.key === selectedSequenceKey; });
35900
35901                 // update viewfields if needed
35902                 context.container().selectAll('.viewfield-group .viewfield')
35903                     .attr('d', viewfieldPath);
35904
35905                 function viewfieldPath() {
35906                     var d = this.parentNode.__data__;
35907                     if (d.pano && d.key !== selectedImageKey) {
35908                         return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
35909                     } else {
35910                         return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
35911                     }
35912                 }
35913
35914                 return this;
35915             },
35916
35917
35918             cache: function() {
35919                 return _oscCache;
35920             }
35921
35922         };
35923
35924         /**
35925          * Checks if `value` is the
35926          * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
35927          * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
35928          *
35929          * @static
35930          * @memberOf _
35931          * @since 0.1.0
35932          * @category Lang
35933          * @param {*} value The value to check.
35934          * @returns {boolean} Returns `true` if `value` is an object, else `false`.
35935          * @example
35936          *
35937          * _.isObject({});
35938          * // => true
35939          *
35940          * _.isObject([1, 2, 3]);
35941          * // => true
35942          *
35943          * _.isObject(_.noop);
35944          * // => true
35945          *
35946          * _.isObject(null);
35947          * // => false
35948          */
35949         function isObject$1(value) {
35950           var type = typeof value;
35951           return value != null && (type == 'object' || type == 'function');
35952         }
35953
35954         /** Detect free variable `global` from Node.js. */
35955         var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;
35956
35957         /** Detect free variable `self`. */
35958         var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
35959
35960         /** Used as a reference to the global object. */
35961         var root$2 = freeGlobal || freeSelf || Function('return this')();
35962
35963         /**
35964          * Gets the timestamp of the number of milliseconds that have elapsed since
35965          * the Unix epoch (1 January 1970 00:00:00 UTC).
35966          *
35967          * @static
35968          * @memberOf _
35969          * @since 2.4.0
35970          * @category Date
35971          * @returns {number} Returns the timestamp.
35972          * @example
35973          *
35974          * _.defer(function(stamp) {
35975          *   console.log(_.now() - stamp);
35976          * }, _.now());
35977          * // => Logs the number of milliseconds it took for the deferred invocation.
35978          */
35979         var now$1 = function() {
35980           return root$2.Date.now();
35981         };
35982
35983         /** Built-in value references. */
35984         var Symbol$1 = root$2.Symbol;
35985
35986         /** Used for built-in method references. */
35987         var objectProto = Object.prototype;
35988
35989         /** Used to check objects for own properties. */
35990         var hasOwnProperty$2 = objectProto.hasOwnProperty;
35991
35992         /**
35993          * Used to resolve the
35994          * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
35995          * of values.
35996          */
35997         var nativeObjectToString = objectProto.toString;
35998
35999         /** Built-in value references. */
36000         var symToStringTag = Symbol$1 ? Symbol$1.toStringTag : undefined;
36001
36002         /**
36003          * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
36004          *
36005          * @private
36006          * @param {*} value The value to query.
36007          * @returns {string} Returns the raw `toStringTag`.
36008          */
36009         function getRawTag(value) {
36010           var isOwn = hasOwnProperty$2.call(value, symToStringTag),
36011               tag = value[symToStringTag];
36012
36013           try {
36014             value[symToStringTag] = undefined;
36015             var unmasked = true;
36016           } catch (e) {}
36017
36018           var result = nativeObjectToString.call(value);
36019           if (unmasked) {
36020             if (isOwn) {
36021               value[symToStringTag] = tag;
36022             } else {
36023               delete value[symToStringTag];
36024             }
36025           }
36026           return result;
36027         }
36028
36029         /** Used for built-in method references. */
36030         var objectProto$1 = Object.prototype;
36031
36032         /**
36033          * Used to resolve the
36034          * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
36035          * of values.
36036          */
36037         var nativeObjectToString$1 = objectProto$1.toString;
36038
36039         /**
36040          * Converts `value` to a string using `Object.prototype.toString`.
36041          *
36042          * @private
36043          * @param {*} value The value to convert.
36044          * @returns {string} Returns the converted string.
36045          */
36046         function objectToString$2(value) {
36047           return nativeObjectToString$1.call(value);
36048         }
36049
36050         /** `Object#toString` result references. */
36051         var nullTag = '[object Null]',
36052             undefinedTag = '[object Undefined]';
36053
36054         /** Built-in value references. */
36055         var symToStringTag$1 = Symbol$1 ? Symbol$1.toStringTag : undefined;
36056
36057         /**
36058          * The base implementation of `getTag` without fallbacks for buggy environments.
36059          *
36060          * @private
36061          * @param {*} value The value to query.
36062          * @returns {string} Returns the `toStringTag`.
36063          */
36064         function baseGetTag(value) {
36065           if (value == null) {
36066             return value === undefined ? undefinedTag : nullTag;
36067           }
36068           return (symToStringTag$1 && symToStringTag$1 in Object(value))
36069             ? getRawTag(value)
36070             : objectToString$2(value);
36071         }
36072
36073         /**
36074          * Checks if `value` is object-like. A value is object-like if it's not `null`
36075          * and has a `typeof` result of "object".
36076          *
36077          * @static
36078          * @memberOf _
36079          * @since 4.0.0
36080          * @category Lang
36081          * @param {*} value The value to check.
36082          * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
36083          * @example
36084          *
36085          * _.isObjectLike({});
36086          * // => true
36087          *
36088          * _.isObjectLike([1, 2, 3]);
36089          * // => true
36090          *
36091          * _.isObjectLike(_.noop);
36092          * // => false
36093          *
36094          * _.isObjectLike(null);
36095          * // => false
36096          */
36097         function isObjectLike(value) {
36098           return value != null && typeof value == 'object';
36099         }
36100
36101         /** `Object#toString` result references. */
36102         var symbolTag = '[object Symbol]';
36103
36104         /**
36105          * Checks if `value` is classified as a `Symbol` primitive or object.
36106          *
36107          * @static
36108          * @memberOf _
36109          * @since 4.0.0
36110          * @category Lang
36111          * @param {*} value The value to check.
36112          * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
36113          * @example
36114          *
36115          * _.isSymbol(Symbol.iterator);
36116          * // => true
36117          *
36118          * _.isSymbol('abc');
36119          * // => false
36120          */
36121         function isSymbol$4(value) {
36122           return typeof value == 'symbol' ||
36123             (isObjectLike(value) && baseGetTag(value) == symbolTag);
36124         }
36125
36126         /** Used as references for various `Number` constants. */
36127         var NAN = 0 / 0;
36128
36129         /** Used to match leading and trailing whitespace. */
36130         var reTrim = /^\s+|\s+$/g;
36131
36132         /** Used to detect bad signed hexadecimal string values. */
36133         var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
36134
36135         /** Used to detect binary string values. */
36136         var reIsBinary = /^0b[01]+$/i;
36137
36138         /** Used to detect octal string values. */
36139         var reIsOctal = /^0o[0-7]+$/i;
36140
36141         /** Built-in method references without a dependency on `root`. */
36142         var freeParseInt = parseInt;
36143
36144         /**
36145          * Converts `value` to a number.
36146          *
36147          * @static
36148          * @memberOf _
36149          * @since 4.0.0
36150          * @category Lang
36151          * @param {*} value The value to process.
36152          * @returns {number} Returns the number.
36153          * @example
36154          *
36155          * _.toNumber(3.2);
36156          * // => 3.2
36157          *
36158          * _.toNumber(Number.MIN_VALUE);
36159          * // => 5e-324
36160          *
36161          * _.toNumber(Infinity);
36162          * // => Infinity
36163          *
36164          * _.toNumber('3.2');
36165          * // => 3.2
36166          */
36167         function toNumber(value) {
36168           if (typeof value == 'number') {
36169             return value;
36170           }
36171           if (isSymbol$4(value)) {
36172             return NAN;
36173           }
36174           if (isObject$1(value)) {
36175             var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
36176             value = isObject$1(other) ? (other + '') : other;
36177           }
36178           if (typeof value != 'string') {
36179             return value === 0 ? value : +value;
36180           }
36181           value = value.replace(reTrim, '');
36182           var isBinary = reIsBinary.test(value);
36183           return (isBinary || reIsOctal.test(value))
36184             ? freeParseInt(value.slice(2), isBinary ? 2 : 8)
36185             : (reIsBadHex.test(value) ? NAN : +value);
36186         }
36187
36188         /** Error message constants. */
36189         var FUNC_ERROR_TEXT = 'Expected a function';
36190
36191         /* Built-in method references for those with the same name as other `lodash` methods. */
36192         var nativeMax = Math.max,
36193             nativeMin = Math.min;
36194
36195         /**
36196          * Creates a debounced function that delays invoking `func` until after `wait`
36197          * milliseconds have elapsed since the last time the debounced function was
36198          * invoked. The debounced function comes with a `cancel` method to cancel
36199          * delayed `func` invocations and a `flush` method to immediately invoke them.
36200          * Provide `options` to indicate whether `func` should be invoked on the
36201          * leading and/or trailing edge of the `wait` timeout. The `func` is invoked
36202          * with the last arguments provided to the debounced function. Subsequent
36203          * calls to the debounced function return the result of the last `func`
36204          * invocation.
36205          *
36206          * **Note:** If `leading` and `trailing` options are `true`, `func` is
36207          * invoked on the trailing edge of the timeout only if the debounced function
36208          * is invoked more than once during the `wait` timeout.
36209          *
36210          * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
36211          * until to the next tick, similar to `setTimeout` with a timeout of `0`.
36212          *
36213          * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
36214          * for details over the differences between `_.debounce` and `_.throttle`.
36215          *
36216          * @static
36217          * @memberOf _
36218          * @since 0.1.0
36219          * @category Function
36220          * @param {Function} func The function to debounce.
36221          * @param {number} [wait=0] The number of milliseconds to delay.
36222          * @param {Object} [options={}] The options object.
36223          * @param {boolean} [options.leading=false]
36224          *  Specify invoking on the leading edge of the timeout.
36225          * @param {number} [options.maxWait]
36226          *  The maximum time `func` is allowed to be delayed before it's invoked.
36227          * @param {boolean} [options.trailing=true]
36228          *  Specify invoking on the trailing edge of the timeout.
36229          * @returns {Function} Returns the new debounced function.
36230          * @example
36231          *
36232          * // Avoid costly calculations while the window size is in flux.
36233          * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
36234          *
36235          * // Invoke `sendMail` when clicked, debouncing subsequent calls.
36236          * jQuery(element).on('click', _.debounce(sendMail, 300, {
36237          *   'leading': true,
36238          *   'trailing': false
36239          * }));
36240          *
36241          * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
36242          * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
36243          * var source = new EventSource('/stream');
36244          * jQuery(source).on('message', debounced);
36245          *
36246          * // Cancel the trailing debounced invocation.
36247          * jQuery(window).on('popstate', debounced.cancel);
36248          */
36249         function debounce(func, wait, options) {
36250           var lastArgs,
36251               lastThis,
36252               maxWait,
36253               result,
36254               timerId,
36255               lastCallTime,
36256               lastInvokeTime = 0,
36257               leading = false,
36258               maxing = false,
36259               trailing = true;
36260
36261           if (typeof func != 'function') {
36262             throw new TypeError(FUNC_ERROR_TEXT);
36263           }
36264           wait = toNumber(wait) || 0;
36265           if (isObject$1(options)) {
36266             leading = !!options.leading;
36267             maxing = 'maxWait' in options;
36268             maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;
36269             trailing = 'trailing' in options ? !!options.trailing : trailing;
36270           }
36271
36272           function invokeFunc(time) {
36273             var args = lastArgs,
36274                 thisArg = lastThis;
36275
36276             lastArgs = lastThis = undefined;
36277             lastInvokeTime = time;
36278             result = func.apply(thisArg, args);
36279             return result;
36280           }
36281
36282           function leadingEdge(time) {
36283             // Reset any `maxWait` timer.
36284             lastInvokeTime = time;
36285             // Start the timer for the trailing edge.
36286             timerId = setTimeout(timerExpired, wait);
36287             // Invoke the leading edge.
36288             return leading ? invokeFunc(time) : result;
36289           }
36290
36291           function remainingWait(time) {
36292             var timeSinceLastCall = time - lastCallTime,
36293                 timeSinceLastInvoke = time - lastInvokeTime,
36294                 timeWaiting = wait - timeSinceLastCall;
36295
36296             return maxing
36297               ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke)
36298               : timeWaiting;
36299           }
36300
36301           function shouldInvoke(time) {
36302             var timeSinceLastCall = time - lastCallTime,
36303                 timeSinceLastInvoke = time - lastInvokeTime;
36304
36305             // Either this is the first call, activity has stopped and we're at the
36306             // trailing edge, the system time has gone backwards and we're treating
36307             // it as the trailing edge, or we've hit the `maxWait` limit.
36308             return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
36309               (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));
36310           }
36311
36312           function timerExpired() {
36313             var time = now$1();
36314             if (shouldInvoke(time)) {
36315               return trailingEdge(time);
36316             }
36317             // Restart the timer.
36318             timerId = setTimeout(timerExpired, remainingWait(time));
36319           }
36320
36321           function trailingEdge(time) {
36322             timerId = undefined;
36323
36324             // Only invoke if we have `lastArgs` which means `func` has been
36325             // debounced at least once.
36326             if (trailing && lastArgs) {
36327               return invokeFunc(time);
36328             }
36329             lastArgs = lastThis = undefined;
36330             return result;
36331           }
36332
36333           function cancel() {
36334             if (timerId !== undefined) {
36335               clearTimeout(timerId);
36336             }
36337             lastInvokeTime = 0;
36338             lastArgs = lastCallTime = lastThis = timerId = undefined;
36339           }
36340
36341           function flush() {
36342             return timerId === undefined ? result : trailingEdge(now$1());
36343           }
36344
36345           function debounced() {
36346             var time = now$1(),
36347                 isInvoking = shouldInvoke(time);
36348
36349             lastArgs = arguments;
36350             lastThis = this;
36351             lastCallTime = time;
36352
36353             if (isInvoking) {
36354               if (timerId === undefined) {
36355                 return leadingEdge(lastCallTime);
36356               }
36357               if (maxing) {
36358                 // Handle invocations in a tight loop.
36359                 clearTimeout(timerId);
36360                 timerId = setTimeout(timerExpired, wait);
36361                 return invokeFunc(lastCallTime);
36362               }
36363             }
36364             if (timerId === undefined) {
36365               timerId = setTimeout(timerExpired, wait);
36366             }
36367             return result;
36368           }
36369           debounced.cancel = cancel;
36370           debounced.flush = flush;
36371           return debounced;
36372         }
36373
36374         /** Error message constants. */
36375         var FUNC_ERROR_TEXT$1 = 'Expected a function';
36376
36377         /**
36378          * Creates a throttled function that only invokes `func` at most once per
36379          * every `wait` milliseconds. The throttled function comes with a `cancel`
36380          * method to cancel delayed `func` invocations and a `flush` method to
36381          * immediately invoke them. Provide `options` to indicate whether `func`
36382          * should be invoked on the leading and/or trailing edge of the `wait`
36383          * timeout. The `func` is invoked with the last arguments provided to the
36384          * throttled function. Subsequent calls to the throttled function return the
36385          * result of the last `func` invocation.
36386          *
36387          * **Note:** If `leading` and `trailing` options are `true`, `func` is
36388          * invoked on the trailing edge of the timeout only if the throttled function
36389          * is invoked more than once during the `wait` timeout.
36390          *
36391          * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
36392          * until to the next tick, similar to `setTimeout` with a timeout of `0`.
36393          *
36394          * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
36395          * for details over the differences between `_.throttle` and `_.debounce`.
36396          *
36397          * @static
36398          * @memberOf _
36399          * @since 0.1.0
36400          * @category Function
36401          * @param {Function} func The function to throttle.
36402          * @param {number} [wait=0] The number of milliseconds to throttle invocations to.
36403          * @param {Object} [options={}] The options object.
36404          * @param {boolean} [options.leading=true]
36405          *  Specify invoking on the leading edge of the timeout.
36406          * @param {boolean} [options.trailing=true]
36407          *  Specify invoking on the trailing edge of the timeout.
36408          * @returns {Function} Returns the new throttled function.
36409          * @example
36410          *
36411          * // Avoid excessively updating the position while scrolling.
36412          * jQuery(window).on('scroll', _.throttle(updatePosition, 100));
36413          *
36414          * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.
36415          * var throttled = _.throttle(renewToken, 300000, { 'trailing': false });
36416          * jQuery(element).on('click', throttled);
36417          *
36418          * // Cancel the trailing throttled invocation.
36419          * jQuery(window).on('popstate', throttled.cancel);
36420          */
36421         function throttle(func, wait, options) {
36422           var leading = true,
36423               trailing = true;
36424
36425           if (typeof func != 'function') {
36426             throw new TypeError(FUNC_ERROR_TEXT$1);
36427           }
36428           if (isObject$1(options)) {
36429             leading = 'leading' in options ? !!options.leading : leading;
36430             trailing = 'trailing' in options ? !!options.trailing : trailing;
36431           }
36432           return debounce(func, wait, {
36433             'leading': leading,
36434             'maxWait': wait,
36435             'trailing': trailing
36436           });
36437         }
36438
36439         var hashes = createCommonjsModule(function (module, exports) {
36440         /**
36441          * jshashes - https://github.com/h2non/jshashes
36442          * Released under the "New BSD" license
36443          *
36444          * Algorithms specification:
36445          *
36446          * MD5 - http://www.ietf.org/rfc/rfc1321.txt
36447          * RIPEMD-160 - http://homes.esat.kuleuven.be/~bosselae/ripemd160.html
36448          * SHA1   - http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf
36449          * SHA256 - http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf
36450          * SHA512 - http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf
36451          * HMAC - http://www.ietf.org/rfc/rfc2104.txt
36452          */
36453         (function() {
36454           var Hashes;
36455
36456           function utf8Encode(str) {
36457             var x, y, output = '',
36458               i = -1,
36459               l;
36460
36461             if (str && str.length) {
36462               l = str.length;
36463               while ((i += 1) < l) {
36464                 /* Decode utf-16 surrogate pairs */
36465                 x = str.charCodeAt(i);
36466                 y = i + 1 < l ? str.charCodeAt(i + 1) : 0;
36467                 if (0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) {
36468                   x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
36469                   i += 1;
36470                 }
36471                 /* Encode output as utf-8 */
36472                 if (x <= 0x7F) {
36473                   output += String.fromCharCode(x);
36474                 } else if (x <= 0x7FF) {
36475                   output += String.fromCharCode(0xC0 | ((x >>> 6) & 0x1F),
36476                     0x80 | (x & 0x3F));
36477                 } else if (x <= 0xFFFF) {
36478                   output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
36479                     0x80 | ((x >>> 6) & 0x3F),
36480                     0x80 | (x & 0x3F));
36481                 } else if (x <= 0x1FFFFF) {
36482                   output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
36483                     0x80 | ((x >>> 12) & 0x3F),
36484                     0x80 | ((x >>> 6) & 0x3F),
36485                     0x80 | (x & 0x3F));
36486                 }
36487               }
36488             }
36489             return output;
36490           }
36491
36492           function utf8Decode(str) {
36493             var i, ac, c1, c2, c3, arr = [],
36494               l;
36495             i = ac = c1 = c2 = c3 = 0;
36496
36497             if (str && str.length) {
36498               l = str.length;
36499               str += '';
36500
36501               while (i < l) {
36502                 c1 = str.charCodeAt(i);
36503                 ac += 1;
36504                 if (c1 < 128) {
36505                   arr[ac] = String.fromCharCode(c1);
36506                   i += 1;
36507                 } else if (c1 > 191 && c1 < 224) {
36508                   c2 = str.charCodeAt(i + 1);
36509                   arr[ac] = String.fromCharCode(((c1 & 31) << 6) | (c2 & 63));
36510                   i += 2;
36511                 } else {
36512                   c2 = str.charCodeAt(i + 1);
36513                   c3 = str.charCodeAt(i + 2);
36514                   arr[ac] = String.fromCharCode(((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
36515                   i += 3;
36516                 }
36517               }
36518             }
36519             return arr.join('');
36520           }
36521
36522           /**
36523            * Add integers, wrapping at 2^32. This uses 16-bit operations internally
36524            * to work around bugs in some JS interpreters.
36525            */
36526
36527           function safe_add(x, y) {
36528             var lsw = (x & 0xFFFF) + (y & 0xFFFF),
36529               msw = (x >> 16) + (y >> 16) + (lsw >> 16);
36530             return (msw << 16) | (lsw & 0xFFFF);
36531           }
36532
36533           /**
36534            * Bitwise rotate a 32-bit number to the left.
36535            */
36536
36537           function bit_rol(num, cnt) {
36538             return (num << cnt) | (num >>> (32 - cnt));
36539           }
36540
36541           /**
36542            * Convert a raw string to a hex string
36543            */
36544
36545           function rstr2hex(input, hexcase) {
36546             var hex_tab = hexcase ? '0123456789ABCDEF' : '0123456789abcdef',
36547               output = '',
36548               x, i = 0,
36549               l = input.length;
36550             for (; i < l; i += 1) {
36551               x = input.charCodeAt(i);
36552               output += hex_tab.charAt((x >>> 4) & 0x0F) + hex_tab.charAt(x & 0x0F);
36553             }
36554             return output;
36555           }
36556
36557           /**
36558            * Convert an array of big-endian words to a string
36559            */
36560
36561           function binb2rstr(input) {
36562             var i, l = input.length * 32,
36563               output = '';
36564             for (i = 0; i < l; i += 8) {
36565               output += String.fromCharCode((input[i >> 5] >>> (24 - i % 32)) & 0xFF);
36566             }
36567             return output;
36568           }
36569
36570           /**
36571            * Convert an array of little-endian words to a string
36572            */
36573
36574           function binl2rstr(input) {
36575             var i, l = input.length * 32,
36576               output = '';
36577             for (i = 0; i < l; i += 8) {
36578               output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xFF);
36579             }
36580             return output;
36581           }
36582
36583           /**
36584            * Convert a raw string to an array of little-endian words
36585            * Characters >255 have their high-byte silently ignored.
36586            */
36587
36588           function rstr2binl(input) {
36589             var i, l = input.length * 8,
36590               output = Array(input.length >> 2),
36591               lo = output.length;
36592             for (i = 0; i < lo; i += 1) {
36593               output[i] = 0;
36594             }
36595             for (i = 0; i < l; i += 8) {
36596               output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << (i % 32);
36597             }
36598             return output;
36599           }
36600
36601           /**
36602            * Convert a raw string to an array of big-endian words
36603            * Characters >255 have their high-byte silently ignored.
36604            */
36605
36606           function rstr2binb(input) {
36607             var i, l = input.length * 8,
36608               output = Array(input.length >> 2),
36609               lo = output.length;
36610             for (i = 0; i < lo; i += 1) {
36611               output[i] = 0;
36612             }
36613             for (i = 0; i < l; i += 8) {
36614               output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32);
36615             }
36616             return output;
36617           }
36618
36619           /**
36620            * Convert a raw string to an arbitrary string encoding
36621            */
36622
36623           function rstr2any(input, encoding) {
36624             var divisor = encoding.length,
36625               remainders = Array(),
36626               i, q, x, ld, quotient, dividend, output, full_length;
36627
36628             /* Convert to an array of 16-bit big-endian values, forming the dividend */
36629             dividend = Array(Math.ceil(input.length / 2));
36630             ld = dividend.length;
36631             for (i = 0; i < ld; i += 1) {
36632               dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
36633             }
36634
36635             /**
36636              * Repeatedly perform a long division. The binary array forms the dividend,
36637              * the length of the encoding is the divisor. Once computed, the quotient
36638              * forms the dividend for the next step. We stop when the dividend is zerHashes.
36639              * All remainders are stored for later use.
36640              */
36641             while (dividend.length > 0) {
36642               quotient = Array();
36643               x = 0;
36644               for (i = 0; i < dividend.length; i += 1) {
36645                 x = (x << 16) + dividend[i];
36646                 q = Math.floor(x / divisor);
36647                 x -= q * divisor;
36648                 if (quotient.length > 0 || q > 0) {
36649                   quotient[quotient.length] = q;
36650                 }
36651               }
36652               remainders[remainders.length] = x;
36653               dividend = quotient;
36654             }
36655
36656             /* Convert the remainders to the output string */
36657             output = '';
36658             for (i = remainders.length - 1; i >= 0; i--) {
36659               output += encoding.charAt(remainders[i]);
36660             }
36661
36662             /* Append leading zero equivalents */
36663             full_length = Math.ceil(input.length * 8 / (Math.log(encoding.length) / Math.log(2)));
36664             for (i = output.length; i < full_length; i += 1) {
36665               output = encoding[0] + output;
36666             }
36667             return output;
36668           }
36669
36670           /**
36671            * Convert a raw string to a base-64 string
36672            */
36673
36674           function rstr2b64(input, b64pad) {
36675             var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
36676               output = '',
36677               len = input.length,
36678               i, j, triplet;
36679             b64pad = b64pad || '=';
36680             for (i = 0; i < len; i += 3) {
36681               triplet = (input.charCodeAt(i) << 16) | (i + 1 < len ? input.charCodeAt(i + 1) << 8 : 0) | (i + 2 < len ? input.charCodeAt(i + 2) : 0);
36682               for (j = 0; j < 4; j += 1) {
36683                 if (i * 8 + j * 6 > input.length * 8) {
36684                   output += b64pad;
36685                 } else {
36686                   output += tab.charAt((triplet >>> 6 * (3 - j)) & 0x3F);
36687                 }
36688               }
36689             }
36690             return output;
36691           }
36692
36693           Hashes = {
36694             /**
36695              * @property {String} version
36696              * @readonly
36697              */
36698             VERSION: '1.0.6',
36699             /**
36700              * @member Hashes
36701              * @class Base64
36702              * @constructor
36703              */
36704             Base64: function() {
36705               // private properties
36706               var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
36707                 pad = '=', // default pad according with the RFC standard
36708                 utf8 = true; // by default enable UTF-8 support encoding
36709
36710               // public method for encoding
36711               this.encode = function(input) {
36712                 var i, j, triplet,
36713                   output = '',
36714                   len = input.length;
36715
36716                 pad = pad || '=';
36717                 input = (utf8) ? utf8Encode(input) : input;
36718
36719                 for (i = 0; i < len; i += 3) {
36720                   triplet = (input.charCodeAt(i) << 16) | (i + 1 < len ? input.charCodeAt(i + 1) << 8 : 0) | (i + 2 < len ? input.charCodeAt(i + 2) : 0);
36721                   for (j = 0; j < 4; j += 1) {
36722                     if (i * 8 + j * 6 > len * 8) {
36723                       output += pad;
36724                     } else {
36725                       output += tab.charAt((triplet >>> 6 * (3 - j)) & 0x3F);
36726                     }
36727                   }
36728                 }
36729                 return output;
36730               };
36731
36732               // public method for decoding
36733               this.decode = function(input) {
36734                 // var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
36735                 var i, o1, o2, o3, h1, h2, h3, h4, bits, ac,
36736                   dec = '',
36737                   arr = [];
36738                 if (!input) {
36739                   return input;
36740                 }
36741
36742                 i = ac = 0;
36743                 input = input.replace(new RegExp('\\' + pad, 'gi'), ''); // use '='
36744                 //input += '';
36745
36746                 do { // unpack four hexets into three octets using index points in b64
36747                   h1 = tab.indexOf(input.charAt(i += 1));
36748                   h2 = tab.indexOf(input.charAt(i += 1));
36749                   h3 = tab.indexOf(input.charAt(i += 1));
36750                   h4 = tab.indexOf(input.charAt(i += 1));
36751
36752                   bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
36753
36754                   o1 = bits >> 16 & 0xff;
36755                   o2 = bits >> 8 & 0xff;
36756                   o3 = bits & 0xff;
36757                   ac += 1;
36758
36759                   if (h3 === 64) {
36760                     arr[ac] = String.fromCharCode(o1);
36761                   } else if (h4 === 64) {
36762                     arr[ac] = String.fromCharCode(o1, o2);
36763                   } else {
36764                     arr[ac] = String.fromCharCode(o1, o2, o3);
36765                   }
36766                 } while (i < input.length);
36767
36768                 dec = arr.join('');
36769                 dec = (utf8) ? utf8Decode(dec) : dec;
36770
36771                 return dec;
36772               };
36773
36774               // set custom pad string
36775               this.setPad = function(str) {
36776                 pad = str || pad;
36777                 return this;
36778               };
36779               // set custom tab string characters
36780               this.setTab = function(str) {
36781                 tab = str || tab;
36782                 return this;
36783               };
36784               this.setUTF8 = function(bool) {
36785                 if (typeof bool === 'boolean') {
36786                   utf8 = bool;
36787                 }
36788                 return this;
36789               };
36790             },
36791
36792             /**
36793              * CRC-32 calculation
36794              * @member Hashes
36795              * @method CRC32
36796              * @static
36797              * @param {String} str Input String
36798              * @return {String}
36799              */
36800             CRC32: function(str) {
36801               var crc = 0,
36802                 x = 0,
36803                 y = 0,
36804                 table, i, iTop;
36805               str = utf8Encode(str);
36806
36807               table = [
36808                 '00000000 77073096 EE0E612C 990951BA 076DC419 706AF48F E963A535 9E6495A3 0EDB8832 ',
36809                 '79DCB8A4 E0D5E91E 97D2D988 09B64C2B 7EB17CBD E7B82D07 90BF1D91 1DB71064 6AB020F2 F3B97148 ',
36810                 '84BE41DE 1ADAD47D 6DDDE4EB F4D4B551 83D385C7 136C9856 646BA8C0 FD62F97A 8A65C9EC 14015C4F ',
36811                 '63066CD9 FA0F3D63 8D080DF5 3B6E20C8 4C69105E D56041E4 A2677172 3C03E4D1 4B04D447 D20D85FD ',
36812                 'A50AB56B 35B5A8FA 42B2986C DBBBC9D6 ACBCF940 32D86CE3 45DF5C75 DCD60DCF ABD13D59 26D930AC ',
36813                 '51DE003A C8D75180 BFD06116 21B4F4B5 56B3C423 CFBA9599 B8BDA50F 2802B89E 5F058808 C60CD9B2 ',
36814                 'B10BE924 2F6F7C87 58684C11 C1611DAB B6662D3D 76DC4190 01DB7106 98D220BC EFD5102A 71B18589 ',
36815                 '06B6B51F 9FBFE4A5 E8B8D433 7807C9A2 0F00F934 9609A88E E10E9818 7F6A0DBB 086D3D2D 91646C97 ',
36816                 'E6635C01 6B6B51F4 1C6C6162 856530D8 F262004E 6C0695ED 1B01A57B 8208F4C1 F50FC457 65B0D9C6 ',
36817                 '12B7E950 8BBEB8EA FCB9887C 62DD1DDF 15DA2D49 8CD37CF3 FBD44C65 4DB26158 3AB551CE A3BC0074 ',
36818                 'D4BB30E2 4ADFA541 3DD895D7 A4D1C46D D3D6F4FB 4369E96A 346ED9FC AD678846 DA60B8D0 44042D73 ',
36819                 '33031DE5 AA0A4C5F DD0D7CC9 5005713C 270241AA BE0B1010 C90C2086 5768B525 206F85B3 B966D409 ',
36820                 'CE61E49F 5EDEF90E 29D9C998 B0D09822 C7D7A8B4 59B33D17 2EB40D81 B7BD5C3B C0BA6CAD EDB88320 ',
36821                 '9ABFB3B6 03B6E20C 74B1D29A EAD54739 9DD277AF 04DB2615 73DC1683 E3630B12 94643B84 0D6D6A3E ',
36822                 '7A6A5AA8 E40ECF0B 9309FF9D 0A00AE27 7D079EB1 F00F9344 8708A3D2 1E01F268 6906C2FE F762575D ',
36823                 '806567CB 196C3671 6E6B06E7 FED41B76 89D32BE0 10DA7A5A 67DD4ACC F9B9DF6F 8EBEEFF9 17B7BE43 ',
36824                 '60B08ED5 D6D6A3E8 A1D1937E 38D8C2C4 4FDFF252 D1BB67F1 A6BC5767 3FB506DD 48B2364B D80D2BDA ',
36825                 'AF0A1B4C 36034AF6 41047A60 DF60EFC3 A867DF55 316E8EEF 4669BE79 CB61B38C BC66831A 256FD2A0 ',
36826                 '5268E236 CC0C7795 BB0B4703 220216B9 5505262F C5BA3BBE B2BD0B28 2BB45A92 5CB36A04 C2D7FFA7 ',
36827                 'B5D0CF31 2CD99E8B 5BDEAE1D 9B64C2B0 EC63F226 756AA39C 026D930A 9C0906A9 EB0E363F 72076785 ',
36828                 '05005713 95BF4A82 E2B87A14 7BB12BAE 0CB61B38 92D28E9B E5D5BE0D 7CDCEFB7 0BDBDF21 86D3D2D4 ',
36829                 'F1D4E242 68DDB3F8 1FDA836E 81BE16CD F6B9265B 6FB077E1 18B74777 88085AE6 FF0F6A70 66063BCA ',
36830                 '11010B5C 8F659EFF F862AE69 616BFFD3 166CCF45 A00AE278 D70DD2EE 4E048354 3903B3C2 A7672661 ',
36831                 'D06016F7 4969474D 3E6E77DB AED16A4A D9D65ADC 40DF0B66 37D83BF0 A9BCAE53 DEBB9EC5 47B2CF7F ',
36832                 '30B5FFE9 BDBDF21C CABAC28A 53B39330 24B4A3A6 BAD03605 CDD70693 54DE5729 23D967BF B3667A2E ',
36833                 'C4614AB8 5D681B02 2A6F2B94 B40BBE37 C30C8EA1 5A05DF1B 2D02EF8D'
36834               ].join('');
36835
36836               crc = crc ^ (-1);
36837               for (i = 0, iTop = str.length; i < iTop; i += 1) {
36838                 y = (crc ^ str.charCodeAt(i)) & 0xFF;
36839                 x = '0x' + table.substr(y * 9, 8);
36840                 crc = (crc >>> 8) ^ x;
36841               }
36842               // always return a positive number (that's what >>> 0 does)
36843               return (crc ^ (-1)) >>> 0;
36844             },
36845             /**
36846              * @member Hashes
36847              * @class MD5
36848              * @constructor
36849              * @param {Object} [config]
36850              *
36851              * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
36852              * Digest Algorithm, as defined in RFC 1321.
36853              * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
36854              * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
36855              * See <http://pajhome.org.uk/crypt/md5> for more infHashes.
36856              */
36857             MD5: function(options) {
36858               /**
36859                * Private config properties. You may need to tweak these to be compatible with
36860                * the server-side, but the defaults work in most cases.
36861                * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
36862                */
36863               var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase
36864                 b64pad = (options && typeof options.pad === 'string') ? options.pad : '=', // base-64 pad character. Defaults to '=' for strict RFC compliance
36865                 utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true; // enable/disable utf8 encoding
36866
36867               // privileged (public) methods
36868               this.hex = function(s) {
36869                 return rstr2hex(rstr(s), hexcase);
36870               };
36871               this.b64 = function(s) {
36872                 return rstr2b64(rstr(s), b64pad);
36873               };
36874               this.any = function(s, e) {
36875                 return rstr2any(rstr(s), e);
36876               };
36877               this.raw = function(s) {
36878                 return rstr(s);
36879               };
36880               this.hex_hmac = function(k, d) {
36881                 return rstr2hex(rstr_hmac(k, d), hexcase);
36882               };
36883               this.b64_hmac = function(k, d) {
36884                 return rstr2b64(rstr_hmac(k, d), b64pad);
36885               };
36886               this.any_hmac = function(k, d, e) {
36887                 return rstr2any(rstr_hmac(k, d), e);
36888               };
36889               /**
36890                * Perform a simple self-test to see if the VM is working
36891                * @return {String} Hexadecimal hash sample
36892                */
36893               this.vm_test = function() {
36894                 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
36895               };
36896               /**
36897                * Enable/disable uppercase hexadecimal returned string
36898                * @param {Boolean}
36899                * @return {Object} this
36900                */
36901               this.setUpperCase = function(a) {
36902                 if (typeof a === 'boolean') {
36903                   hexcase = a;
36904                 }
36905                 return this;
36906               };
36907               /**
36908                * Defines a base64 pad string
36909                * @param {String} Pad
36910                * @return {Object} this
36911                */
36912               this.setPad = function(a) {
36913                 b64pad = a || b64pad;
36914                 return this;
36915               };
36916               /**
36917                * Defines a base64 pad string
36918                * @param {Boolean}
36919                * @return {Object} [this]
36920                */
36921               this.setUTF8 = function(a) {
36922                 if (typeof a === 'boolean') {
36923                   utf8 = a;
36924                 }
36925                 return this;
36926               };
36927
36928               // private methods
36929
36930               /**
36931                * Calculate the MD5 of a raw string
36932                */
36933
36934               function rstr(s) {
36935                 s = (utf8) ? utf8Encode(s) : s;
36936                 return binl2rstr(binl(rstr2binl(s), s.length * 8));
36937               }
36938
36939               /**
36940                * Calculate the HMAC-MD5, of a key and some data (raw strings)
36941                */
36942
36943               function rstr_hmac(key, data) {
36944                 var bkey, ipad, opad, hash, i;
36945
36946                 key = (utf8) ? utf8Encode(key) : key;
36947                 data = (utf8) ? utf8Encode(data) : data;
36948                 bkey = rstr2binl(key);
36949                 if (bkey.length > 16) {
36950                   bkey = binl(bkey, key.length * 8);
36951                 }
36952
36953                 ipad = Array(16), opad = Array(16);
36954                 for (i = 0; i < 16; i += 1) {
36955                   ipad[i] = bkey[i] ^ 0x36363636;
36956                   opad[i] = bkey[i] ^ 0x5C5C5C5C;
36957                 }
36958                 hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
36959                 return binl2rstr(binl(opad.concat(hash), 512 + 128));
36960               }
36961
36962               /**
36963                * Calculate the MD5 of an array of little-endian words, and a bit length.
36964                */
36965
36966               function binl(x, len) {
36967                 var i, olda, oldb, oldc, oldd,
36968                   a = 1732584193,
36969                   b = -271733879,
36970                   c = -1732584194,
36971                   d = 271733878;
36972
36973                 /* append padding */
36974                 x[len >> 5] |= 0x80 << ((len) % 32);
36975                 x[(((len + 64) >>> 9) << 4) + 14] = len;
36976
36977                 for (i = 0; i < x.length; i += 16) {
36978                   olda = a;
36979                   oldb = b;
36980                   oldc = c;
36981                   oldd = d;
36982
36983                   a = md5_ff(a, b, c, d, x[i + 0], 7, -680876936);
36984                   d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586);
36985                   c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819);
36986                   b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330);
36987                   a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897);
36988                   d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426);
36989                   c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341);
36990                   b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983);
36991                   a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416);
36992                   d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417);
36993                   c = md5_ff(c, d, a, b, x[i + 10], 17, -42063);
36994                   b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162);
36995                   a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682);
36996                   d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101);
36997                   c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290);
36998                   b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329);
36999
37000                   a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510);
37001                   d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632);
37002                   c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713);
37003                   b = md5_gg(b, c, d, a, x[i + 0], 20, -373897302);
37004                   a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691);
37005                   d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083);
37006                   c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335);
37007                   b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848);
37008                   a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438);
37009                   d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690);
37010                   c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961);
37011                   b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501);
37012                   a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467);
37013                   d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784);
37014                   c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473);
37015                   b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734);
37016
37017                   a = md5_hh(a, b, c, d, x[i + 5], 4, -378558);
37018                   d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463);
37019                   c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562);
37020                   b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556);
37021                   a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060);
37022                   d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353);
37023                   c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632);
37024                   b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640);
37025                   a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174);
37026                   d = md5_hh(d, a, b, c, x[i + 0], 11, -358537222);
37027                   c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979);
37028                   b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189);
37029                   a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487);
37030                   d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835);
37031                   c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520);
37032                   b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651);
37033
37034                   a = md5_ii(a, b, c, d, x[i + 0], 6, -198630844);
37035                   d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415);
37036                   c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905);
37037                   b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055);
37038                   a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571);
37039                   d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606);
37040                   c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523);
37041                   b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799);
37042                   a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359);
37043                   d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744);
37044                   c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380);
37045                   b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649);
37046                   a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070);
37047                   d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379);
37048                   c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259);
37049                   b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551);
37050
37051                   a = safe_add(a, olda);
37052                   b = safe_add(b, oldb);
37053                   c = safe_add(c, oldc);
37054                   d = safe_add(d, oldd);
37055                 }
37056                 return Array(a, b, c, d);
37057               }
37058
37059               /**
37060                * These functions implement the four basic operations the algorithm uses.
37061                */
37062
37063               function md5_cmn(q, a, b, x, s, t) {
37064                 return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b);
37065               }
37066
37067               function md5_ff(a, b, c, d, x, s, t) {
37068                 return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
37069               }
37070
37071               function md5_gg(a, b, c, d, x, s, t) {
37072                 return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
37073               }
37074
37075               function md5_hh(a, b, c, d, x, s, t) {
37076                 return md5_cmn(b ^ c ^ d, a, b, x, s, t);
37077               }
37078
37079               function md5_ii(a, b, c, d, x, s, t) {
37080                 return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
37081               }
37082             },
37083             /**
37084              * @member Hashes
37085              * @class Hashes.SHA1
37086              * @param {Object} [config]
37087              * @constructor
37088              *
37089              * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined in FIPS 180-1
37090              * Version 2.2 Copyright Paul Johnston 2000 - 2009.
37091              * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
37092              * See http://pajhome.org.uk/crypt/md5 for details.
37093              */
37094             SHA1: function(options) {
37095               /**
37096                * Private config properties. You may need to tweak these to be compatible with
37097                * the server-side, but the defaults work in most cases.
37098                * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
37099                */
37100               var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase
37101                 b64pad = (options && typeof options.pad === 'string') ? options.pad : '=', // base-64 pad character. Defaults to '=' for strict RFC compliance
37102                 utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true; // enable/disable utf8 encoding
37103
37104               // public methods
37105               this.hex = function(s) {
37106                 return rstr2hex(rstr(s), hexcase);
37107               };
37108               this.b64 = function(s) {
37109                 return rstr2b64(rstr(s), b64pad);
37110               };
37111               this.any = function(s, e) {
37112                 return rstr2any(rstr(s), e);
37113               };
37114               this.raw = function(s) {
37115                 return rstr(s);
37116               };
37117               this.hex_hmac = function(k, d) {
37118                 return rstr2hex(rstr_hmac(k, d));
37119               };
37120               this.b64_hmac = function(k, d) {
37121                 return rstr2b64(rstr_hmac(k, d), b64pad);
37122               };
37123               this.any_hmac = function(k, d, e) {
37124                 return rstr2any(rstr_hmac(k, d), e);
37125               };
37126               /**
37127                * Perform a simple self-test to see if the VM is working
37128                * @return {String} Hexadecimal hash sample
37129                * @public
37130                */
37131               this.vm_test = function() {
37132                 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
37133               };
37134               /**
37135                * @description Enable/disable uppercase hexadecimal returned string
37136                * @param {boolean}
37137                * @return {Object} this
37138                * @public
37139                */
37140               this.setUpperCase = function(a) {
37141                 if (typeof a === 'boolean') {
37142                   hexcase = a;
37143                 }
37144                 return this;
37145               };
37146               /**
37147                * @description Defines a base64 pad string
37148                * @param {string} Pad
37149                * @return {Object} this
37150                * @public
37151                */
37152               this.setPad = function(a) {
37153                 b64pad = a || b64pad;
37154                 return this;
37155               };
37156               /**
37157                * @description Defines a base64 pad string
37158                * @param {boolean}
37159                * @return {Object} this
37160                * @public
37161                */
37162               this.setUTF8 = function(a) {
37163                 if (typeof a === 'boolean') {
37164                   utf8 = a;
37165                 }
37166                 return this;
37167               };
37168
37169               // private methods
37170
37171               /**
37172                * Calculate the SHA-512 of a raw string
37173                */
37174
37175               function rstr(s) {
37176                 s = (utf8) ? utf8Encode(s) : s;
37177                 return binb2rstr(binb(rstr2binb(s), s.length * 8));
37178               }
37179
37180               /**
37181                * Calculate the HMAC-SHA1 of a key and some data (raw strings)
37182                */
37183
37184               function rstr_hmac(key, data) {
37185                 var bkey, ipad, opad, i, hash;
37186                 key = (utf8) ? utf8Encode(key) : key;
37187                 data = (utf8) ? utf8Encode(data) : data;
37188                 bkey = rstr2binb(key);
37189
37190                 if (bkey.length > 16) {
37191                   bkey = binb(bkey, key.length * 8);
37192                 }
37193                 ipad = Array(16), opad = Array(16);
37194                 for (i = 0; i < 16; i += 1) {
37195                   ipad[i] = bkey[i] ^ 0x36363636;
37196                   opad[i] = bkey[i] ^ 0x5C5C5C5C;
37197                 }
37198                 hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
37199                 return binb2rstr(binb(opad.concat(hash), 512 + 160));
37200               }
37201
37202               /**
37203                * Calculate the SHA-1 of an array of big-endian words, and a bit length
37204                */
37205
37206               function binb(x, len) {
37207                 var i, j, t, olda, oldb, oldc, oldd, olde,
37208                   w = Array(80),
37209                   a = 1732584193,
37210                   b = -271733879,
37211                   c = -1732584194,
37212                   d = 271733878,
37213                   e = -1009589776;
37214
37215                 /* append padding */
37216                 x[len >> 5] |= 0x80 << (24 - len % 32);
37217                 x[((len + 64 >> 9) << 4) + 15] = len;
37218
37219                 for (i = 0; i < x.length; i += 16) {
37220                   olda = a;
37221                   oldb = b;
37222                   oldc = c;
37223                   oldd = d;
37224                   olde = e;
37225
37226                   for (j = 0; j < 80; j += 1) {
37227                     if (j < 16) {
37228                       w[j] = x[i + j];
37229                     } else {
37230                       w[j] = bit_rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1);
37231                     }
37232                     t = safe_add(safe_add(bit_rol(a, 5), sha1_ft(j, b, c, d)),
37233                       safe_add(safe_add(e, w[j]), sha1_kt(j)));
37234                     e = d;
37235                     d = c;
37236                     c = bit_rol(b, 30);
37237                     b = a;
37238                     a = t;
37239                   }
37240
37241                   a = safe_add(a, olda);
37242                   b = safe_add(b, oldb);
37243                   c = safe_add(c, oldc);
37244                   d = safe_add(d, oldd);
37245                   e = safe_add(e, olde);
37246                 }
37247                 return Array(a, b, c, d, e);
37248               }
37249
37250               /**
37251                * Perform the appropriate triplet combination function for the current
37252                * iteration
37253                */
37254
37255               function sha1_ft(t, b, c, d) {
37256                 if (t < 20) {
37257                   return (b & c) | ((~b) & d);
37258                 }
37259                 if (t < 40) {
37260                   return b ^ c ^ d;
37261                 }
37262                 if (t < 60) {
37263                   return (b & c) | (b & d) | (c & d);
37264                 }
37265                 return b ^ c ^ d;
37266               }
37267
37268               /**
37269                * Determine the appropriate additive constant for the current iteration
37270                */
37271
37272               function sha1_kt(t) {
37273                 return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 :
37274                   (t < 60) ? -1894007588 : -899497514;
37275               }
37276             },
37277             /**
37278              * @class Hashes.SHA256
37279              * @param {config}
37280              *
37281              * A JavaScript implementation of the Secure Hash Algorithm, SHA-256, as defined in FIPS 180-2
37282              * Version 2.2 Copyright Angel Marin, Paul Johnston 2000 - 2009.
37283              * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
37284              * See http://pajhome.org.uk/crypt/md5 for details.
37285              * Also http://anmar.eu.org/projects/jssha2/
37286              */
37287             SHA256: function(options) {
37288               /**
37289                * Private properties configuration variables. You may need to tweak these to be compatible with
37290                * the server-side, but the defaults work in most cases.
37291                * @see this.setUpperCase() method
37292                * @see this.setPad() method
37293                */
37294               var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase  */
37295                 b64pad = (options && typeof options.pad === 'string') ? options.pad : '=',
37296                 /* base-64 pad character. Default '=' for strict RFC compliance   */
37297                 utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true,
37298                 /* enable/disable utf8 encoding */
37299                 sha256_K;
37300
37301               /* privileged (public) methods */
37302               this.hex = function(s) {
37303                 return rstr2hex(rstr(s, utf8));
37304               };
37305               this.b64 = function(s) {
37306                 return rstr2b64(rstr(s, utf8), b64pad);
37307               };
37308               this.any = function(s, e) {
37309                 return rstr2any(rstr(s, utf8), e);
37310               };
37311               this.raw = function(s) {
37312                 return rstr(s, utf8);
37313               };
37314               this.hex_hmac = function(k, d) {
37315                 return rstr2hex(rstr_hmac(k, d));
37316               };
37317               this.b64_hmac = function(k, d) {
37318                 return rstr2b64(rstr_hmac(k, d), b64pad);
37319               };
37320               this.any_hmac = function(k, d, e) {
37321                 return rstr2any(rstr_hmac(k, d), e);
37322               };
37323               /**
37324                * Perform a simple self-test to see if the VM is working
37325                * @return {String} Hexadecimal hash sample
37326                * @public
37327                */
37328               this.vm_test = function() {
37329                 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
37330               };
37331               /**
37332                * Enable/disable uppercase hexadecimal returned string
37333                * @param {boolean}
37334                * @return {Object} this
37335                * @public
37336                */
37337               this.setUpperCase = function(a) {
37338                 if (typeof a === 'boolean') {
37339                   hexcase = a;
37340                 }
37341                 return this;
37342               };
37343               /**
37344                * @description Defines a base64 pad string
37345                * @param {string} Pad
37346                * @return {Object} this
37347                * @public
37348                */
37349               this.setPad = function(a) {
37350                 b64pad = a || b64pad;
37351                 return this;
37352               };
37353               /**
37354                * Defines a base64 pad string
37355                * @param {boolean}
37356                * @return {Object} this
37357                * @public
37358                */
37359               this.setUTF8 = function(a) {
37360                 if (typeof a === 'boolean') {
37361                   utf8 = a;
37362                 }
37363                 return this;
37364               };
37365
37366               // private methods
37367
37368               /**
37369                * Calculate the SHA-512 of a raw string
37370                */
37371
37372               function rstr(s, utf8) {
37373                 s = (utf8) ? utf8Encode(s) : s;
37374                 return binb2rstr(binb(rstr2binb(s), s.length * 8));
37375               }
37376
37377               /**
37378                * Calculate the HMAC-sha256 of a key and some data (raw strings)
37379                */
37380
37381               function rstr_hmac(key, data) {
37382                 key = (utf8) ? utf8Encode(key) : key;
37383                 data = (utf8) ? utf8Encode(data) : data;
37384                 var hash, i = 0,
37385                   bkey = rstr2binb(key),
37386                   ipad = Array(16),
37387                   opad = Array(16);
37388
37389                 if (bkey.length > 16) {
37390                   bkey = binb(bkey, key.length * 8);
37391                 }
37392
37393                 for (; i < 16; i += 1) {
37394                   ipad[i] = bkey[i] ^ 0x36363636;
37395                   opad[i] = bkey[i] ^ 0x5C5C5C5C;
37396                 }
37397
37398                 hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
37399                 return binb2rstr(binb(opad.concat(hash), 512 + 256));
37400               }
37401
37402               /*
37403                * Main sha256 function, with its support functions
37404                */
37405
37406               function sha256_S(X, n) {
37407                 return (X >>> n) | (X << (32 - n));
37408               }
37409
37410               function sha256_R(X, n) {
37411                 return (X >>> n);
37412               }
37413
37414               function sha256_Ch(x, y, z) {
37415                 return ((x & y) ^ ((~x) & z));
37416               }
37417
37418               function sha256_Maj(x, y, z) {
37419                 return ((x & y) ^ (x & z) ^ (y & z));
37420               }
37421
37422               function sha256_Sigma0256(x) {
37423                 return (sha256_S(x, 2) ^ sha256_S(x, 13) ^ sha256_S(x, 22));
37424               }
37425
37426               function sha256_Sigma1256(x) {
37427                 return (sha256_S(x, 6) ^ sha256_S(x, 11) ^ sha256_S(x, 25));
37428               }
37429
37430               function sha256_Gamma0256(x) {
37431                 return (sha256_S(x, 7) ^ sha256_S(x, 18) ^ sha256_R(x, 3));
37432               }
37433
37434               function sha256_Gamma1256(x) {
37435                 return (sha256_S(x, 17) ^ sha256_S(x, 19) ^ sha256_R(x, 10));
37436               }
37437
37438               sha256_K = [
37439                 1116352408, 1899447441, -1245643825, -373957723, 961987163, 1508970993, -1841331548, -1424204075, -670586216, 310598401, 607225278, 1426881987,
37440                 1925078388, -2132889090, -1680079193, -1046744716, -459576895, -272742522,
37441                 264347078, 604807628, 770255983, 1249150122, 1555081692, 1996064986, -1740746414, -1473132947, -1341970488, -1084653625, -958395405, -710438585,
37442                 113926993, 338241895, 666307205, 773529912, 1294757372, 1396182291,
37443                 1695183700, 1986661051, -2117940946, -1838011259, -1564481375, -1474664885, -1035236496, -949202525, -778901479, -694614492, -200395387, 275423344,
37444                 430227734, 506948616, 659060556, 883997877, 958139571, 1322822218,
37445                 1537002063, 1747873779, 1955562222, 2024104815, -2067236844, -1933114872, -1866530822, -1538233109, -1090935817, -965641998
37446               ];
37447
37448               function binb(m, l) {
37449                 var HASH = [1779033703, -1150833019, 1013904242, -1521486534,
37450                   1359893119, -1694144372, 528734635, 1541459225
37451                 ];
37452                 var W = new Array(64);
37453                 var a, b, c, d, e, f, g, h;
37454                 var i, j, T1, T2;
37455
37456                 /* append padding */
37457                 m[l >> 5] |= 0x80 << (24 - l % 32);
37458                 m[((l + 64 >> 9) << 4) + 15] = l;
37459
37460                 for (i = 0; i < m.length; i += 16) {
37461                   a = HASH[0];
37462                   b = HASH[1];
37463                   c = HASH[2];
37464                   d = HASH[3];
37465                   e = HASH[4];
37466                   f = HASH[5];
37467                   g = HASH[6];
37468                   h = HASH[7];
37469
37470                   for (j = 0; j < 64; j += 1) {
37471                     if (j < 16) {
37472                       W[j] = m[j + i];
37473                     } else {
37474                       W[j] = safe_add(safe_add(safe_add(sha256_Gamma1256(W[j - 2]), W[j - 7]),
37475                         sha256_Gamma0256(W[j - 15])), W[j - 16]);
37476                     }
37477
37478                     T1 = safe_add(safe_add(safe_add(safe_add(h, sha256_Sigma1256(e)), sha256_Ch(e, f, g)),
37479                       sha256_K[j]), W[j]);
37480                     T2 = safe_add(sha256_Sigma0256(a), sha256_Maj(a, b, c));
37481                     h = g;
37482                     g = f;
37483                     f = e;
37484                     e = safe_add(d, T1);
37485                     d = c;
37486                     c = b;
37487                     b = a;
37488                     a = safe_add(T1, T2);
37489                   }
37490
37491                   HASH[0] = safe_add(a, HASH[0]);
37492                   HASH[1] = safe_add(b, HASH[1]);
37493                   HASH[2] = safe_add(c, HASH[2]);
37494                   HASH[3] = safe_add(d, HASH[3]);
37495                   HASH[4] = safe_add(e, HASH[4]);
37496                   HASH[5] = safe_add(f, HASH[5]);
37497                   HASH[6] = safe_add(g, HASH[6]);
37498                   HASH[7] = safe_add(h, HASH[7]);
37499                 }
37500                 return HASH;
37501               }
37502
37503             },
37504
37505             /**
37506              * @class Hashes.SHA512
37507              * @param {config}
37508              *
37509              * A JavaScript implementation of the Secure Hash Algorithm, SHA-512, as defined in FIPS 180-2
37510              * Version 2.2 Copyright Anonymous Contributor, Paul Johnston 2000 - 2009.
37511              * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
37512              * See http://pajhome.org.uk/crypt/md5 for details.
37513              */
37514             SHA512: function(options) {
37515               /**
37516                * Private properties configuration variables. You may need to tweak these to be compatible with
37517                * the server-side, but the defaults work in most cases.
37518                * @see this.setUpperCase() method
37519                * @see this.setPad() method
37520                */
37521               var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false,
37522                 /* hexadecimal output case format. false - lowercase; true - uppercase  */
37523                 b64pad = (options && typeof options.pad === 'string') ? options.pad : '=',
37524                 /* base-64 pad character. Default '=' for strict RFC compliance   */
37525                 utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true,
37526                 /* enable/disable utf8 encoding */
37527                 sha512_k;
37528
37529               /* privileged (public) methods */
37530               this.hex = function(s) {
37531                 return rstr2hex(rstr(s));
37532               };
37533               this.b64 = function(s) {
37534                 return rstr2b64(rstr(s), b64pad);
37535               };
37536               this.any = function(s, e) {
37537                 return rstr2any(rstr(s), e);
37538               };
37539               this.raw = function(s) {
37540                 return rstr(s);
37541               };
37542               this.hex_hmac = function(k, d) {
37543                 return rstr2hex(rstr_hmac(k, d));
37544               };
37545               this.b64_hmac = function(k, d) {
37546                 return rstr2b64(rstr_hmac(k, d), b64pad);
37547               };
37548               this.any_hmac = function(k, d, e) {
37549                 return rstr2any(rstr_hmac(k, d), e);
37550               };
37551               /**
37552                * Perform a simple self-test to see if the VM is working
37553                * @return {String} Hexadecimal hash sample
37554                * @public
37555                */
37556               this.vm_test = function() {
37557                 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
37558               };
37559               /**
37560                * @description Enable/disable uppercase hexadecimal returned string
37561                * @param {boolean}
37562                * @return {Object} this
37563                * @public
37564                */
37565               this.setUpperCase = function(a) {
37566                 if (typeof a === 'boolean') {
37567                   hexcase = a;
37568                 }
37569                 return this;
37570               };
37571               /**
37572                * @description Defines a base64 pad string
37573                * @param {string} Pad
37574                * @return {Object} this
37575                * @public
37576                */
37577               this.setPad = function(a) {
37578                 b64pad = a || b64pad;
37579                 return this;
37580               };
37581               /**
37582                * @description Defines a base64 pad string
37583                * @param {boolean}
37584                * @return {Object} this
37585                * @public
37586                */
37587               this.setUTF8 = function(a) {
37588                 if (typeof a === 'boolean') {
37589                   utf8 = a;
37590                 }
37591                 return this;
37592               };
37593
37594               /* private methods */
37595
37596               /**
37597                * Calculate the SHA-512 of a raw string
37598                */
37599
37600               function rstr(s) {
37601                 s = (utf8) ? utf8Encode(s) : s;
37602                 return binb2rstr(binb(rstr2binb(s), s.length * 8));
37603               }
37604               /*
37605                * Calculate the HMAC-SHA-512 of a key and some data (raw strings)
37606                */
37607
37608               function rstr_hmac(key, data) {
37609                 key = (utf8) ? utf8Encode(key) : key;
37610                 data = (utf8) ? utf8Encode(data) : data;
37611
37612                 var hash, i = 0,
37613                   bkey = rstr2binb(key),
37614                   ipad = Array(32),
37615                   opad = Array(32);
37616
37617                 if (bkey.length > 32) {
37618                   bkey = binb(bkey, key.length * 8);
37619                 }
37620
37621                 for (; i < 32; i += 1) {
37622                   ipad[i] = bkey[i] ^ 0x36363636;
37623                   opad[i] = bkey[i] ^ 0x5C5C5C5C;
37624                 }
37625
37626                 hash = binb(ipad.concat(rstr2binb(data)), 1024 + data.length * 8);
37627                 return binb2rstr(binb(opad.concat(hash), 1024 + 512));
37628               }
37629
37630               /**
37631                * Calculate the SHA-512 of an array of big-endian dwords, and a bit length
37632                */
37633
37634               function binb(x, len) {
37635                 var j, i, l,
37636                   W = new Array(80),
37637                   hash = new Array(16),
37638                   //Initial hash values
37639                   H = [
37640                     new int64(0x6a09e667, -205731576),
37641                     new int64(-1150833019, -2067093701),
37642                     new int64(0x3c6ef372, -23791573),
37643                     new int64(-1521486534, 0x5f1d36f1),
37644                     new int64(0x510e527f, -1377402159),
37645                     new int64(-1694144372, 0x2b3e6c1f),
37646                     new int64(0x1f83d9ab, -79577749),
37647                     new int64(0x5be0cd19, 0x137e2179)
37648                   ],
37649                   T1 = new int64(0, 0),
37650                   T2 = new int64(0, 0),
37651                   a = new int64(0, 0),
37652                   b = new int64(0, 0),
37653                   c = new int64(0, 0),
37654                   d = new int64(0, 0),
37655                   e = new int64(0, 0),
37656                   f = new int64(0, 0),
37657                   g = new int64(0, 0),
37658                   h = new int64(0, 0),
37659                   //Temporary variables not specified by the document
37660                   s0 = new int64(0, 0),
37661                   s1 = new int64(0, 0),
37662                   Ch = new int64(0, 0),
37663                   Maj = new int64(0, 0),
37664                   r1 = new int64(0, 0),
37665                   r2 = new int64(0, 0),
37666                   r3 = new int64(0, 0);
37667
37668                 if (sha512_k === undefined) {
37669                   //SHA512 constants
37670                   sha512_k = [
37671                     new int64(0x428a2f98, -685199838), new int64(0x71374491, 0x23ef65cd),
37672                     new int64(-1245643825, -330482897), new int64(-373957723, -2121671748),
37673                     new int64(0x3956c25b, -213338824), new int64(0x59f111f1, -1241133031),
37674                     new int64(-1841331548, -1357295717), new int64(-1424204075, -630357736),
37675                     new int64(-670586216, -1560083902), new int64(0x12835b01, 0x45706fbe),
37676                     new int64(0x243185be, 0x4ee4b28c), new int64(0x550c7dc3, -704662302),
37677                     new int64(0x72be5d74, -226784913), new int64(-2132889090, 0x3b1696b1),
37678                     new int64(-1680079193, 0x25c71235), new int64(-1046744716, -815192428),
37679                     new int64(-459576895, -1628353838), new int64(-272742522, 0x384f25e3),
37680                     new int64(0xfc19dc6, -1953704523), new int64(0x240ca1cc, 0x77ac9c65),
37681                     new int64(0x2de92c6f, 0x592b0275), new int64(0x4a7484aa, 0x6ea6e483),
37682                     new int64(0x5cb0a9dc, -1119749164), new int64(0x76f988da, -2096016459),
37683                     new int64(-1740746414, -295247957), new int64(-1473132947, 0x2db43210),
37684                     new int64(-1341970488, -1728372417), new int64(-1084653625, -1091629340),
37685                     new int64(-958395405, 0x3da88fc2), new int64(-710438585, -1828018395),
37686                     new int64(0x6ca6351, -536640913), new int64(0x14292967, 0xa0e6e70),
37687                     new int64(0x27b70a85, 0x46d22ffc), new int64(0x2e1b2138, 0x5c26c926),
37688                     new int64(0x4d2c6dfc, 0x5ac42aed), new int64(0x53380d13, -1651133473),
37689                     new int64(0x650a7354, -1951439906), new int64(0x766a0abb, 0x3c77b2a8),
37690                     new int64(-2117940946, 0x47edaee6), new int64(-1838011259, 0x1482353b),
37691                     new int64(-1564481375, 0x4cf10364), new int64(-1474664885, -1136513023),
37692                     new int64(-1035236496, -789014639), new int64(-949202525, 0x654be30),
37693                     new int64(-778901479, -688958952), new int64(-694614492, 0x5565a910),
37694                     new int64(-200395387, 0x5771202a), new int64(0x106aa070, 0x32bbd1b8),
37695                     new int64(0x19a4c116, -1194143544), new int64(0x1e376c08, 0x5141ab53),
37696                     new int64(0x2748774c, -544281703), new int64(0x34b0bcb5, -509917016),
37697                     new int64(0x391c0cb3, -976659869), new int64(0x4ed8aa4a, -482243893),
37698                     new int64(0x5b9cca4f, 0x7763e373), new int64(0x682e6ff3, -692930397),
37699                     new int64(0x748f82ee, 0x5defb2fc), new int64(0x78a5636f, 0x43172f60),
37700                     new int64(-2067236844, -1578062990), new int64(-1933114872, 0x1a6439ec),
37701                     new int64(-1866530822, 0x23631e28), new int64(-1538233109, -561857047),
37702                     new int64(-1090935817, -1295615723), new int64(-965641998, -479046869),
37703                     new int64(-903397682, -366583396), new int64(-779700025, 0x21c0c207),
37704                     new int64(-354779690, -840897762), new int64(-176337025, -294727304),
37705                     new int64(0x6f067aa, 0x72176fba), new int64(0xa637dc5, -1563912026),
37706                     new int64(0x113f9804, -1090974290), new int64(0x1b710b35, 0x131c471b),
37707                     new int64(0x28db77f5, 0x23047d84), new int64(0x32caab7b, 0x40c72493),
37708                     new int64(0x3c9ebe0a, 0x15c9bebc), new int64(0x431d67c4, -1676669620),
37709                     new int64(0x4cc5d4be, -885112138), new int64(0x597f299c, -60457430),
37710                     new int64(0x5fcb6fab, 0x3ad6faec), new int64(0x6c44198c, 0x4a475817)
37711                   ];
37712                 }
37713
37714                 for (i = 0; i < 80; i += 1) {
37715                   W[i] = new int64(0, 0);
37716                 }
37717
37718                 // append padding to the source string. The format is described in the FIPS.
37719                 x[len >> 5] |= 0x80 << (24 - (len & 0x1f));
37720                 x[((len + 128 >> 10) << 5) + 31] = len;
37721                 l = x.length;
37722                 for (i = 0; i < l; i += 32) { //32 dwords is the block size
37723                   int64copy(a, H[0]);
37724                   int64copy(b, H[1]);
37725                   int64copy(c, H[2]);
37726                   int64copy(d, H[3]);
37727                   int64copy(e, H[4]);
37728                   int64copy(f, H[5]);
37729                   int64copy(g, H[6]);
37730                   int64copy(h, H[7]);
37731
37732                   for (j = 0; j < 16; j += 1) {
37733                     W[j].h = x[i + 2 * j];
37734                     W[j].l = x[i + 2 * j + 1];
37735                   }
37736
37737                   for (j = 16; j < 80; j += 1) {
37738                     //sigma1
37739                     int64rrot(r1, W[j - 2], 19);
37740                     int64revrrot(r2, W[j - 2], 29);
37741                     int64shr(r3, W[j - 2], 6);
37742                     s1.l = r1.l ^ r2.l ^ r3.l;
37743                     s1.h = r1.h ^ r2.h ^ r3.h;
37744                     //sigma0
37745                     int64rrot(r1, W[j - 15], 1);
37746                     int64rrot(r2, W[j - 15], 8);
37747                     int64shr(r3, W[j - 15], 7);
37748                     s0.l = r1.l ^ r2.l ^ r3.l;
37749                     s0.h = r1.h ^ r2.h ^ r3.h;
37750
37751                     int64add4(W[j], s1, W[j - 7], s0, W[j - 16]);
37752                   }
37753
37754                   for (j = 0; j < 80; j += 1) {
37755                     //Ch
37756                     Ch.l = (e.l & f.l) ^ (~e.l & g.l);
37757                     Ch.h = (e.h & f.h) ^ (~e.h & g.h);
37758
37759                     //Sigma1
37760                     int64rrot(r1, e, 14);
37761                     int64rrot(r2, e, 18);
37762                     int64revrrot(r3, e, 9);
37763                     s1.l = r1.l ^ r2.l ^ r3.l;
37764                     s1.h = r1.h ^ r2.h ^ r3.h;
37765
37766                     //Sigma0
37767                     int64rrot(r1, a, 28);
37768                     int64revrrot(r2, a, 2);
37769                     int64revrrot(r3, a, 7);
37770                     s0.l = r1.l ^ r2.l ^ r3.l;
37771                     s0.h = r1.h ^ r2.h ^ r3.h;
37772
37773                     //Maj
37774                     Maj.l = (a.l & b.l) ^ (a.l & c.l) ^ (b.l & c.l);
37775                     Maj.h = (a.h & b.h) ^ (a.h & c.h) ^ (b.h & c.h);
37776
37777                     int64add5(T1, h, s1, Ch, sha512_k[j], W[j]);
37778                     int64add(T2, s0, Maj);
37779
37780                     int64copy(h, g);
37781                     int64copy(g, f);
37782                     int64copy(f, e);
37783                     int64add(e, d, T1);
37784                     int64copy(d, c);
37785                     int64copy(c, b);
37786                     int64copy(b, a);
37787                     int64add(a, T1, T2);
37788                   }
37789                   int64add(H[0], H[0], a);
37790                   int64add(H[1], H[1], b);
37791                   int64add(H[2], H[2], c);
37792                   int64add(H[3], H[3], d);
37793                   int64add(H[4], H[4], e);
37794                   int64add(H[5], H[5], f);
37795                   int64add(H[6], H[6], g);
37796                   int64add(H[7], H[7], h);
37797                 }
37798
37799                 //represent the hash as an array of 32-bit dwords
37800                 for (i = 0; i < 8; i += 1) {
37801                   hash[2 * i] = H[i].h;
37802                   hash[2 * i + 1] = H[i].l;
37803                 }
37804                 return hash;
37805               }
37806
37807               //A constructor for 64-bit numbers
37808
37809               function int64(h, l) {
37810                 this.h = h;
37811                 this.l = l;
37812                 //this.toString = int64toString;
37813               }
37814
37815               //Copies src into dst, assuming both are 64-bit numbers
37816
37817               function int64copy(dst, src) {
37818                 dst.h = src.h;
37819                 dst.l = src.l;
37820               }
37821
37822               //Right-rotates a 64-bit number by shift
37823               //Won't handle cases of shift>=32
37824               //The function revrrot() is for that
37825
37826               function int64rrot(dst, x, shift) {
37827                 dst.l = (x.l >>> shift) | (x.h << (32 - shift));
37828                 dst.h = (x.h >>> shift) | (x.l << (32 - shift));
37829               }
37830
37831               //Reverses the dwords of the source and then rotates right by shift.
37832               //This is equivalent to rotation by 32+shift
37833
37834               function int64revrrot(dst, x, shift) {
37835                 dst.l = (x.h >>> shift) | (x.l << (32 - shift));
37836                 dst.h = (x.l >>> shift) | (x.h << (32 - shift));
37837               }
37838
37839               //Bitwise-shifts right a 64-bit number by shift
37840               //Won't handle shift>=32, but it's never needed in SHA512
37841
37842               function int64shr(dst, x, shift) {
37843                 dst.l = (x.l >>> shift) | (x.h << (32 - shift));
37844                 dst.h = (x.h >>> shift);
37845               }
37846
37847               //Adds two 64-bit numbers
37848               //Like the original implementation, does not rely on 32-bit operations
37849
37850               function int64add(dst, x, y) {
37851                 var w0 = (x.l & 0xffff) + (y.l & 0xffff);
37852                 var w1 = (x.l >>> 16) + (y.l >>> 16) + (w0 >>> 16);
37853                 var w2 = (x.h & 0xffff) + (y.h & 0xffff) + (w1 >>> 16);
37854                 var w3 = (x.h >>> 16) + (y.h >>> 16) + (w2 >>> 16);
37855                 dst.l = (w0 & 0xffff) | (w1 << 16);
37856                 dst.h = (w2 & 0xffff) | (w3 << 16);
37857               }
37858
37859               //Same, except with 4 addends. Works faster than adding them one by one.
37860
37861               function int64add4(dst, a, b, c, d) {
37862                 var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff);
37863                 var w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (w0 >>> 16);
37864                 var w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (w1 >>> 16);
37865                 var w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (w2 >>> 16);
37866                 dst.l = (w0 & 0xffff) | (w1 << 16);
37867                 dst.h = (w2 & 0xffff) | (w3 << 16);
37868               }
37869
37870               //Same, except with 5 addends
37871
37872               function int64add5(dst, a, b, c, d, e) {
37873                 var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff) + (e.l & 0xffff),
37874                   w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (e.l >>> 16) + (w0 >>> 16),
37875                   w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (e.h & 0xffff) + (w1 >>> 16),
37876                   w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (e.h >>> 16) + (w2 >>> 16);
37877                 dst.l = (w0 & 0xffff) | (w1 << 16);
37878                 dst.h = (w2 & 0xffff) | (w3 << 16);
37879               }
37880             },
37881             /**
37882              * @class Hashes.RMD160
37883              * @constructor
37884              * @param {Object} [config]
37885              *
37886              * A JavaScript implementation of the RIPEMD-160 Algorithm
37887              * Version 2.2 Copyright Jeremy Lin, Paul Johnston 2000 - 2009.
37888              * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
37889              * See http://pajhome.org.uk/crypt/md5 for details.
37890              * Also http://www.ocf.berkeley.edu/~jjlin/jsotp/
37891              */
37892             RMD160: function(options) {
37893               /**
37894                * Private properties configuration variables. You may need to tweak these to be compatible with
37895                * the server-side, but the defaults work in most cases.
37896                * @see this.setUpperCase() method
37897                * @see this.setPad() method
37898                */
37899               var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false,
37900                 /* hexadecimal output case format. false - lowercase; true - uppercase  */
37901                 b64pad = (options && typeof options.pad === 'string') ? options.pa : '=',
37902                 /* base-64 pad character. Default '=' for strict RFC compliance   */
37903                 utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true,
37904                 /* enable/disable utf8 encoding */
37905                 rmd160_r1 = [
37906                   0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
37907                   7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8,
37908                   3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12,
37909                   1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2,
37910                   4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13
37911                 ],
37912                 rmd160_r2 = [
37913                   5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12,
37914                   6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2,
37915                   15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13,
37916                   8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14,
37917                   12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11
37918                 ],
37919                 rmd160_s1 = [
37920                   11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8,
37921                   7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12,
37922                   11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5,
37923                   11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12,
37924                   9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6
37925                 ],
37926                 rmd160_s2 = [
37927                   8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6,
37928                   9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11,
37929                   9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5,
37930                   15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8,
37931                   8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11
37932                 ];
37933
37934               /* privileged (public) methods */
37935               this.hex = function(s) {
37936                 return rstr2hex(rstr(s));
37937               };
37938               this.b64 = function(s) {
37939                 return rstr2b64(rstr(s), b64pad);
37940               };
37941               this.any = function(s, e) {
37942                 return rstr2any(rstr(s), e);
37943               };
37944               this.raw = function(s) {
37945                 return rstr(s);
37946               };
37947               this.hex_hmac = function(k, d) {
37948                 return rstr2hex(rstr_hmac(k, d));
37949               };
37950               this.b64_hmac = function(k, d) {
37951                 return rstr2b64(rstr_hmac(k, d), b64pad);
37952               };
37953               this.any_hmac = function(k, d, e) {
37954                 return rstr2any(rstr_hmac(k, d), e);
37955               };
37956               /**
37957                * Perform a simple self-test to see if the VM is working
37958                * @return {String} Hexadecimal hash sample
37959                * @public
37960                */
37961               this.vm_test = function() {
37962                 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
37963               };
37964               /**
37965                * @description Enable/disable uppercase hexadecimal returned string
37966                * @param {boolean}
37967                * @return {Object} this
37968                * @public
37969                */
37970               this.setUpperCase = function(a) {
37971                 if (typeof a === 'boolean') {
37972                   hexcase = a;
37973                 }
37974                 return this;
37975               };
37976               /**
37977                * @description Defines a base64 pad string
37978                * @param {string} Pad
37979                * @return {Object} this
37980                * @public
37981                */
37982               this.setPad = function(a) {
37983                 if (typeof a !== 'undefined') {
37984                   b64pad = a;
37985                 }
37986                 return this;
37987               };
37988               /**
37989                * @description Defines a base64 pad string
37990                * @param {boolean}
37991                * @return {Object} this
37992                * @public
37993                */
37994               this.setUTF8 = function(a) {
37995                 if (typeof a === 'boolean') {
37996                   utf8 = a;
37997                 }
37998                 return this;
37999               };
38000
38001               /* private methods */
38002
38003               /**
38004                * Calculate the rmd160 of a raw string
38005                */
38006
38007               function rstr(s) {
38008                 s = (utf8) ? utf8Encode(s) : s;
38009                 return binl2rstr(binl(rstr2binl(s), s.length * 8));
38010               }
38011
38012               /**
38013                * Calculate the HMAC-rmd160 of a key and some data (raw strings)
38014                */
38015
38016               function rstr_hmac(key, data) {
38017                 key = (utf8) ? utf8Encode(key) : key;
38018                 data = (utf8) ? utf8Encode(data) : data;
38019                 var i, hash,
38020                   bkey = rstr2binl(key),
38021                   ipad = Array(16),
38022                   opad = Array(16);
38023
38024                 if (bkey.length > 16) {
38025                   bkey = binl(bkey, key.length * 8);
38026                 }
38027
38028                 for (i = 0; i < 16; i += 1) {
38029                   ipad[i] = bkey[i] ^ 0x36363636;
38030                   opad[i] = bkey[i] ^ 0x5C5C5C5C;
38031                 }
38032                 hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
38033                 return binl2rstr(binl(opad.concat(hash), 512 + 160));
38034               }
38035
38036               /**
38037                * Convert an array of little-endian words to a string
38038                */
38039
38040               function binl2rstr(input) {
38041                 var i, output = '',
38042                   l = input.length * 32;
38043                 for (i = 0; i < l; i += 8) {
38044                   output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xFF);
38045                 }
38046                 return output;
38047               }
38048
38049               /**
38050                * Calculate the RIPE-MD160 of an array of little-endian words, and a bit length.
38051                */
38052
38053               function binl(x, len) {
38054                 var T, j, i, l,
38055                   h0 = 0x67452301,
38056                   h1 = 0xefcdab89,
38057                   h2 = 0x98badcfe,
38058                   h3 = 0x10325476,
38059                   h4 = 0xc3d2e1f0,
38060                   A1, B1, C1, D1, E1,
38061                   A2, B2, C2, D2, E2;
38062
38063                 /* append padding */
38064                 x[len >> 5] |= 0x80 << (len % 32);
38065                 x[(((len + 64) >>> 9) << 4) + 14] = len;
38066                 l = x.length;
38067
38068                 for (i = 0; i < l; i += 16) {
38069                   A1 = A2 = h0;
38070                   B1 = B2 = h1;
38071                   C1 = C2 = h2;
38072                   D1 = D2 = h3;
38073                   E1 = E2 = h4;
38074                   for (j = 0; j <= 79; j += 1) {
38075                     T = safe_add(A1, rmd160_f(j, B1, C1, D1));
38076                     T = safe_add(T, x[i + rmd160_r1[j]]);
38077                     T = safe_add(T, rmd160_K1(j));
38078                     T = safe_add(bit_rol(T, rmd160_s1[j]), E1);
38079                     A1 = E1;
38080                     E1 = D1;
38081                     D1 = bit_rol(C1, 10);
38082                     C1 = B1;
38083                     B1 = T;
38084                     T = safe_add(A2, rmd160_f(79 - j, B2, C2, D2));
38085                     T = safe_add(T, x[i + rmd160_r2[j]]);
38086                     T = safe_add(T, rmd160_K2(j));
38087                     T = safe_add(bit_rol(T, rmd160_s2[j]), E2);
38088                     A2 = E2;
38089                     E2 = D2;
38090                     D2 = bit_rol(C2, 10);
38091                     C2 = B2;
38092                     B2 = T;
38093                   }
38094
38095                   T = safe_add(h1, safe_add(C1, D2));
38096                   h1 = safe_add(h2, safe_add(D1, E2));
38097                   h2 = safe_add(h3, safe_add(E1, A2));
38098                   h3 = safe_add(h4, safe_add(A1, B2));
38099                   h4 = safe_add(h0, safe_add(B1, C2));
38100                   h0 = T;
38101                 }
38102                 return [h0, h1, h2, h3, h4];
38103               }
38104
38105               // specific algorithm methods
38106
38107               function rmd160_f(j, x, y, z) {
38108                 return (0 <= j && j <= 15) ? (x ^ y ^ z) :
38109                   (16 <= j && j <= 31) ? (x & y) | (~x & z) :
38110                   (32 <= j && j <= 47) ? (x | ~y) ^ z :
38111                   (48 <= j && j <= 63) ? (x & z) | (y & ~z) :
38112                   (64 <= j && j <= 79) ? x ^ (y | ~z) :
38113                   'rmd160_f: j out of range';
38114               }
38115
38116               function rmd160_K1(j) {
38117                 return (0 <= j && j <= 15) ? 0x00000000 :
38118                   (16 <= j && j <= 31) ? 0x5a827999 :
38119                   (32 <= j && j <= 47) ? 0x6ed9eba1 :
38120                   (48 <= j && j <= 63) ? 0x8f1bbcdc :
38121                   (64 <= j && j <= 79) ? 0xa953fd4e :
38122                   'rmd160_K1: j out of range';
38123               }
38124
38125               function rmd160_K2(j) {
38126                 return (0 <= j && j <= 15) ? 0x50a28be6 :
38127                   (16 <= j && j <= 31) ? 0x5c4dd124 :
38128                   (32 <= j && j <= 47) ? 0x6d703ef3 :
38129                   (48 <= j && j <= 63) ? 0x7a6d76e9 :
38130                   (64 <= j && j <= 79) ? 0x00000000 :
38131                   'rmd160_K2: j out of range';
38132               }
38133             }
38134           };
38135
38136           // exposes Hashes
38137           (function(window, undefined$1) {
38138             var freeExports = false;
38139             {
38140               freeExports = exports;
38141               if (exports && typeof commonjsGlobal === 'object' && commonjsGlobal && commonjsGlobal === commonjsGlobal.global) {
38142                 window = commonjsGlobal;
38143               }
38144             }
38145
38146             if (typeof undefined$1 === 'function' && typeof undefined$1.amd === 'object' && undefined$1.amd) {
38147               // define as an anonymous module, so, through path mapping, it can be aliased
38148               undefined$1(function() {
38149                 return Hashes;
38150               });
38151             } else if (freeExports) {
38152               // in Node.js or RingoJS v0.8.0+
38153               if ( module && module.exports === freeExports) {
38154                 module.exports = Hashes;
38155               }
38156               // in Narwhal or RingoJS v0.7.0-
38157               else {
38158                 freeExports.Hashes = Hashes;
38159               }
38160             } else {
38161               // in a browser or Rhino
38162               window.Hashes = Hashes;
38163             }
38164           }(this));
38165         }()); // IIFE
38166         });
38167
38168         var immutable = extend$2;
38169
38170         var hasOwnProperty$3 = Object.prototype.hasOwnProperty;
38171
38172         function extend$2() {
38173             var target = {};
38174
38175             for (var i = 0; i < arguments.length; i++) {
38176                 var source = arguments[i];
38177
38178                 for (var key in source) {
38179                     if (hasOwnProperty$3.call(source, key)) {
38180                         target[key] = source[key];
38181                     }
38182                 }
38183             }
38184
38185             return target
38186         }
38187
38188         var sha1 = new hashes.SHA1();
38189
38190         var ohauth = {};
38191
38192         ohauth.qsString = function(obj) {
38193             return Object.keys(obj).sort().map(function(key) {
38194                 return ohauth.percentEncode(key) + '=' +
38195                     ohauth.percentEncode(obj[key]);
38196             }).join('&');
38197         };
38198
38199         ohauth.stringQs = function(str) {
38200             return str.split('&').filter(function (pair) {
38201                 return pair !== '';
38202             }).reduce(function(obj, pair){
38203                 var parts = pair.split('=');
38204                 obj[decodeURIComponent(parts[0])] = (null === parts[1]) ?
38205                     '' : decodeURIComponent(parts[1]);
38206                 return obj;
38207             }, {});
38208         };
38209
38210         ohauth.rawxhr = function(method, url, data, headers, callback) {
38211             var xhr = new XMLHttpRequest(),
38212                 twoHundred = /^20\d$/;
38213             xhr.onreadystatechange = function() {
38214                 if (4 === xhr.readyState && 0 !== xhr.status) {
38215                     if (twoHundred.test(xhr.status)) callback(null, xhr);
38216                     else return callback(xhr, null);
38217                 }
38218             };
38219             xhr.onerror = function(e) { return callback(e, null); };
38220             xhr.open(method, url, true);
38221             for (var h in headers) xhr.setRequestHeader(h, headers[h]);
38222             xhr.send(data);
38223             return xhr;
38224         };
38225
38226         ohauth.xhr = function(method, url, auth, data, options, callback) {
38227             var headers = (options && options.header) || {
38228                 'Content-Type': 'application/x-www-form-urlencoded'
38229             };
38230             headers.Authorization = 'OAuth ' + ohauth.authHeader(auth);
38231             return ohauth.rawxhr(method, url, data, headers, callback);
38232         };
38233
38234         ohauth.nonce = function() {
38235             for (var o = ''; o.length < 6;) {
38236                 o += '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'[Math.floor(Math.random() * 61)];
38237             }
38238             return o;
38239         };
38240
38241         ohauth.authHeader = function(obj) {
38242             return Object.keys(obj).sort().map(function(key) {
38243                 return encodeURIComponent(key) + '="' + encodeURIComponent(obj[key]) + '"';
38244             }).join(', ');
38245         };
38246
38247         ohauth.timestamp = function() { return ~~((+new Date()) / 1000); };
38248
38249         ohauth.percentEncode = function(s) {
38250             return encodeURIComponent(s)
38251                 .replace(/\!/g, '%21').replace(/\'/g, '%27')
38252                 .replace(/\*/g, '%2A').replace(/\(/g, '%28').replace(/\)/g, '%29');
38253         };
38254
38255         ohauth.baseString = function(method, url, params) {
38256             if (params.oauth_signature) delete params.oauth_signature;
38257             return [
38258                 method,
38259                 ohauth.percentEncode(url),
38260                 ohauth.percentEncode(ohauth.qsString(params))].join('&');
38261         };
38262
38263         ohauth.signature = function(oauth_secret, token_secret, baseString) {
38264             return sha1.b64_hmac(
38265                 ohauth.percentEncode(oauth_secret) + '&' +
38266                 ohauth.percentEncode(token_secret),
38267                 baseString);
38268         };
38269
38270         /**
38271          * Takes an options object for configuration (consumer_key,
38272          * consumer_secret, version, signature_method, token, token_secret)
38273          * and returns a function that generates the Authorization header
38274          * for given data.
38275          *
38276          * The returned function takes these parameters:
38277          * - method: GET/POST/...
38278          * - uri: full URI with protocol, port, path and query string
38279          * - extra_params: any extra parameters (that are passed in the POST data),
38280          *   can be an object or a from-urlencoded string.
38281          *
38282          * Returned function returns full OAuth header with "OAuth" string in it.
38283          */
38284
38285         ohauth.headerGenerator = function(options) {
38286             options = options || {};
38287             var consumer_key = options.consumer_key || '',
38288                 consumer_secret = options.consumer_secret || '',
38289                 signature_method = options.signature_method || 'HMAC-SHA1',
38290                 version = options.version || '1.0',
38291                 token = options.token || '',
38292                 token_secret = options.token_secret || '';
38293
38294             return function(method, uri, extra_params) {
38295                 method = method.toUpperCase();
38296                 if (typeof extra_params === 'string' && extra_params.length > 0) {
38297                     extra_params = ohauth.stringQs(extra_params);
38298                 }
38299
38300                 var uri_parts = uri.split('?', 2),
38301                 base_uri = uri_parts[0];
38302
38303                 var query_params = uri_parts.length === 2 ?
38304                     ohauth.stringQs(uri_parts[1]) : {};
38305
38306                 var oauth_params = {
38307                     oauth_consumer_key: consumer_key,
38308                     oauth_signature_method: signature_method,
38309                     oauth_version: version,
38310                     oauth_timestamp: ohauth.timestamp(),
38311                     oauth_nonce: ohauth.nonce()
38312                 };
38313
38314                 if (token) oauth_params.oauth_token = token;
38315
38316                 var all_params = immutable({}, oauth_params, query_params, extra_params),
38317                     base_str = ohauth.baseString(method, base_uri, all_params);
38318
38319                 oauth_params.oauth_signature = ohauth.signature(consumer_secret, token_secret, base_str);
38320
38321                 return 'OAuth ' + ohauth.authHeader(oauth_params);
38322             };
38323         };
38324
38325         var ohauth_1 = ohauth;
38326
38327         var resolveUrl$1 = createCommonjsModule(function (module, exports) {
38328         // Copyright 2014 Simon Lydell
38329         // X11 (“MIT”) Licensed. (See LICENSE.)
38330
38331         void (function(root, factory) {
38332           {
38333             module.exports = factory();
38334           }
38335         }(commonjsGlobal, function() {
38336
38337           function resolveUrl(/* ...urls */) {
38338             var numUrls = arguments.length;
38339
38340             if (numUrls === 0) {
38341               throw new Error("resolveUrl requires at least one argument; got none.")
38342             }
38343
38344             var base = document.createElement("base");
38345             base.href = arguments[0];
38346
38347             if (numUrls === 1) {
38348               return base.href
38349             }
38350
38351             var head = document.getElementsByTagName("head")[0];
38352             head.insertBefore(base, head.firstChild);
38353
38354             var a = document.createElement("a");
38355             var resolved;
38356
38357             for (var index = 1; index < numUrls; index++) {
38358               a.href = arguments[index];
38359               resolved = a.href;
38360               base.href = resolved;
38361             }
38362
38363             head.removeChild(base);
38364
38365             return resolved
38366           }
38367
38368           return resolveUrl
38369
38370         }));
38371         });
38372
38373         var assign$1 = make_assign();
38374         var create$8 = make_create();
38375         var trim = make_trim();
38376         var Global = (typeof window !== 'undefined' ? window : commonjsGlobal);
38377
38378         var util = {
38379                 assign: assign$1,
38380                 create: create$8,
38381                 trim: trim,
38382                 bind: bind$3,
38383                 slice: slice$5,
38384                 each: each,
38385                 map: map$5,
38386                 pluck: pluck,
38387                 isList: isList,
38388                 isFunction: isFunction$2,
38389                 isObject: isObject$2,
38390                 Global: Global
38391         };
38392
38393         function make_assign() {
38394                 if (Object.assign) {
38395                         return Object.assign
38396                 } else {
38397                         return function shimAssign(obj, props1, props2, etc) {
38398                                 for (var i = 1; i < arguments.length; i++) {
38399                                         each(Object(arguments[i]), function(val, key) {
38400                                                 obj[key] = val;
38401                                         });
38402                                 }                       
38403                                 return obj
38404                         }
38405                 }
38406         }
38407
38408         function make_create() {
38409                 if (Object.create) {
38410                         return function create(obj, assignProps1, assignProps2, etc) {
38411                                 var assignArgsList = slice$5(arguments, 1);
38412                                 return assign$1.apply(this, [Object.create(obj)].concat(assignArgsList))
38413                         }
38414                 } else {
38415                         function F() {} // eslint-disable-line no-inner-declarations
38416                         return function create(obj, assignProps1, assignProps2, etc) {
38417                                 var assignArgsList = slice$5(arguments, 1);
38418                                 F.prototype = obj;
38419                                 return assign$1.apply(this, [new F()].concat(assignArgsList))
38420                         }
38421                 }
38422         }
38423
38424         function make_trim() {
38425                 if (String.prototype.trim) {
38426                         return function trim(str) {
38427                                 return String.prototype.trim.call(str)
38428                         }
38429                 } else {
38430                         return function trim(str) {
38431                                 return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '')
38432                         }
38433                 }
38434         }
38435
38436         function bind$3(obj, fn) {
38437                 return function() {
38438                         return fn.apply(obj, Array.prototype.slice.call(arguments, 0))
38439                 }
38440         }
38441
38442         function slice$5(arr, index) {
38443                 return Array.prototype.slice.call(arr, index || 0)
38444         }
38445
38446         function each(obj, fn) {
38447                 pluck(obj, function(val, key) {
38448                         fn(val, key);
38449                         return false
38450                 });
38451         }
38452
38453         function map$5(obj, fn) {
38454                 var res = (isList(obj) ? [] : {});
38455                 pluck(obj, function(v, k) {
38456                         res[k] = fn(v, k);
38457                         return false
38458                 });
38459                 return res
38460         }
38461
38462         function pluck(obj, fn) {
38463                 if (isList(obj)) {
38464                         for (var i=0; i<obj.length; i++) {
38465                                 if (fn(obj[i], i)) {
38466                                         return obj[i]
38467                                 }
38468                         }
38469                 } else {
38470                         for (var key in obj) {
38471                                 if (obj.hasOwnProperty(key)) {
38472                                         if (fn(obj[key], key)) {
38473                                                 return obj[key]
38474                                         }
38475                                 }
38476                         }
38477                 }
38478         }
38479
38480         function isList(val) {
38481                 return (val != null && typeof val != 'function' && typeof val.length == 'number')
38482         }
38483
38484         function isFunction$2(val) {
38485                 return val && {}.toString.call(val) === '[object Function]'
38486         }
38487
38488         function isObject$2(val) {
38489                 return val && {}.toString.call(val) === '[object Object]'
38490         }
38491
38492         var slice$6 = util.slice;
38493         var pluck$1 = util.pluck;
38494         var each$1 = util.each;
38495         var bind$4 = util.bind;
38496         var create$9 = util.create;
38497         var isList$1 = util.isList;
38498         var isFunction$3 = util.isFunction;
38499         var isObject$3 = util.isObject;
38500
38501         var storeEngine = {
38502                 createStore: createStore
38503         };
38504
38505         var storeAPI = {
38506                 version: '2.0.12',
38507                 enabled: false,
38508                 
38509                 // get returns the value of the given key. If that value
38510                 // is undefined, it returns optionalDefaultValue instead.
38511                 get: function(key, optionalDefaultValue) {
38512                         var data = this.storage.read(this._namespacePrefix + key);
38513                         return this._deserialize(data, optionalDefaultValue)
38514                 },
38515
38516                 // set will store the given value at key and returns value.
38517                 // Calling set with value === undefined is equivalent to calling remove.
38518                 set: function(key, value) {
38519                         if (value === undefined) {
38520                                 return this.remove(key)
38521                         }
38522                         this.storage.write(this._namespacePrefix + key, this._serialize(value));
38523                         return value
38524                 },
38525
38526                 // remove deletes the key and value stored at the given key.
38527                 remove: function(key) {
38528                         this.storage.remove(this._namespacePrefix + key);
38529                 },
38530
38531                 // each will call the given callback once for each key-value pair
38532                 // in this store.
38533                 each: function(callback) {
38534                         var self = this;
38535                         this.storage.each(function(val, namespacedKey) {
38536                                 callback.call(self, self._deserialize(val), (namespacedKey || '').replace(self._namespaceRegexp, ''));
38537                         });
38538                 },
38539
38540                 // clearAll will remove all the stored key-value pairs in this store.
38541                 clearAll: function() {
38542                         this.storage.clearAll();
38543                 },
38544
38545                 // additional functionality that can't live in plugins
38546                 // ---------------------------------------------------
38547
38548                 // hasNamespace returns true if this store instance has the given namespace.
38549                 hasNamespace: function(namespace) {
38550                         return (this._namespacePrefix == '__storejs_'+namespace+'_')
38551                 },
38552
38553                 // createStore creates a store.js instance with the first
38554                 // functioning storage in the list of storage candidates,
38555                 // and applies the the given mixins to the instance.
38556                 createStore: function() {
38557                         return createStore.apply(this, arguments)
38558                 },
38559                 
38560                 addPlugin: function(plugin) {
38561                         this._addPlugin(plugin);
38562                 },
38563                 
38564                 namespace: function(namespace) {
38565                         return createStore(this.storage, this.plugins, namespace)
38566                 }
38567         };
38568
38569         function _warn() {
38570                 var _console = (typeof console == 'undefined' ? null : console);
38571                 if (!_console) { return }
38572                 var fn = (_console.warn ? _console.warn : _console.log);
38573                 fn.apply(_console, arguments);
38574         }
38575
38576         function createStore(storages, plugins, namespace) {
38577                 if (!namespace) {
38578                         namespace = '';
38579                 }
38580                 if (storages && !isList$1(storages)) {
38581                         storages = [storages];
38582                 }
38583                 if (plugins && !isList$1(plugins)) {
38584                         plugins = [plugins];
38585                 }
38586
38587                 var namespacePrefix = (namespace ? '__storejs_'+namespace+'_' : '');
38588                 var namespaceRegexp = (namespace ? new RegExp('^'+namespacePrefix) : null);
38589                 var legalNamespaces = /^[a-zA-Z0-9_\-]*$/; // alpha-numeric + underscore and dash
38590                 if (!legalNamespaces.test(namespace)) {
38591                         throw new Error('store.js namespaces can only have alphanumerics + underscores and dashes')
38592                 }
38593                 
38594                 var _privateStoreProps = {
38595                         _namespacePrefix: namespacePrefix,
38596                         _namespaceRegexp: namespaceRegexp,
38597
38598                         _testStorage: function(storage) {
38599                                 try {
38600                                         var testStr = '__storejs__test__';
38601                                         storage.write(testStr, testStr);
38602                                         var ok = (storage.read(testStr) === testStr);
38603                                         storage.remove(testStr);
38604                                         return ok
38605                                 } catch(e) {
38606                                         return false
38607                                 }
38608                         },
38609
38610                         _assignPluginFnProp: function(pluginFnProp, propName) {
38611                                 var oldFn = this[propName];
38612                                 this[propName] = function pluginFn() {
38613                                         var args = slice$6(arguments, 0);
38614                                         var self = this;
38615
38616                                         // super_fn calls the old function which was overwritten by
38617                                         // this mixin.
38618                                         function super_fn() {
38619                                                 if (!oldFn) { return }
38620                                                 each$1(arguments, function(arg, i) {
38621                                                         args[i] = arg;
38622                                                 });
38623                                                 return oldFn.apply(self, args)
38624                                         }
38625
38626                                         // Give mixing function access to super_fn by prefixing all mixin function
38627                                         // arguments with super_fn.
38628                                         var newFnArgs = [super_fn].concat(args);
38629
38630                                         return pluginFnProp.apply(self, newFnArgs)
38631                                 };
38632                         },
38633
38634                         _serialize: function(obj) {
38635                                 return JSON.stringify(obj)
38636                         },
38637
38638                         _deserialize: function(strVal, defaultVal) {
38639                                 if (!strVal) { return defaultVal }
38640                                 // It is possible that a raw string value has been previously stored
38641                                 // in a storage without using store.js, meaning it will be a raw
38642                                 // string value instead of a JSON serialized string. By defaulting
38643                                 // to the raw string value in case of a JSON parse error, we allow
38644                                 // for past stored values to be forwards-compatible with store.js
38645                                 var val = '';
38646                                 try { val = JSON.parse(strVal); }
38647                                 catch(e) { val = strVal; }
38648
38649                                 return (val !== undefined ? val : defaultVal)
38650                         },
38651                         
38652                         _addStorage: function(storage) {
38653                                 if (this.enabled) { return }
38654                                 if (this._testStorage(storage)) {
38655                                         this.storage = storage;
38656                                         this.enabled = true;
38657                                 }
38658                         },
38659
38660                         _addPlugin: function(plugin) {
38661                                 var self = this;
38662
38663                                 // If the plugin is an array, then add all plugins in the array.
38664                                 // This allows for a plugin to depend on other plugins.
38665                                 if (isList$1(plugin)) {
38666                                         each$1(plugin, function(plugin) {
38667                                                 self._addPlugin(plugin);
38668                                         });
38669                                         return
38670                                 }
38671
38672                                 // Keep track of all plugins we've seen so far, so that we
38673                                 // don't add any of them twice.
38674                                 var seenPlugin = pluck$1(this.plugins, function(seenPlugin) {
38675                                         return (plugin === seenPlugin)
38676                                 });
38677                                 if (seenPlugin) {
38678                                         return
38679                                 }
38680                                 this.plugins.push(plugin);
38681
38682                                 // Check that the plugin is properly formed
38683                                 if (!isFunction$3(plugin)) {
38684                                         throw new Error('Plugins must be function values that return objects')
38685                                 }
38686
38687                                 var pluginProperties = plugin.call(this);
38688                                 if (!isObject$3(pluginProperties)) {
38689                                         throw new Error('Plugins must return an object of function properties')
38690                                 }
38691
38692                                 // Add the plugin function properties to this store instance.
38693                                 each$1(pluginProperties, function(pluginFnProp, propName) {
38694                                         if (!isFunction$3(pluginFnProp)) {
38695                                                 throw new Error('Bad plugin property: '+propName+' from plugin '+plugin.name+'. Plugins should only return functions.')
38696                                         }
38697                                         self._assignPluginFnProp(pluginFnProp, propName);
38698                                 });
38699                         },
38700                         
38701                         // Put deprecated properties in the private API, so as to not expose it to accidential
38702                         // discovery through inspection of the store object.
38703                         
38704                         // Deprecated: addStorage
38705                         addStorage: function(storage) {
38706                                 _warn('store.addStorage(storage) is deprecated. Use createStore([storages])');
38707                                 this._addStorage(storage);
38708                         }
38709                 };
38710
38711                 var store = create$9(_privateStoreProps, storeAPI, {
38712                         plugins: []
38713                 });
38714                 store.raw = {};
38715                 each$1(store, function(prop, propName) {
38716                         if (isFunction$3(prop)) {
38717                                 store.raw[propName] = bind$4(store, prop);                      
38718                         }
38719                 });
38720                 each$1(storages, function(storage) {
38721                         store._addStorage(storage);
38722                 });
38723                 each$1(plugins, function(plugin) {
38724                         store._addPlugin(plugin);
38725                 });
38726                 return store
38727         }
38728
38729         var Global$1 = util.Global;
38730
38731         var localStorage_1 = {
38732                 name: 'localStorage',
38733                 read: read,
38734                 write: write,
38735                 each: each$2,
38736                 remove: remove$2,
38737                 clearAll: clearAll,
38738         };
38739
38740         function localStorage$1() {
38741                 return Global$1.localStorage
38742         }
38743
38744         function read(key) {
38745                 return localStorage$1().getItem(key)
38746         }
38747
38748         function write(key, data) {
38749                 return localStorage$1().setItem(key, data)
38750         }
38751
38752         function each$2(fn) {
38753                 for (var i = localStorage$1().length - 1; i >= 0; i--) {
38754                         var key = localStorage$1().key(i);
38755                         fn(read(key), key);
38756                 }
38757         }
38758
38759         function remove$2(key) {
38760                 return localStorage$1().removeItem(key)
38761         }
38762
38763         function clearAll() {
38764                 return localStorage$1().clear()
38765         }
38766
38767         // oldFF-globalStorage provides storage for Firefox
38768         // versions 6 and 7, where no localStorage, etc
38769         // is available.
38770
38771
38772         var Global$2 = util.Global;
38773
38774         var oldFFGlobalStorage = {
38775                 name: 'oldFF-globalStorage',
38776                 read: read$1,
38777                 write: write$1,
38778                 each: each$3,
38779                 remove: remove$3,
38780                 clearAll: clearAll$1,
38781         };
38782
38783         var globalStorage = Global$2.globalStorage;
38784
38785         function read$1(key) {
38786                 return globalStorage[key]
38787         }
38788
38789         function write$1(key, data) {
38790                 globalStorage[key] = data;
38791         }
38792
38793         function each$3(fn) {
38794                 for (var i = globalStorage.length - 1; i >= 0; i--) {
38795                         var key = globalStorage.key(i);
38796                         fn(globalStorage[key], key);
38797                 }
38798         }
38799
38800         function remove$3(key) {
38801                 return globalStorage.removeItem(key)
38802         }
38803
38804         function clearAll$1() {
38805                 each$3(function(key, _) {
38806                         delete globalStorage[key];
38807                 });
38808         }
38809
38810         // oldIE-userDataStorage provides storage for Internet Explorer
38811         // versions 6 and 7, where no localStorage, sessionStorage, etc
38812         // is available.
38813
38814
38815         var Global$3 = util.Global;
38816
38817         var oldIEUserDataStorage = {
38818                 name: 'oldIE-userDataStorage',
38819                 write: write$2,
38820                 read: read$2,
38821                 each: each$4,
38822                 remove: remove$4,
38823                 clearAll: clearAll$2,
38824         };
38825
38826         var storageName = 'storejs';
38827         var doc = Global$3.document;
38828         var _withStorageEl = _makeIEStorageElFunction();
38829         var disable = (Global$3.navigator ? Global$3.navigator.userAgent : '').match(/ (MSIE 8|MSIE 9|MSIE 10)\./); // MSIE 9.x, MSIE 10.x
38830
38831         function write$2(unfixedKey, data) {
38832                 if (disable) { return }
38833                 var fixedKey = fixKey(unfixedKey);
38834                 _withStorageEl(function(storageEl) {
38835                         storageEl.setAttribute(fixedKey, data);
38836                         storageEl.save(storageName);
38837                 });
38838         }
38839
38840         function read$2(unfixedKey) {
38841                 if (disable) { return }
38842                 var fixedKey = fixKey(unfixedKey);
38843                 var res = null;
38844                 _withStorageEl(function(storageEl) {
38845                         res = storageEl.getAttribute(fixedKey);
38846                 });
38847                 return res
38848         }
38849
38850         function each$4(callback) {
38851                 _withStorageEl(function(storageEl) {
38852                         var attributes = storageEl.XMLDocument.documentElement.attributes;
38853                         for (var i=attributes.length-1; i>=0; i--) {
38854                                 var attr = attributes[i];
38855                                 callback(storageEl.getAttribute(attr.name), attr.name);
38856                         }
38857                 });
38858         }
38859
38860         function remove$4(unfixedKey) {
38861                 var fixedKey = fixKey(unfixedKey);
38862                 _withStorageEl(function(storageEl) {
38863                         storageEl.removeAttribute(fixedKey);
38864                         storageEl.save(storageName);
38865                 });
38866         }
38867
38868         function clearAll$2() {
38869                 _withStorageEl(function(storageEl) {
38870                         var attributes = storageEl.XMLDocument.documentElement.attributes;
38871                         storageEl.load(storageName);
38872                         for (var i=attributes.length-1; i>=0; i--) {
38873                                 storageEl.removeAttribute(attributes[i].name);
38874                         }
38875                         storageEl.save(storageName);
38876                 });
38877         }
38878
38879         // Helpers
38880         //////////
38881
38882         // In IE7, keys cannot start with a digit or contain certain chars.
38883         // See https://github.com/marcuswestin/store.js/issues/40
38884         // See https://github.com/marcuswestin/store.js/issues/83
38885         var forbiddenCharsRegex = new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]", "g");
38886         function fixKey(key) {
38887                 return key.replace(/^\d/, '___$&').replace(forbiddenCharsRegex, '___')
38888         }
38889
38890         function _makeIEStorageElFunction() {
38891                 if (!doc || !doc.documentElement || !doc.documentElement.addBehavior) {
38892                         return null
38893                 }
38894                 var scriptTag = 'script',
38895                         storageOwner,
38896                         storageContainer,
38897                         storageEl;
38898
38899                 // Since #userData storage applies only to specific paths, we need to
38900                 // somehow link our data to a specific path.  We choose /favicon.ico
38901                 // as a pretty safe option, since all browsers already make a request to
38902                 // this URL anyway and being a 404 will not hurt us here.  We wrap an
38903                 // iframe pointing to the favicon in an ActiveXObject(htmlfile) object
38904                 // (see: http://msdn.microsoft.com/en-us/library/aa752574(v=VS.85).aspx)
38905                 // since the iframe access rules appear to allow direct access and
38906                 // manipulation of the document element, even for a 404 page.  This
38907                 // document can be used instead of the current document (which would
38908                 // have been limited to the current path) to perform #userData storage.
38909                 try {
38910                         /* global ActiveXObject */
38911                         storageContainer = new ActiveXObject('htmlfile');
38912                         storageContainer.open();
38913                         storageContainer.write('<'+scriptTag+'>document.w=window</'+scriptTag+'><iframe src="/favicon.ico"></iframe>');
38914                         storageContainer.close();
38915                         storageOwner = storageContainer.w.frames[0].document;
38916                         storageEl = storageOwner.createElement('div');
38917                 } catch(e) {
38918                         // somehow ActiveXObject instantiation failed (perhaps some special
38919                         // security settings or otherwse), fall back to per-path storage
38920                         storageEl = doc.createElement('div');
38921                         storageOwner = doc.body;
38922                 }
38923
38924                 return function(storeFunction) {
38925                         var args = [].slice.call(arguments, 0);
38926                         args.unshift(storageEl);
38927                         // See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx
38928                         // and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx
38929                         storageOwner.appendChild(storageEl);
38930                         storageEl.addBehavior('#default#userData');
38931                         storageEl.load(storageName);
38932                         storeFunction.apply(this, args);
38933                         storageOwner.removeChild(storageEl);
38934                         return
38935                 }
38936         }
38937
38938         // cookieStorage is useful Safari private browser mode, where localStorage
38939         // doesn't work but cookies do. This implementation is adopted from
38940         // https://developer.mozilla.org/en-US/docs/Web/API/Storage/LocalStorage
38941
38942
38943         var Global$4 = util.Global;
38944         var trim$1 = util.trim;
38945
38946         var cookieStorage = {
38947                 name: 'cookieStorage',
38948                 read: read$3,
38949                 write: write$3,
38950                 each: each$5,
38951                 remove: remove$5,
38952                 clearAll: clearAll$3,
38953         };
38954
38955         var doc$1 = Global$4.document;
38956
38957         function read$3(key) {
38958                 if (!key || !_has(key)) { return null }
38959                 var regexpStr = "(?:^|.*;\\s*)" +
38960                         escape(key).replace(/[\-\.\+\*]/g, "\\$&") +
38961                         "\\s*\\=\\s*((?:[^;](?!;))*[^;]?).*";
38962                 return unescape(doc$1.cookie.replace(new RegExp(regexpStr), "$1"))
38963         }
38964
38965         function each$5(callback) {
38966                 var cookies = doc$1.cookie.split(/; ?/g);
38967                 for (var i = cookies.length - 1; i >= 0; i--) {
38968                         if (!trim$1(cookies[i])) {
38969                                 continue
38970                         }
38971                         var kvp = cookies[i].split('=');
38972                         var key = unescape(kvp[0]);
38973                         var val = unescape(kvp[1]);
38974                         callback(val, key);
38975                 }
38976         }
38977
38978         function write$3(key, data) {
38979                 if(!key) { return }
38980                 doc$1.cookie = escape(key) + "=" + escape(data) + "; expires=Tue, 19 Jan 2038 03:14:07 GMT; path=/";
38981         }
38982
38983         function remove$5(key) {
38984                 if (!key || !_has(key)) {
38985                         return
38986                 }
38987                 doc$1.cookie = escape(key) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/";
38988         }
38989
38990         function clearAll$3() {
38991                 each$5(function(_, key) {
38992                         remove$5(key);
38993                 });
38994         }
38995
38996         function _has(key) {
38997                 return (new RegExp("(?:^|;\\s*)" + escape(key).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=")).test(doc$1.cookie)
38998         }
38999
39000         var Global$5 = util.Global;
39001
39002         var sessionStorage_1 = {
39003                 name: 'sessionStorage',
39004                 read: read$4,
39005                 write: write$4,
39006                 each: each$6,
39007                 remove: remove$6,
39008                 clearAll: clearAll$4
39009         };
39010
39011         function sessionStorage() {
39012                 return Global$5.sessionStorage
39013         }
39014
39015         function read$4(key) {
39016                 return sessionStorage().getItem(key)
39017         }
39018
39019         function write$4(key, data) {
39020                 return sessionStorage().setItem(key, data)
39021         }
39022
39023         function each$6(fn) {
39024                 for (var i = sessionStorage().length - 1; i >= 0; i--) {
39025                         var key = sessionStorage().key(i);
39026                         fn(read$4(key), key);
39027                 }
39028         }
39029
39030         function remove$6(key) {
39031                 return sessionStorage().removeItem(key)
39032         }
39033
39034         function clearAll$4() {
39035                 return sessionStorage().clear()
39036         }
39037
39038         // memoryStorage is a useful last fallback to ensure that the store
39039         // is functions (meaning store.get(), store.set(), etc will all function).
39040         // However, stored values will not persist when the browser navigates to
39041         // a new page or reloads the current page.
39042
39043         var memoryStorage_1 = {
39044                 name: 'memoryStorage',
39045                 read: read$5,
39046                 write: write$5,
39047                 each: each$7,
39048                 remove: remove$7,
39049                 clearAll: clearAll$5,
39050         };
39051
39052         var memoryStorage = {};
39053
39054         function read$5(key) {
39055                 return memoryStorage[key]
39056         }
39057
39058         function write$5(key, data) {
39059                 memoryStorage[key] = data;
39060         }
39061
39062         function each$7(callback) {
39063                 for (var key in memoryStorage) {
39064                         if (memoryStorage.hasOwnProperty(key)) {
39065                                 callback(memoryStorage[key], key);
39066                         }
39067                 }
39068         }
39069
39070         function remove$7(key) {
39071                 delete memoryStorage[key];
39072         }
39073
39074         function clearAll$5(key) {
39075                 memoryStorage = {};
39076         }
39077
39078         var all = [
39079                 // Listed in order of usage preference
39080                 localStorage_1,
39081                 oldFFGlobalStorage,
39082                 oldIEUserDataStorage,
39083                 cookieStorage,
39084                 sessionStorage_1,
39085                 memoryStorage_1
39086         ];
39087
39088         /* eslint-disable */
39089
39090         //  json2.js
39091         //  2016-10-28
39092         //  Public Domain.
39093         //  NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
39094         //  See http://www.JSON.org/js.html
39095         //  This code should be minified before deployment.
39096         //  See http://javascript.crockford.com/jsmin.html
39097
39098         //  USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
39099         //  NOT CONTROL.
39100
39101         //  This file creates a global JSON object containing two methods: stringify
39102         //  and parse. This file provides the ES5 JSON capability to ES3 systems.
39103         //  If a project might run on IE8 or earlier, then this file should be included.
39104         //  This file does nothing on ES5 systems.
39105
39106         //      JSON.stringify(value, replacer, space)
39107         //          value       any JavaScript value, usually an object or array.
39108         //          replacer    an optional parameter that determines how object
39109         //                      values are stringified for objects. It can be a
39110         //                      function or an array of strings.
39111         //          space       an optional parameter that specifies the indentation
39112         //                      of nested structures. If it is omitted, the text will
39113         //                      be packed without extra whitespace. If it is a number,
39114         //                      it will specify the number of spaces to indent at each
39115         //                      level. If it is a string (such as "\t" or "&nbsp;"),
39116         //                      it contains the characters used to indent at each level.
39117         //          This method produces a JSON text from a JavaScript value.
39118         //          When an object value is found, if the object contains a toJSON
39119         //          method, its toJSON method will be called and the result will be
39120         //          stringified. A toJSON method does not serialize: it returns the
39121         //          value represented by the name/value pair that should be serialized,
39122         //          or undefined if nothing should be serialized. The toJSON method
39123         //          will be passed the key associated with the value, and this will be
39124         //          bound to the value.
39125
39126         //          For example, this would serialize Dates as ISO strings.
39127
39128         //              Date.prototype.toJSON = function (key) {
39129         //                  function f(n) {
39130         //                      // Format integers to have at least two digits.
39131         //                      return (n < 10)
39132         //                          ? "0" + n
39133         //                          : n;
39134         //                  }
39135         //                  return this.getUTCFullYear()   + "-" +
39136         //                       f(this.getUTCMonth() + 1) + "-" +
39137         //                       f(this.getUTCDate())      + "T" +
39138         //                       f(this.getUTCHours())     + ":" +
39139         //                       f(this.getUTCMinutes())   + ":" +
39140         //                       f(this.getUTCSeconds())   + "Z";
39141         //              };
39142
39143         //          You can provide an optional replacer method. It will be passed the
39144         //          key and value of each member, with this bound to the containing
39145         //          object. The value that is returned from your method will be
39146         //          serialized. If your method returns undefined, then the member will
39147         //          be excluded from the serialization.
39148
39149         //          If the replacer parameter is an array of strings, then it will be
39150         //          used to select the members to be serialized. It filters the results
39151         //          such that only members with keys listed in the replacer array are
39152         //          stringified.
39153
39154         //          Values that do not have JSON representations, such as undefined or
39155         //          functions, will not be serialized. Such values in objects will be
39156         //          dropped; in arrays they will be replaced with null. You can use
39157         //          a replacer function to replace those with JSON values.
39158
39159         //          JSON.stringify(undefined) returns undefined.
39160
39161         //          The optional space parameter produces a stringification of the
39162         //          value that is filled with line breaks and indentation to make it
39163         //          easier to read.
39164
39165         //          If the space parameter is a non-empty string, then that string will
39166         //          be used for indentation. If the space parameter is a number, then
39167         //          the indentation will be that many spaces.
39168
39169         //          Example:
39170
39171         //          text = JSON.stringify(["e", {pluribus: "unum"}]);
39172         //          // text is '["e",{"pluribus":"unum"}]'
39173
39174         //          text = JSON.stringify(["e", {pluribus: "unum"}], null, "\t");
39175         //          // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
39176
39177         //          text = JSON.stringify([new Date()], function (key, value) {
39178         //              return this[key] instanceof Date
39179         //                  ? "Date(" + this[key] + ")"
39180         //                  : value;
39181         //          });
39182         //          // text is '["Date(---current time---)"]'
39183
39184         //      JSON.parse(text, reviver)
39185         //          This method parses a JSON text to produce an object or array.
39186         //          It can throw a SyntaxError exception.
39187
39188         //          The optional reviver parameter is a function that can filter and
39189         //          transform the results. It receives each of the keys and values,
39190         //          and its return value is used instead of the original value.
39191         //          If it returns what it received, then the structure is not modified.
39192         //          If it returns undefined then the member is deleted.
39193
39194         //          Example:
39195
39196         //          // Parse the text. Values that look like ISO date strings will
39197         //          // be converted to Date objects.
39198
39199         //          myData = JSON.parse(text, function (key, value) {
39200         //              var a;
39201         //              if (typeof value === "string") {
39202         //                  a =
39203         //   /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
39204         //                  if (a) {
39205         //                      return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
39206         //                          +a[5], +a[6]));
39207         //                  }
39208         //              }
39209         //              return value;
39210         //          });
39211
39212         //          myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
39213         //              var d;
39214         //              if (typeof value === "string" &&
39215         //                      value.slice(0, 5) === "Date(" &&
39216         //                      value.slice(-1) === ")") {
39217         //                  d = new Date(value.slice(5, -1));
39218         //                  if (d) {
39219         //                      return d;
39220         //                  }
39221         //              }
39222         //              return value;
39223         //          });
39224
39225         //  This is a reference implementation. You are free to copy, modify, or
39226         //  redistribute.
39227
39228         /*jslint
39229             eval, for, this
39230         */
39231
39232         /*property
39233             JSON, apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
39234             getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
39235             lastIndex, length, parse, prototype, push, replace, slice, stringify,
39236             test, toJSON, toString, valueOf
39237         */
39238
39239
39240         // Create a JSON object only if one does not already exist. We create the
39241         // methods in a closure to avoid creating global variables.
39242
39243         if (typeof JSON !== "object") {
39244             JSON = {};
39245         }
39246
39247         (function () {
39248
39249             var rx_one = /^[\],:{}\s]*$/;
39250             var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g;
39251             var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
39252             var rx_four = /(?:^|:|,)(?:\s*\[)+/g;
39253             var rx_escapable = /[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
39254             var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
39255
39256             function f(n) {
39257                 // Format integers to have at least two digits.
39258                 return n < 10
39259                     ? "0" + n
39260                     : n;
39261             }
39262
39263             function this_value() {
39264                 return this.valueOf();
39265             }
39266
39267             if (typeof Date.prototype.toJSON !== "function") {
39268
39269                 Date.prototype.toJSON = function () {
39270
39271                     return isFinite(this.valueOf())
39272                         ? this.getUTCFullYear() + "-" +
39273                                 f(this.getUTCMonth() + 1) + "-" +
39274                                 f(this.getUTCDate()) + "T" +
39275                                 f(this.getUTCHours()) + ":" +
39276                                 f(this.getUTCMinutes()) + ":" +
39277                                 f(this.getUTCSeconds()) + "Z"
39278                         : null;
39279                 };
39280
39281                 Boolean.prototype.toJSON = this_value;
39282                 Number.prototype.toJSON = this_value;
39283                 String.prototype.toJSON = this_value;
39284             }
39285
39286             var gap;
39287             var indent;
39288             var meta;
39289             var rep;
39290
39291
39292             function quote(string) {
39293
39294         // If the string contains no control characters, no quote characters, and no
39295         // backslash characters, then we can safely slap some quotes around it.
39296         // Otherwise we must also replace the offending characters with safe escape
39297         // sequences.
39298
39299                 rx_escapable.lastIndex = 0;
39300                 return rx_escapable.test(string)
39301                     ? "\"" + string.replace(rx_escapable, function (a) {
39302                         var c = meta[a];
39303                         return typeof c === "string"
39304                             ? c
39305                             : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
39306                     }) + "\""
39307                     : "\"" + string + "\"";
39308             }
39309
39310
39311             function str(key, holder) {
39312
39313         // Produce a string from holder[key].
39314
39315                 var i;          // The loop counter.
39316                 var k;          // The member key.
39317                 var v;          // The member value.
39318                 var length;
39319                 var mind = gap;
39320                 var partial;
39321                 var value = holder[key];
39322
39323         // If the value has a toJSON method, call it to obtain a replacement value.
39324
39325                 if (value && typeof value === "object" &&
39326                         typeof value.toJSON === "function") {
39327                     value = value.toJSON(key);
39328                 }
39329
39330         // If we were called with a replacer function, then call the replacer to
39331         // obtain a replacement value.
39332
39333                 if (typeof rep === "function") {
39334                     value = rep.call(holder, key, value);
39335                 }
39336
39337         // What happens next depends on the value's type.
39338
39339                 switch (typeof value) {
39340                 case "string":
39341                     return quote(value);
39342
39343                 case "number":
39344
39345         // JSON numbers must be finite. Encode non-finite numbers as null.
39346
39347                     return isFinite(value)
39348                         ? String(value)
39349                         : "null";
39350
39351                 case "boolean":
39352                 case "null":
39353
39354         // If the value is a boolean or null, convert it to a string. Note:
39355         // typeof null does not produce "null". The case is included here in
39356         // the remote chance that this gets fixed someday.
39357
39358                     return String(value);
39359
39360         // If the type is "object", we might be dealing with an object or an array or
39361         // null.
39362
39363                 case "object":
39364
39365         // Due to a specification blunder in ECMAScript, typeof null is "object",
39366         // so watch out for that case.
39367
39368                     if (!value) {
39369                         return "null";
39370                     }
39371
39372         // Make an array to hold the partial results of stringifying this object value.
39373
39374                     gap += indent;
39375                     partial = [];
39376
39377         // Is the value an array?
39378
39379                     if (Object.prototype.toString.apply(value) === "[object Array]") {
39380
39381         // The value is an array. Stringify every element. Use null as a placeholder
39382         // for non-JSON values.
39383
39384                         length = value.length;
39385                         for (i = 0; i < length; i += 1) {
39386                             partial[i] = str(i, value) || "null";
39387                         }
39388
39389         // Join all of the elements together, separated with commas, and wrap them in
39390         // brackets.
39391
39392                         v = partial.length === 0
39393                             ? "[]"
39394                             : gap
39395                                 ? "[\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "]"
39396                                 : "[" + partial.join(",") + "]";
39397                         gap = mind;
39398                         return v;
39399                     }
39400
39401         // If the replacer is an array, use it to select the members to be stringified.
39402
39403                     if (rep && typeof rep === "object") {
39404                         length = rep.length;
39405                         for (i = 0; i < length; i += 1) {
39406                             if (typeof rep[i] === "string") {
39407                                 k = rep[i];
39408                                 v = str(k, value);
39409                                 if (v) {
39410                                     partial.push(quote(k) + (
39411                                         gap
39412                                             ? ": "
39413                                             : ":"
39414                                     ) + v);
39415                                 }
39416                             }
39417                         }
39418                     } else {
39419
39420         // Otherwise, iterate through all of the keys in the object.
39421
39422                         for (k in value) {
39423                             if (Object.prototype.hasOwnProperty.call(value, k)) {
39424                                 v = str(k, value);
39425                                 if (v) {
39426                                     partial.push(quote(k) + (
39427                                         gap
39428                                             ? ": "
39429                                             : ":"
39430                                     ) + v);
39431                                 }
39432                             }
39433                         }
39434                     }
39435
39436         // Join all of the member texts together, separated with commas,
39437         // and wrap them in braces.
39438
39439                     v = partial.length === 0
39440                         ? "{}"
39441                         : gap
39442                             ? "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}"
39443                             : "{" + partial.join(",") + "}";
39444                     gap = mind;
39445                     return v;
39446                 }
39447             }
39448
39449         // If the JSON object does not yet have a stringify method, give it one.
39450
39451             if (typeof JSON.stringify !== "function") {
39452                 meta = {    // table of character substitutions
39453                     "\b": "\\b",
39454                     "\t": "\\t",
39455                     "\n": "\\n",
39456                     "\f": "\\f",
39457                     "\r": "\\r",
39458                     "\"": "\\\"",
39459                     "\\": "\\\\"
39460                 };
39461                 JSON.stringify = function (value, replacer, space) {
39462
39463         // The stringify method takes a value and an optional replacer, and an optional
39464         // space parameter, and returns a JSON text. The replacer can be a function
39465         // that can replace values, or an array of strings that will select the keys.
39466         // A default replacer method can be provided. Use of the space parameter can
39467         // produce text that is more easily readable.
39468
39469                     var i;
39470                     gap = "";
39471                     indent = "";
39472
39473         // If the space parameter is a number, make an indent string containing that
39474         // many spaces.
39475
39476                     if (typeof space === "number") {
39477                         for (i = 0; i < space; i += 1) {
39478                             indent += " ";
39479                         }
39480
39481         // If the space parameter is a string, it will be used as the indent string.
39482
39483                     } else if (typeof space === "string") {
39484                         indent = space;
39485                     }
39486
39487         // If there is a replacer, it must be a function or an array.
39488         // Otherwise, throw an error.
39489
39490                     rep = replacer;
39491                     if (replacer && typeof replacer !== "function" &&
39492                             (typeof replacer !== "object" ||
39493                             typeof replacer.length !== "number")) {
39494                         throw new Error("JSON.stringify");
39495                     }
39496
39497         // Make a fake root object containing our value under the key of "".
39498         // Return the result of stringifying the value.
39499
39500                     return str("", {"": value});
39501                 };
39502             }
39503
39504
39505         // If the JSON object does not yet have a parse method, give it one.
39506
39507             if (typeof JSON.parse !== "function") {
39508                 JSON.parse = function (text, reviver) {
39509
39510         // The parse method takes a text and an optional reviver function, and returns
39511         // a JavaScript value if the text is a valid JSON text.
39512
39513                     var j;
39514
39515                     function walk(holder, key) {
39516
39517         // The walk method is used to recursively walk the resulting structure so
39518         // that modifications can be made.
39519
39520                         var k;
39521                         var v;
39522                         var value = holder[key];
39523                         if (value && typeof value === "object") {
39524                             for (k in value) {
39525                                 if (Object.prototype.hasOwnProperty.call(value, k)) {
39526                                     v = walk(value, k);
39527                                     if (v !== undefined) {
39528                                         value[k] = v;
39529                                     } else {
39530                                         delete value[k];
39531                                     }
39532                                 }
39533                             }
39534                         }
39535                         return reviver.call(holder, key, value);
39536                     }
39537
39538
39539         // Parsing happens in four stages. In the first stage, we replace certain
39540         // Unicode characters with escape sequences. JavaScript handles many characters
39541         // incorrectly, either silently deleting them, or treating them as line endings.
39542
39543                     text = String(text);
39544                     rx_dangerous.lastIndex = 0;
39545                     if (rx_dangerous.test(text)) {
39546                         text = text.replace(rx_dangerous, function (a) {
39547                             return "\\u" +
39548                                     ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
39549                         });
39550                     }
39551
39552         // In the second stage, we run the text against regular expressions that look
39553         // for non-JSON patterns. We are especially concerned with "()" and "new"
39554         // because they can cause invocation, and "=" because it can cause mutation.
39555         // But just to be safe, we want to reject all unexpected forms.
39556
39557         // We split the second stage into 4 regexp operations in order to work around
39558         // crippling inefficiencies in IE's and Safari's regexp engines. First we
39559         // replace the JSON backslash pairs with "@" (a non-JSON character). Second, we
39560         // replace all simple value tokens with "]" characters. Third, we delete all
39561         // open brackets that follow a colon or comma or that begin the text. Finally,
39562         // we look to see that the remaining characters are only whitespace or "]" or
39563         // "," or ":" or "{" or "}". If that is so, then the text is safe for eval.
39564
39565                     if (
39566                         rx_one.test(
39567                             text
39568                                 .replace(rx_two, "@")
39569                                 .replace(rx_three, "]")
39570                                 .replace(rx_four, "")
39571                         )
39572                     ) {
39573
39574         // In the third stage we use the eval function to compile the text into a
39575         // JavaScript structure. The "{" operator is subject to a syntactic ambiguity
39576         // in JavaScript: it can begin a block or an object literal. We wrap the text
39577         // in parens to eliminate the ambiguity.
39578
39579                         j = eval("(" + text + ")");
39580
39581         // In the optional fourth stage, we recursively walk the new structure, passing
39582         // each name/value pair to a reviver function for possible transformation.
39583
39584                         return (typeof reviver === "function")
39585                             ? walk({"": j}, "")
39586                             : j;
39587                     }
39588
39589         // If the text is not JSON parseable, then a SyntaxError is thrown.
39590
39591                     throw new SyntaxError("JSON.parse");
39592                 };
39593             }
39594         }());
39595
39596         var json2 = json2Plugin;
39597
39598         function json2Plugin() {
39599                 
39600                 return {}
39601         }
39602
39603         var plugins = [json2];
39604
39605         var store_legacy = storeEngine.createStore(all, plugins);
39606
39607         // # osm-auth
39608         //
39609         // This code is only compatible with IE10+ because the [XDomainRequest](http://bit.ly/LfO7xo)
39610         // object, IE<10's idea of [CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing),
39611         // does not support custom headers, which this uses everywhere.
39612         var osmAuth = function(o) {
39613
39614             var oauth = {};
39615
39616             // authenticated users will also have a request token secret, but it's
39617             // not used in transactions with the server
39618             oauth.authenticated = function() {
39619                 return !!(token('oauth_token') && token('oauth_token_secret'));
39620             };
39621
39622             oauth.logout = function() {
39623                 token('oauth_token', '');
39624                 token('oauth_token_secret', '');
39625                 token('oauth_request_token_secret', '');
39626                 return oauth;
39627             };
39628
39629             // TODO: detect lack of click event
39630             oauth.authenticate = function(callback) {
39631                 if (oauth.authenticated()) return callback();
39632
39633                 oauth.logout();
39634
39635                 // ## Getting a request token
39636                 var params = timenonce(getAuth(o)),
39637                     url = o.url + '/oauth/request_token';
39638
39639                 params.oauth_signature = ohauth_1.signature(
39640                     o.oauth_secret, '',
39641                     ohauth_1.baseString('POST', url, params));
39642
39643                 if (!o.singlepage) {
39644                     // Create a 600x550 popup window in the center of the screen
39645                     var w = 600, h = 550,
39646                         settings = [
39647                             ['width', w], ['height', h],
39648                             ['left', screen.width / 2 - w / 2],
39649                             ['top', screen.height / 2 - h / 2]].map(function(x) {
39650                                 return x.join('=');
39651                             }).join(','),
39652                         popup = window.open('about:blank', 'oauth_window', settings);
39653                 }
39654
39655                 // Request a request token. When this is complete, the popup
39656                 // window is redirected to OSM's authorization page.
39657                 ohauth_1.xhr('POST', url, params, null, {}, reqTokenDone);
39658                 o.loading();
39659
39660                 function reqTokenDone(err, xhr) {
39661                     o.done();
39662                     if (err) return callback(err);
39663                     var resp = ohauth_1.stringQs(xhr.response);
39664                     token('oauth_request_token_secret', resp.oauth_token_secret);
39665                     var authorize_url = o.url + '/oauth/authorize?' + ohauth_1.qsString({
39666                         oauth_token: resp.oauth_token,
39667                         oauth_callback: resolveUrl$1(o.landing)
39668                     });
39669
39670                     if (o.singlepage) {
39671                         location.href = authorize_url;
39672                     } else {
39673                         popup.location = authorize_url;
39674                     }
39675                 }
39676
39677                 // Called by a function in a landing page, in the popup window. The
39678                 // window closes itself.
39679                 window.authComplete = function(token) {
39680                     var oauth_token = ohauth_1.stringQs(token.split('?')[1]);
39681                     get_access_token(oauth_token.oauth_token);
39682                     delete window.authComplete;
39683                 };
39684
39685                 // ## Getting an request token
39686                 //
39687                 // At this point we have an `oauth_token`, brought in from a function
39688                 // call on a landing page popup.
39689                 function get_access_token(oauth_token) {
39690                     var url = o.url + '/oauth/access_token',
39691                         params = timenonce(getAuth(o)),
39692                         request_token_secret = token('oauth_request_token_secret');
39693                     params.oauth_token = oauth_token;
39694                     params.oauth_signature = ohauth_1.signature(
39695                         o.oauth_secret,
39696                         request_token_secret,
39697                         ohauth_1.baseString('POST', url, params));
39698
39699                     // ## Getting an access token
39700                     //
39701                     // The final token required for authentication. At this point
39702                     // we have a `request token secret`
39703                     ohauth_1.xhr('POST', url, params, null, {}, accessTokenDone);
39704                     o.loading();
39705                 }
39706
39707                 function accessTokenDone(err, xhr) {
39708                     o.done();
39709                     if (err) return callback(err);
39710                     var access_token = ohauth_1.stringQs(xhr.response);
39711                     token('oauth_token', access_token.oauth_token);
39712                     token('oauth_token_secret', access_token.oauth_token_secret);
39713                     callback(null, oauth);
39714                 }
39715             };
39716
39717             oauth.bootstrapToken = function(oauth_token, callback) {
39718                 // ## Getting an request token
39719                 // At this point we have an `oauth_token`, brought in from a function
39720                 // call on a landing page popup.
39721                 function get_access_token(oauth_token) {
39722                     var url = o.url + '/oauth/access_token',
39723                         params = timenonce(getAuth(o)),
39724                         request_token_secret = token('oauth_request_token_secret');
39725                     params.oauth_token = oauth_token;
39726                     params.oauth_signature = ohauth_1.signature(
39727                         o.oauth_secret,
39728                         request_token_secret,
39729                         ohauth_1.baseString('POST', url, params));
39730
39731                     // ## Getting an access token
39732                     // The final token required for authentication. At this point
39733                     // we have a `request token secret`
39734                     ohauth_1.xhr('POST', url, params, null, {}, accessTokenDone);
39735                     o.loading();
39736                 }
39737
39738                 function accessTokenDone(err, xhr) {
39739                     o.done();
39740                     if (err) return callback(err);
39741                     var access_token = ohauth_1.stringQs(xhr.response);
39742                     token('oauth_token', access_token.oauth_token);
39743                     token('oauth_token_secret', access_token.oauth_token_secret);
39744                     callback(null, oauth);
39745                 }
39746
39747                 get_access_token(oauth_token);
39748             };
39749
39750             // # xhr
39751             //
39752             // A single XMLHttpRequest wrapper that does authenticated calls if the
39753             // user has logged in.
39754             oauth.xhr = function(options, callback) {
39755                 if (!oauth.authenticated()) {
39756                     if (o.auto) {
39757                         return oauth.authenticate(run);
39758                     } else {
39759                         callback('not authenticated', null);
39760                         return;
39761                     }
39762                 } else {
39763                     return run();
39764                 }
39765
39766                 function run() {
39767                     var params = timenonce(getAuth(o)),
39768                         oauth_token_secret = token('oauth_token_secret'),
39769                         url = (options.prefix !== false) ? o.url + options.path : options.path,
39770                         url_parts = url.replace(/#.*$/, '').split('?', 2),
39771                         base_url = url_parts[0],
39772                         query = (url_parts.length === 2) ? url_parts[1] : '';
39773
39774                     // https://tools.ietf.org/html/rfc5849#section-3.4.1.3.1
39775                     if ((!options.options || !options.options.header ||
39776                         options.options.header['Content-Type'] === 'application/x-www-form-urlencoded') &&
39777                         options.content) {
39778                         params = immutable(params, ohauth_1.stringQs(options.content));
39779                     }
39780
39781                     params.oauth_token = token('oauth_token');
39782                     params.oauth_signature = ohauth_1.signature(
39783                         o.oauth_secret,
39784                         oauth_token_secret,
39785                         ohauth_1.baseString(options.method, base_url, immutable(params, ohauth_1.stringQs(query)))
39786                     );
39787
39788                     return ohauth_1.xhr(options.method, url, params, options.content, options.options, done);
39789                 }
39790
39791                 function done(err, xhr) {
39792                     if (err) return callback(err);
39793                     else if (xhr.responseXML) return callback(err, xhr.responseXML);
39794                     else return callback(err, xhr.response);
39795                 }
39796             };
39797
39798             // pre-authorize this object, if we can just get a token and token_secret
39799             // from the start
39800             oauth.preauth = function(c) {
39801                 if (!c) return;
39802                 if (c.oauth_token) token('oauth_token', c.oauth_token);
39803                 if (c.oauth_token_secret) token('oauth_token_secret', c.oauth_token_secret);
39804                 return oauth;
39805             };
39806
39807             oauth.options = function(_) {
39808                 if (!arguments.length) return o;
39809
39810                 o = _;
39811                 o.url = o.url || 'https://www.openstreetmap.org';
39812                 o.landing = o.landing || 'land.html';
39813                 o.singlepage = o.singlepage || false;
39814
39815                 // Optional loading and loading-done functions for nice UI feedback.
39816                 // by default, no-ops
39817                 o.loading = o.loading || function() {};
39818                 o.done = o.done || function() {};
39819
39820                 return oauth.preauth(o);
39821             };
39822
39823             // 'stamp' an authentication object from `getAuth()`
39824             // with a [nonce](http://en.wikipedia.org/wiki/Cryptographic_nonce)
39825             // and timestamp
39826             function timenonce(o) {
39827                 o.oauth_timestamp = ohauth_1.timestamp();
39828                 o.oauth_nonce = ohauth_1.nonce();
39829                 return o;
39830             }
39831
39832             // get/set tokens. These are prefixed with the base URL so that `osm-auth`
39833             // can be used with multiple APIs and the keys in `localStorage`
39834             // will not clash
39835             var token;
39836
39837             if (store_legacy.enabled) {
39838                 token = function (x, y) {
39839                     if (arguments.length === 1) return store_legacy.get(o.url + x);
39840                     else if (arguments.length === 2) return store_legacy.set(o.url + x, y);
39841                 };
39842             } else {
39843                 var storage = {};
39844                 token = function (x, y) {
39845                     if (arguments.length === 1) return storage[o.url + x];
39846                     else if (arguments.length === 2) return storage[o.url + x] = y;
39847                 };
39848             }
39849
39850             // Get an authentication object. If you just add and remove properties
39851             // from a single object, you'll need to use `delete` to make sure that
39852             // it doesn't contain undesired properties for authentication
39853             function getAuth(o) {
39854                 return {
39855                     oauth_consumer_key: o.oauth_consumer_key,
39856                     oauth_signature_method: 'HMAC-SHA1'
39857                 };
39858             }
39859
39860             // potentially pre-authorize
39861             oauth.options(o);
39862
39863             return oauth;
39864         };
39865
39866         var JXON = new (function () {
39867           var
39868             sValueProp = 'keyValue', sAttributesProp = 'keyAttributes', sAttrPref = '@', /* you can customize these values */
39869             aCache = [], rIsNull = /^\s*$/, rIsBool = /^(?:true|false)$/i;
39870
39871           function parseText (sValue) {
39872             if (rIsNull.test(sValue)) { return null; }
39873             if (rIsBool.test(sValue)) { return sValue.toLowerCase() === 'true'; }
39874             if (isFinite(sValue)) { return parseFloat(sValue); }
39875             if (isFinite(Date.parse(sValue))) { return new Date(sValue); }
39876             return sValue;
39877           }
39878
39879           function EmptyTree () { }
39880           EmptyTree.prototype.toString = function () { return 'null'; };
39881           EmptyTree.prototype.valueOf = function () { return null; };
39882
39883           function objectify (vValue) {
39884             return vValue === null ? new EmptyTree() : vValue instanceof Object ? vValue : new vValue.constructor(vValue);
39885           }
39886
39887           function createObjTree (oParentNode, nVerb, bFreeze, bNesteAttr) {
39888             var
39889               nLevelStart = aCache.length, bChildren = oParentNode.hasChildNodes(),
39890               bAttributes = oParentNode.hasAttributes(), bHighVerb = Boolean(nVerb & 2);
39891
39892             var
39893               sProp, vContent, nLength = 0, sCollectedTxt = '',
39894               vResult = bHighVerb ? {} : /* put here the default value for empty nodes: */ true;
39895
39896             if (bChildren) {
39897               for (var oNode, nItem = 0; nItem < oParentNode.childNodes.length; nItem++) {
39898                 oNode = oParentNode.childNodes.item(nItem);
39899                 if (oNode.nodeType === 4) { sCollectedTxt += oNode.nodeValue; } /* nodeType is 'CDATASection' (4) */
39900                 else if (oNode.nodeType === 3) { sCollectedTxt += oNode.nodeValue.trim(); } /* nodeType is 'Text' (3) */
39901                 else if (oNode.nodeType === 1 && !oNode.prefix) { aCache.push(oNode); } /* nodeType is 'Element' (1) */
39902               }
39903             }
39904
39905             var nLevelEnd = aCache.length, vBuiltVal = parseText(sCollectedTxt);
39906
39907             if (!bHighVerb && (bChildren || bAttributes)) { vResult = nVerb === 0 ? objectify(vBuiltVal) : {}; }
39908
39909             for (var nElId = nLevelStart; nElId < nLevelEnd; nElId++) {
39910               sProp = aCache[nElId].nodeName.toLowerCase();
39911               vContent = createObjTree(aCache[nElId], nVerb, bFreeze, bNesteAttr);
39912               if (vResult.hasOwnProperty(sProp)) {
39913                 if (vResult[sProp].constructor !== Array) { vResult[sProp] = [vResult[sProp]]; }
39914                 vResult[sProp].push(vContent);
39915               } else {
39916                 vResult[sProp] = vContent;
39917                 nLength++;
39918               }
39919             }
39920
39921             if (bAttributes) {
39922               var
39923                 nAttrLen = oParentNode.attributes.length,
39924                 sAPrefix = bNesteAttr ? '' : sAttrPref, oAttrParent = bNesteAttr ? {} : vResult;
39925
39926               for (var oAttrib, nAttrib = 0; nAttrib < nAttrLen; nLength++, nAttrib++) {
39927                 oAttrib = oParentNode.attributes.item(nAttrib);
39928                 oAttrParent[sAPrefix + oAttrib.name.toLowerCase()] = parseText(oAttrib.value.trim());
39929               }
39930
39931               if (bNesteAttr) {
39932                 if (bFreeze) { Object.freeze(oAttrParent); }
39933                 vResult[sAttributesProp] = oAttrParent;
39934                 nLength -= nAttrLen - 1;
39935               }
39936             }
39937
39938             if (nVerb === 3 || (nVerb === 2 || nVerb === 1 && nLength > 0) && sCollectedTxt) {
39939               vResult[sValueProp] = vBuiltVal;
39940             } else if (!bHighVerb && nLength === 0 && sCollectedTxt) {
39941               vResult = vBuiltVal;
39942             }
39943
39944             if (bFreeze && (bHighVerb || nLength > 0)) { Object.freeze(vResult); }
39945
39946             aCache.length = nLevelStart;
39947
39948             return vResult;
39949           }
39950
39951           function loadObjTree (oXMLDoc, oParentEl, oParentObj) {
39952             var vValue, oChild;
39953
39954             if (oParentObj instanceof String || oParentObj instanceof Number || oParentObj instanceof Boolean) {
39955               oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toString())); /* verbosity level is 0 */
39956             } else if (oParentObj.constructor === Date) {
39957               oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toGMTString()));    
39958             }
39959
39960             for (var sName in oParentObj) {
39961               vValue = oParentObj[sName];
39962               if (isFinite(sName) || vValue instanceof Function) { continue; } /* verbosity level is 0 */
39963               if (sName === sValueProp) {
39964                 if (vValue !== null && vValue !== true) { oParentEl.appendChild(oXMLDoc.createTextNode(vValue.constructor === Date ? vValue.toGMTString() : String(vValue))); }
39965               } else if (sName === sAttributesProp) { /* verbosity level is 3 */
39966                 for (var sAttrib in vValue) { oParentEl.setAttribute(sAttrib, vValue[sAttrib]); }
39967               } else if (sName.charAt(0) === sAttrPref) {
39968                 oParentEl.setAttribute(sName.slice(1), vValue);
39969               } else if (vValue.constructor === Array) {
39970                 for (var nItem = 0; nItem < vValue.length; nItem++) {
39971                   oChild = oXMLDoc.createElement(sName);
39972                   loadObjTree(oXMLDoc, oChild, vValue[nItem]);
39973                   oParentEl.appendChild(oChild);
39974                 }
39975               } else {
39976                 oChild = oXMLDoc.createElement(sName);
39977                 if (vValue instanceof Object) {
39978                   loadObjTree(oXMLDoc, oChild, vValue);
39979                 } else if (vValue !== null && vValue !== true) {
39980                   oChild.appendChild(oXMLDoc.createTextNode(vValue.toString()));
39981                 }
39982                 oParentEl.appendChild(oChild);
39983              }
39984            }
39985           }
39986
39987           this.build = function (oXMLParent, nVerbosity /* optional */, bFreeze /* optional */, bNesteAttributes /* optional */) {
39988             var _nVerb = arguments.length > 1 && typeof nVerbosity === 'number' ? nVerbosity & 3 : /* put here the default verbosity level: */ 1;
39989             return createObjTree(oXMLParent, _nVerb, bFreeze || false, arguments.length > 3 ? bNesteAttributes : _nVerb === 3);    
39990           };
39991
39992           this.unbuild = function (oObjTree) {    
39993             var oNewDoc = document.implementation.createDocument('', '', null);
39994             loadObjTree(oNewDoc, oNewDoc, oObjTree);
39995             return oNewDoc;
39996           };
39997
39998           this.stringify = function (oObjTree) {
39999             return (new XMLSerializer()).serializeToString(JXON.unbuild(oObjTree));
40000           };
40001         })();
40002
40003         // var myObject = JXON.build(doc);
40004         // we got our javascript object! try: alert(JSON.stringify(myObject));
40005
40006         // var newDoc = JXON.unbuild(myObject);
40007         // we got our Document instance! try: alert((new XMLSerializer()).serializeToString(newDoc));
40008
40009         var tiler$5 = utilTiler();
40010         var dispatch$6 = dispatch('apiStatusChange', 'authLoading', 'authDone', 'change', 'loading', 'loaded', 'loadedNotes');
40011         var urlroot = 'https://www.openstreetmap.org';
40012         var oauth = osmAuth({
40013             url: urlroot,
40014             oauth_consumer_key: '5A043yRSEugj4DJ5TljuapfnrflWDte8jTOcWLlT',
40015             oauth_secret: 'aB3jKq1TRsCOUrfOIZ6oQMEDmv2ptV76PA54NGLL',
40016             loading: authLoading,
40017             done: authDone
40018         });
40019
40020         var _blacklists = ['.*\.google(apis)?\..*/(vt|kh)[\?/].*([xyz]=.*){3}.*'];
40021         var _tileCache = { toLoad: {}, loaded: {}, inflight: {}, seen: {}, rtree: new RBush() };
40022         var _noteCache = { toLoad: {}, loaded: {}, inflight: {}, inflightPost: {}, note: {}, closed: {}, rtree: new RBush() };
40023         var _userCache = { toLoad: {}, user: {} };
40024         var _cachedApiStatus;
40025         var _changeset = {};
40026
40027         var _deferred = new Set();
40028         var _connectionID = 1;
40029         var _tileZoom$3 = 16;
40030         var _noteZoom = 12;
40031         var _rateLimitError;
40032         var _userChangesets;
40033         var _userDetails;
40034         var _off;
40035
40036         // set a default but also load this from the API status
40037         var _maxWayNodes = 2000;
40038
40039
40040         function authLoading() {
40041             dispatch$6.call('authLoading');
40042         }
40043
40044
40045         function authDone() {
40046             dispatch$6.call('authDone');
40047         }
40048
40049
40050         function abortRequest$5(controllerOrXHR) {
40051             if (controllerOrXHR) {
40052                 controllerOrXHR.abort();
40053             }
40054         }
40055
40056
40057         function hasInflightRequests(cache) {
40058             return Object.keys(cache.inflight).length;
40059         }
40060
40061
40062         function abortUnwantedRequests$3(cache, visibleTiles) {
40063             Object.keys(cache.inflight).forEach(function(k) {
40064                 if (cache.toLoad[k]) return;
40065                 if (visibleTiles.find(function(tile) { return k === tile.id; })) return;
40066
40067                 abortRequest$5(cache.inflight[k]);
40068                 delete cache.inflight[k];
40069             });
40070         }
40071
40072
40073         function getLoc(attrs) {
40074             var lon = attrs.lon && attrs.lon.value;
40075             var lat = attrs.lat && attrs.lat.value;
40076             return [parseFloat(lon), parseFloat(lat)];
40077         }
40078
40079
40080         function getNodes(obj) {
40081             var elems = obj.getElementsByTagName('nd');
40082             var nodes = new Array(elems.length);
40083             for (var i = 0, l = elems.length; i < l; i++) {
40084                 nodes[i] = 'n' + elems[i].attributes.ref.value;
40085             }
40086             return nodes;
40087         }
40088
40089         function getNodesJSON(obj) {
40090             var elems = obj.nodes;
40091             var nodes = new Array(elems.length);
40092             for (var i = 0, l = elems.length; i < l; i++) {
40093                 nodes[i] = 'n' + elems[i];
40094             }
40095             return nodes;
40096         }
40097
40098         function getTags(obj) {
40099             var elems = obj.getElementsByTagName('tag');
40100             var tags = {};
40101             for (var i = 0, l = elems.length; i < l; i++) {
40102                 var attrs = elems[i].attributes;
40103                 tags[attrs.k.value] = attrs.v.value;
40104             }
40105
40106             return tags;
40107         }
40108
40109
40110         function getMembers(obj) {
40111             var elems = obj.getElementsByTagName('member');
40112             var members = new Array(elems.length);
40113             for (var i = 0, l = elems.length; i < l; i++) {
40114                 var attrs = elems[i].attributes;
40115                 members[i] = {
40116                     id: attrs.type.value[0] + attrs.ref.value,
40117                     type: attrs.type.value,
40118                     role: attrs.role.value
40119                 };
40120             }
40121             return members;
40122         }
40123
40124         function getMembersJSON(obj) {
40125             var elems = obj.members;
40126             var members = new Array(elems.length);
40127             for (var i = 0, l = elems.length; i < l; i++) {
40128                 var attrs = elems[i];
40129                 members[i] = {
40130                     id: attrs.type[0] + attrs.ref,
40131                     type: attrs.type,
40132                     role: attrs.role
40133                 };
40134             }
40135             return members;
40136         }
40137
40138         function getVisible(attrs) {
40139             return (!attrs.visible || attrs.visible.value !== 'false');
40140         }
40141
40142
40143         function parseComments(comments) {
40144             var parsedComments = [];
40145
40146             // for each comment
40147             for (var i = 0; i < comments.length; i++) {
40148                 var comment = comments[i];
40149                 if (comment.nodeName === 'comment') {
40150                     var childNodes = comment.childNodes;
40151                     var parsedComment = {};
40152
40153                     for (var j = 0; j < childNodes.length; j++) {
40154                         var node = childNodes[j];
40155                         var nodeName = node.nodeName;
40156                         if (nodeName === '#text') continue;
40157                         parsedComment[nodeName] = node.textContent;
40158
40159                         if (nodeName === 'uid') {
40160                             var uid = node.textContent;
40161                             if (uid && !_userCache.user[uid]) {
40162                                 _userCache.toLoad[uid] = true;
40163                             }
40164                         }
40165                     }
40166
40167                     if (parsedComment) {
40168                         parsedComments.push(parsedComment);
40169                     }
40170                 }
40171             }
40172             return parsedComments;
40173         }
40174
40175
40176         function encodeNoteRtree(note) {
40177             return {
40178                 minX: note.loc[0],
40179                 minY: note.loc[1],
40180                 maxX: note.loc[0],
40181                 maxY: note.loc[1],
40182                 data: note
40183             };
40184         }
40185
40186
40187         var jsonparsers = {
40188
40189             node: function nodeData(obj, uid) {
40190                 return new osmNode({
40191                     id:  uid,
40192                     visible: typeof obj.visible === 'boolean' ? obj.visible : true,
40193                     version: obj.version.toString(),
40194                     changeset: obj.changeset.toString(),
40195                     timestamp: obj.timestamp,
40196                     user: obj.user,
40197                     uid: obj.uid.toString(),
40198                     loc: [parseFloat(obj.lon), parseFloat(obj.lat)],
40199                     tags: obj.tags
40200                 });
40201             },
40202
40203             way: function wayData(obj, uid) {
40204                 return new osmWay({
40205                     id:  uid,
40206                     visible: typeof obj.visible === 'boolean' ? obj.visible : true,
40207                     version: obj.version.toString(),
40208                     changeset: obj.changeset.toString(),
40209                     timestamp: obj.timestamp,
40210                     user: obj.user,
40211                     uid: obj.uid.toString(),
40212                     tags: obj.tags,
40213                     nodes: getNodesJSON(obj)
40214                 });
40215             },
40216
40217             relation: function relationData(obj, uid) {
40218                 return new osmRelation({
40219                     id:  uid,
40220                     visible: typeof obj.visible === 'boolean' ? obj.visible : true,
40221                     version: obj.version.toString(),
40222                     changeset: obj.changeset.toString(),
40223                     timestamp: obj.timestamp,
40224                     user: obj.user,
40225                     uid: obj.uid.toString(),
40226                     tags: obj.tags,
40227                     members: getMembersJSON(obj)
40228                 });
40229             }
40230         };
40231
40232         function parseJSON(payload, callback, options) {
40233             options = Object.assign({ skipSeen: true }, options);
40234             if (!payload)  {
40235                 return callback({ message: 'No JSON', status: -1 });
40236             }
40237
40238             var json = payload;
40239             if (typeof json !== 'object')
40240                json = JSON.parse(payload);
40241
40242             if (!json.elements)
40243                 return callback({ message: 'No JSON', status: -1 });
40244
40245             var children = json.elements;
40246
40247             var handle = window.requestIdleCallback(function() {
40248                 var results = [];
40249                 var result;
40250                 for (var i = 0; i < children.length; i++) {
40251                     result = parseChild(children[i]);
40252                     if (result) results.push(result);
40253                 }
40254                 callback(null, results);
40255             });
40256
40257             _deferred.add(handle);
40258
40259             function parseChild(child) {
40260                 var parser = jsonparsers[child.type];
40261                 if (!parser) return null;
40262
40263                 var uid;
40264
40265                 uid = osmEntity.id.fromOSM(child.type, child.id);
40266                 if (options.skipSeen) {
40267                     if (_tileCache.seen[uid]) return null;  // avoid reparsing a "seen" entity
40268                     _tileCache.seen[uid] = true;
40269                 }
40270
40271                 return parser(child, uid);
40272             }
40273         }
40274
40275         var parsers = {
40276             node: function nodeData(obj, uid) {
40277                 var attrs = obj.attributes;
40278                 return new osmNode({
40279                     id: uid,
40280                     visible: getVisible(attrs),
40281                     version: attrs.version.value,
40282                     changeset: attrs.changeset && attrs.changeset.value,
40283                     timestamp: attrs.timestamp && attrs.timestamp.value,
40284                     user: attrs.user && attrs.user.value,
40285                     uid: attrs.uid && attrs.uid.value,
40286                     loc: getLoc(attrs),
40287                     tags: getTags(obj)
40288                 });
40289             },
40290
40291             way: function wayData(obj, uid) {
40292                 var attrs = obj.attributes;
40293                 return new osmWay({
40294                     id: uid,
40295                     visible: getVisible(attrs),
40296                     version: attrs.version.value,
40297                     changeset: attrs.changeset && attrs.changeset.value,
40298                     timestamp: attrs.timestamp && attrs.timestamp.value,
40299                     user: attrs.user && attrs.user.value,
40300                     uid: attrs.uid && attrs.uid.value,
40301                     tags: getTags(obj),
40302                     nodes: getNodes(obj),
40303                 });
40304             },
40305
40306             relation: function relationData(obj, uid) {
40307                 var attrs = obj.attributes;
40308                 return new osmRelation({
40309                     id: uid,
40310                     visible: getVisible(attrs),
40311                     version: attrs.version.value,
40312                     changeset: attrs.changeset && attrs.changeset.value,
40313                     timestamp: attrs.timestamp && attrs.timestamp.value,
40314                     user: attrs.user && attrs.user.value,
40315                     uid: attrs.uid && attrs.uid.value,
40316                     tags: getTags(obj),
40317                     members: getMembers(obj)
40318                 });
40319             },
40320
40321             note: function parseNote(obj, uid) {
40322                 var attrs = obj.attributes;
40323                 var childNodes = obj.childNodes;
40324                 var props = {};
40325
40326                 props.id = uid;
40327                 props.loc = getLoc(attrs);
40328
40329                 // if notes are coincident, move them apart slightly
40330                 var coincident = false;
40331                 var epsilon = 0.00001;
40332                 do {
40333                     if (coincident) {
40334                         props.loc = geoVecAdd(props.loc, [epsilon, epsilon]);
40335                     }
40336                     var bbox = geoExtent(props.loc).bbox();
40337                     coincident = _noteCache.rtree.search(bbox).length;
40338                 } while (coincident);
40339
40340                 // parse note contents
40341                 for (var i = 0; i < childNodes.length; i++) {
40342                     var node = childNodes[i];
40343                     var nodeName = node.nodeName;
40344                     if (nodeName === '#text') continue;
40345
40346                     // if the element is comments, parse the comments
40347                     if (nodeName === 'comments') {
40348                         props[nodeName] = parseComments(node.childNodes);
40349                     } else {
40350                         props[nodeName] = node.textContent;
40351                     }
40352                 }
40353
40354                 var note = new osmNote(props);
40355                 var item = encodeNoteRtree(note);
40356                 _noteCache.note[note.id] = note;
40357                 _noteCache.rtree.insert(item);
40358
40359                 return note;
40360             },
40361
40362             user: function parseUser(obj, uid) {
40363                 var attrs = obj.attributes;
40364                 var user = {
40365                     id: uid,
40366                     display_name: attrs.display_name && attrs.display_name.value,
40367                     account_created: attrs.account_created && attrs.account_created.value,
40368                     changesets_count: '0',
40369                     active_blocks: '0'
40370                 };
40371
40372                 var img = obj.getElementsByTagName('img');
40373                 if (img && img[0] && img[0].getAttribute('href')) {
40374                     user.image_url = img[0].getAttribute('href');
40375                 }
40376
40377                 var changesets = obj.getElementsByTagName('changesets');
40378                 if (changesets && changesets[0] && changesets[0].getAttribute('count')) {
40379                     user.changesets_count = changesets[0].getAttribute('count');
40380                 }
40381
40382                 var blocks = obj.getElementsByTagName('blocks');
40383                 if (blocks && blocks[0]) {
40384                     var received = blocks[0].getElementsByTagName('received');
40385                     if (received && received[0] && received[0].getAttribute('active')) {
40386                         user.active_blocks = received[0].getAttribute('active');
40387                     }
40388                 }
40389
40390                 _userCache.user[uid] = user;
40391                 delete _userCache.toLoad[uid];
40392                 return user;
40393             }
40394         };
40395
40396
40397         function parseXML(xml, callback, options) {
40398             options = Object.assign({ skipSeen: true }, options);
40399             if (!xml || !xml.childNodes) {
40400                 return callback({ message: 'No XML', status: -1 });
40401             }
40402
40403             var root = xml.childNodes[0];
40404             var children = root.childNodes;
40405
40406             var handle = window.requestIdleCallback(function() {
40407                 var results = [];
40408                 var result;
40409                 for (var i = 0; i < children.length; i++) {
40410                     result = parseChild(children[i]);
40411                     if (result) results.push(result);
40412                 }
40413                 callback(null, results);
40414             });
40415
40416             _deferred.add(handle);
40417
40418
40419             function parseChild(child) {
40420                 var parser = parsers[child.nodeName];
40421                 if (!parser) return null;
40422
40423                 var uid;
40424                 if (child.nodeName === 'user') {
40425                     uid = child.attributes.id.value;
40426                     if (options.skipSeen && _userCache.user[uid]) {
40427                         delete _userCache.toLoad[uid];
40428                         return null;
40429                     }
40430
40431                 } else if (child.nodeName === 'note') {
40432                     uid = child.getElementsByTagName('id')[0].textContent;
40433
40434                 } else {
40435                     uid = osmEntity.id.fromOSM(child.nodeName, child.attributes.id.value);
40436                     if (options.skipSeen) {
40437                         if (_tileCache.seen[uid]) return null;  // avoid reparsing a "seen" entity
40438                         _tileCache.seen[uid] = true;
40439                     }
40440                 }
40441
40442                 return parser(child, uid);
40443             }
40444         }
40445
40446
40447         // replace or remove note from rtree
40448         function updateRtree$3(item, replace) {
40449             _noteCache.rtree.remove(item, function isEql(a, b) { return a.data.id === b.data.id; });
40450
40451             if (replace) {
40452                 _noteCache.rtree.insert(item);
40453             }
40454         }
40455
40456
40457         function wrapcb(thisArg, callback, cid) {
40458             return function(err, result) {
40459                 if (err) {
40460                     // 400 Bad Request, 401 Unauthorized, 403 Forbidden..
40461                     if (err.status === 400 || err.status === 401 || err.status === 403) {
40462                         thisArg.logout();
40463                     }
40464                     return callback.call(thisArg, err);
40465
40466                 } else if (thisArg.getConnectionId() !== cid) {
40467                     return callback.call(thisArg, { message: 'Connection Switched', status: -1 });
40468
40469                 } else {
40470                     return callback.call(thisArg, err, result);
40471                 }
40472             };
40473         }
40474
40475
40476         var serviceOsm = {
40477
40478             init: function() {
40479                 utilRebind(this, dispatch$6, 'on');
40480             },
40481
40482
40483             reset: function() {
40484                 Array.from(_deferred).forEach(function(handle) {
40485                     window.cancelIdleCallback(handle);
40486                     _deferred.delete(handle);
40487                 });
40488
40489                 _connectionID++;
40490                 _userChangesets = undefined;
40491                 _userDetails = undefined;
40492                 _rateLimitError = undefined;
40493
40494                 Object.values(_tileCache.inflight).forEach(abortRequest$5);
40495                 Object.values(_noteCache.inflight).forEach(abortRequest$5);
40496                 Object.values(_noteCache.inflightPost).forEach(abortRequest$5);
40497                 if (_changeset.inflight) abortRequest$5(_changeset.inflight);
40498
40499                 _tileCache = { toLoad: {}, loaded: {}, inflight: {}, seen: {}, rtree: new RBush() };
40500                 _noteCache = { toLoad: {}, loaded: {}, inflight: {}, inflightPost: {}, note: {}, closed: {}, rtree: new RBush() };
40501                 _userCache = { toLoad: {}, user: {} };
40502                 _cachedApiStatus = undefined;
40503                 _changeset = {};
40504
40505                 return this;
40506             },
40507
40508
40509             getConnectionId: function() {
40510                 return _connectionID;
40511             },
40512
40513
40514             changesetURL: function(changesetID) {
40515                 return urlroot + '/changeset/' + changesetID;
40516             },
40517
40518
40519             changesetsURL: function(center, zoom) {
40520                 var precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
40521                 return urlroot + '/history#map=' +
40522                     Math.floor(zoom) + '/' +
40523                     center[1].toFixed(precision) + '/' +
40524                     center[0].toFixed(precision);
40525             },
40526
40527
40528             entityURL: function(entity) {
40529                 return urlroot + '/' + entity.type + '/' + entity.osmId();
40530             },
40531
40532
40533             historyURL: function(entity) {
40534                 return urlroot + '/' + entity.type + '/' + entity.osmId() + '/history';
40535             },
40536
40537
40538             userURL: function(username) {
40539                 return urlroot + '/user/' + username;
40540             },
40541
40542
40543             noteURL: function(note) {
40544                 return urlroot + '/note/' + note.id;
40545             },
40546
40547
40548             noteReportURL: function(note) {
40549                 return urlroot + '/reports/new?reportable_type=Note&reportable_id=' + note.id;
40550             },
40551
40552
40553             // Generic method to load data from the OSM API
40554             // Can handle either auth or unauth calls.
40555             loadFromAPI: function(path, callback, options) {
40556                 options = Object.assign({ skipSeen: true }, options);
40557                 var that = this;
40558                 var cid = _connectionID;
40559
40560                 function done(err, payload) {
40561                     if (that.getConnectionId() !== cid) {
40562                         if (callback) callback({ message: 'Connection Switched', status: -1 });
40563                         return;
40564                     }
40565
40566                     var isAuthenticated = that.authenticated();
40567
40568                     // 400 Bad Request, 401 Unauthorized, 403 Forbidden
40569                     // Logout and retry the request..
40570                     if (isAuthenticated && err && err.status &&
40571                             (err.status === 400 || err.status === 401 || err.status === 403)) {
40572                         that.logout();
40573                         that.loadFromAPI(path, callback, options);
40574
40575                     // else, no retry..
40576                     } else {
40577                         // 509 Bandwidth Limit Exceeded, 429 Too Many Requests
40578                         // Set the rateLimitError flag and trigger a warning..
40579                         if (!isAuthenticated && !_rateLimitError && err && err.status &&
40580                                 (err.status === 509 || err.status === 429)) {
40581                             _rateLimitError = err;
40582                             dispatch$6.call('change');
40583                             that.reloadApiStatus();
40584
40585                         } else if ((err && _cachedApiStatus === 'online') ||
40586                             (!err && _cachedApiStatus !== 'online')) {
40587                             // If the response's error state doesn't match the status,
40588                             // it's likely we lost or gained the connection so reload the status
40589                             that.reloadApiStatus();
40590                         }
40591
40592                         if (callback) {
40593                             if (err) {
40594                                 return callback(err);
40595                             } else {
40596                                 if (path.indexOf('.json') !== -1) {
40597                                     return parseJSON(payload, callback, options);
40598                                 } else {
40599                                     return parseXML(payload, callback, options);
40600                                 }
40601                             }
40602                         }
40603                     }
40604                 }
40605
40606                 if (this.authenticated()) {
40607                     return oauth.xhr({ method: 'GET', path: path }, done);
40608                 } else {
40609                     var url = urlroot + path;
40610                     var controller = new AbortController();
40611                     d3_json(url, { signal: controller.signal })
40612                         .then(function(data) {
40613                             done(null, data);
40614                         })
40615                         .catch(function(err) {
40616                             if (err.name === 'AbortError') return;
40617                             // d3-fetch includes status in the error message,
40618                             // but we can't access the response itself
40619                             // https://github.com/d3/d3-fetch/issues/27
40620                             var match = err.message.match(/^\d{3}/);
40621                             if (match) {
40622                                 done({ status: +match[0], statusText: err.message });
40623                             } else {
40624                                 done(err.message);
40625                             }
40626                         });
40627                     return controller;
40628                 }
40629             },
40630
40631
40632             // Load a single entity by id (ways and relations use the `/full` call)
40633             // GET /api/0.6/node/#id
40634             // GET /api/0.6/[way|relation]/#id/full
40635             loadEntity: function(id, callback) {
40636                 var type = osmEntity.id.type(id);
40637                 var osmID = osmEntity.id.toOSM(id);
40638                 var options = { skipSeen: false };
40639
40640                 this.loadFromAPI(
40641                     '/api/0.6/' + type + '/' + osmID + (type !== 'node' ? '/full' : '') + '.json',
40642                     function(err, entities) {
40643                         if (callback) callback(err, { data: entities });
40644                     },
40645                     options
40646                 );
40647             },
40648
40649
40650             // Load a single entity with a specific version
40651             // GET /api/0.6/[node|way|relation]/#id/#version
40652             loadEntityVersion: function(id, version, callback) {
40653                 var type = osmEntity.id.type(id);
40654                 var osmID = osmEntity.id.toOSM(id);
40655                 var options = { skipSeen: false };
40656
40657                 this.loadFromAPI(
40658                     '/api/0.6/' + type + '/' + osmID + '/' + version + '.json',
40659                     function(err, entities) {
40660                         if (callback) callback(err, { data: entities });
40661                     },
40662                     options
40663                 );
40664             },
40665
40666
40667             // Load multiple entities in chunks
40668             // (note: callback may be called multiple times)
40669             // Unlike `loadEntity`, child nodes and members are not fetched
40670             // GET /api/0.6/[nodes|ways|relations]?#parameters
40671             loadMultiple: function(ids, callback) {
40672                 var that = this;
40673                 var groups = utilArrayGroupBy(utilArrayUniq(ids), osmEntity.id.type);
40674
40675                 Object.keys(groups).forEach(function(k) {
40676                     var type = k + 's';   // nodes, ways, relations
40677                     var osmIDs = groups[k].map(function(id) { return osmEntity.id.toOSM(id); });
40678                     var options = { skipSeen: false };
40679
40680                     utilArrayChunk(osmIDs, 150).forEach(function(arr) {
40681                         that.loadFromAPI(
40682                             '/api/0.6/' + type + '.json?' + type + '=' + arr.join(),
40683                             function(err, entities) {
40684                                 if (callback) callback(err, { data: entities });
40685                             },
40686                             options
40687                         );
40688                     });
40689                 });
40690             },
40691
40692
40693             // Create, upload, and close a changeset
40694             // PUT /api/0.6/changeset/create
40695             // POST /api/0.6/changeset/#id/upload
40696             // PUT /api/0.6/changeset/#id/close
40697             putChangeset: function(changeset, changes, callback) {
40698                 var cid = _connectionID;
40699
40700                 if (_changeset.inflight) {
40701                     return callback({ message: 'Changeset already inflight', status: -2 }, changeset);
40702
40703                 } else if (_changeset.open) {   // reuse existing open changeset..
40704                     return createdChangeset.call(this, null, _changeset.open);
40705
40706                 } else {   // Open a new changeset..
40707                     var options = {
40708                         method: 'PUT',
40709                         path: '/api/0.6/changeset/create',
40710                         options: { header: { 'Content-Type': 'text/xml' } },
40711                         content: JXON.stringify(changeset.asJXON())
40712                     };
40713                     _changeset.inflight = oauth.xhr(
40714                         options,
40715                         wrapcb(this, createdChangeset, cid)
40716                     );
40717                 }
40718
40719
40720                 function createdChangeset(err, changesetID) {
40721                     _changeset.inflight = null;
40722                     if (err) { return callback(err, changeset); }
40723
40724                     _changeset.open = changesetID;
40725                     changeset = changeset.update({ id: changesetID });
40726
40727                     // Upload the changeset..
40728                     var options = {
40729                         method: 'POST',
40730                         path: '/api/0.6/changeset/' + changesetID + '/upload',
40731                         options: { header: { 'Content-Type': 'text/xml' } },
40732                         content: JXON.stringify(changeset.osmChangeJXON(changes))
40733                     };
40734                     _changeset.inflight = oauth.xhr(
40735                         options,
40736                         wrapcb(this, uploadedChangeset, cid)
40737                     );
40738                 }
40739
40740
40741                 function uploadedChangeset(err) {
40742                     _changeset.inflight = null;
40743                     if (err) return callback(err, changeset);
40744
40745                     // Upload was successful, safe to call the callback.
40746                     // Add delay to allow for postgres replication #1646 #2678
40747                     window.setTimeout(function() { callback(null, changeset); }, 2500);
40748                     _changeset.open = null;
40749
40750                     // At this point, we don't really care if the connection was switched..
40751                     // Only try to close the changeset if we're still talking to the same server.
40752                     if (this.getConnectionId() === cid) {
40753                         // Still attempt to close changeset, but ignore response because #2667
40754                         oauth.xhr({
40755                             method: 'PUT',
40756                             path: '/api/0.6/changeset/' + changeset.id + '/close',
40757                             options: { header: { 'Content-Type': 'text/xml' } }
40758                         }, function() { return true; });
40759                     }
40760                 }
40761             },
40762
40763
40764             // Load multiple users in chunks
40765             // (note: callback may be called multiple times)
40766             // GET /api/0.6/users?users=#id1,#id2,...,#idn
40767             loadUsers: function(uids, callback) {
40768                 var toLoad = [];
40769                 var cached = [];
40770
40771                 utilArrayUniq(uids).forEach(function(uid) {
40772                     if (_userCache.user[uid]) {
40773                         delete _userCache.toLoad[uid];
40774                         cached.push(_userCache.user[uid]);
40775                     } else {
40776                         toLoad.push(uid);
40777                     }
40778                 });
40779
40780                 if (cached.length || !this.authenticated()) {
40781                     callback(undefined, cached);
40782                     if (!this.authenticated()) return;  // require auth
40783                 }
40784
40785                 utilArrayChunk(toLoad, 150).forEach(function(arr) {
40786                     oauth.xhr(
40787                         { method: 'GET', path: '/api/0.6/users?users=' + arr.join() },
40788                         wrapcb(this, done, _connectionID)
40789                     );
40790                 }.bind(this));
40791
40792                 function done(err, xml) {
40793                     if (err) { return callback(err); }
40794
40795                     var options = { skipSeen: true };
40796                     return parseXML(xml, function(err, results) {
40797                         if (err) {
40798                             return callback(err);
40799                         } else {
40800                             return callback(undefined, results);
40801                         }
40802                     }, options);
40803                 }
40804             },
40805
40806
40807             // Load a given user by id
40808             // GET /api/0.6/user/#id
40809             loadUser: function(uid, callback) {
40810                 if (_userCache.user[uid] || !this.authenticated()) {   // require auth
40811                     delete _userCache.toLoad[uid];
40812                     return callback(undefined, _userCache.user[uid]);
40813                 }
40814
40815                 oauth.xhr(
40816                     { method: 'GET', path: '/api/0.6/user/' + uid },
40817                     wrapcb(this, done, _connectionID)
40818                 );
40819
40820                 function done(err, xml) {
40821                     if (err) { return callback(err); }
40822
40823                     var options = { skipSeen: true };
40824                     return parseXML(xml, function(err, results) {
40825                         if (err) {
40826                             return callback(err);
40827                         } else {
40828                             return callback(undefined, results[0]);
40829                         }
40830                     }, options);
40831                 }
40832             },
40833
40834
40835             // Load the details of the logged-in user
40836             // GET /api/0.6/user/details
40837             userDetails: function(callback) {
40838                 if (_userDetails) {    // retrieve cached
40839                     return callback(undefined, _userDetails);
40840                 }
40841
40842                 oauth.xhr(
40843                     { method: 'GET', path: '/api/0.6/user/details' },
40844                     wrapcb(this, done, _connectionID)
40845                 );
40846
40847                 function done(err, xml) {
40848                     if (err) { return callback(err); }
40849
40850                     var options = { skipSeen: false };
40851                     return parseXML(xml, function(err, results) {
40852                         if (err) {
40853                             return callback(err);
40854                         } else {
40855                             _userDetails = results[0];
40856                             return callback(undefined, _userDetails);
40857                         }
40858                     }, options);
40859                 }
40860             },
40861
40862
40863             // Load previous changesets for the logged in user
40864             // GET /api/0.6/changesets?user=#id
40865             userChangesets: function(callback) {
40866                 if (_userChangesets) {    // retrieve cached
40867                     return callback(undefined, _userChangesets);
40868                 }
40869
40870                 this.userDetails(
40871                     wrapcb(this, gotDetails, _connectionID)
40872                 );
40873
40874
40875                 function gotDetails(err, user) {
40876                     if (err) { return callback(err); }
40877
40878                     oauth.xhr(
40879                         { method: 'GET', path: '/api/0.6/changesets?user=' + user.id },
40880                         wrapcb(this, done, _connectionID)
40881                     );
40882                 }
40883
40884                 function done(err, xml) {
40885                     if (err) { return callback(err); }
40886
40887                     _userChangesets = Array.prototype.map.call(
40888                         xml.getElementsByTagName('changeset'),
40889                         function (changeset) { return { tags: getTags(changeset) }; }
40890                     ).filter(function (changeset) {
40891                         var comment = changeset.tags.comment;
40892                         return comment && comment !== '';
40893                     });
40894
40895                     return callback(undefined, _userChangesets);
40896                 }
40897             },
40898
40899
40900             // Fetch the status of the OSM API
40901             // GET /api/capabilities
40902             status: function(callback) {
40903                 var url = urlroot + '/api/capabilities';
40904                 var errback = wrapcb(this, done, _connectionID);
40905                 d3_xml(url)
40906                     .then(function(data) { errback(null, data); })
40907                     .catch(function(err) { errback(err.message); });
40908
40909                 function done(err, xml) {
40910                     if (err) {
40911                         // the status is null if no response could be retrieved
40912                         return callback(err, null);
40913                     }
40914
40915                     // update blacklists
40916                     var elements = xml.getElementsByTagName('blacklist');
40917                     var regexes = [];
40918                     for (var i = 0; i < elements.length; i++) {
40919                         var regex = elements[i].getAttribute('regex');  // needs unencode?
40920                         if (regex) {
40921                             regexes.push(regex);
40922                         }
40923                     }
40924                     if (regexes.length) {
40925                         _blacklists = regexes;
40926                     }
40927
40928                     if (_rateLimitError) {
40929                         return callback(_rateLimitError, 'rateLimited');
40930                     } else {
40931                         var waynodes = xml.getElementsByTagName('waynodes');
40932                         var maxWayNodes = waynodes.length && parseInt(waynodes[0].getAttribute('maximum'), 10);
40933                         if (maxWayNodes && isFinite(maxWayNodes)) _maxWayNodes = maxWayNodes;
40934
40935                         var apiStatus = xml.getElementsByTagName('status');
40936                         var val = apiStatus[0].getAttribute('api');
40937                         return callback(undefined, val);
40938                     }
40939                 }
40940             },
40941
40942             // Calls `status` and dispatches an `apiStatusChange` event if the returned
40943             // status differs from the cached status.
40944             reloadApiStatus: function() {
40945                 // throttle to avoid unncessary API calls
40946                 if (!this.throttledReloadApiStatus) {
40947                     var that = this;
40948                     this.throttledReloadApiStatus = throttle(function() {
40949                         that.status(function(err, status) {
40950                             if (status !== _cachedApiStatus) {
40951                                 _cachedApiStatus = status;
40952                                 dispatch$6.call('apiStatusChange', that, err, status);
40953                             }
40954                         });
40955                     }, 500);
40956                 }
40957                 this.throttledReloadApiStatus();
40958             },
40959
40960
40961             // Returns the maximum number of nodes a single way can have
40962             maxWayNodes: function() {
40963                 return _maxWayNodes;
40964             },
40965
40966
40967             // Load data (entities) from the API in tiles
40968             // GET /api/0.6/map?bbox=
40969             loadTiles: function(projection, callback) {
40970                 if (_off) return;
40971
40972                 // determine the needed tiles to cover the view
40973                 var tiles = tiler$5.zoomExtent([_tileZoom$3, _tileZoom$3]).getTiles(projection);
40974
40975                 // abort inflight requests that are no longer needed
40976                 var hadRequests = hasInflightRequests(_tileCache);
40977                 abortUnwantedRequests$3(_tileCache, tiles);
40978                 if (hadRequests && !hasInflightRequests(_tileCache)) {
40979                     dispatch$6.call('loaded');    // stop the spinner
40980                 }
40981
40982                 // issue new requests..
40983                 tiles.forEach(function(tile) {
40984                     this.loadTile(tile, callback);
40985                 }, this);
40986             },
40987
40988
40989             // Load a single data tile
40990             // GET /api/0.6/map?bbox=
40991             loadTile: function(tile, callback) {
40992                 if (_off) return;
40993                 if (_tileCache.loaded[tile.id] || _tileCache.inflight[tile.id]) return;
40994
40995                 if (!hasInflightRequests(_tileCache)) {
40996                     dispatch$6.call('loading');   // start the spinner
40997                 }
40998
40999                 var path = '/api/0.6/map.json?bbox=';
41000                 var options = { skipSeen: true };
41001
41002                 _tileCache.inflight[tile.id] = this.loadFromAPI(
41003                     path + tile.extent.toParam(),
41004                     tileCallback,
41005                     options
41006                 );
41007
41008                 function tileCallback(err, parsed) {
41009                     delete _tileCache.inflight[tile.id];
41010                     if (!err) {
41011                         delete _tileCache.toLoad[tile.id];
41012                         _tileCache.loaded[tile.id] = true;
41013                         var bbox = tile.extent.bbox();
41014                         bbox.id = tile.id;
41015                         _tileCache.rtree.insert(bbox);
41016                     }
41017                     if (callback) {
41018                         callback(err, Object.assign({ data: parsed }, tile));
41019                     }
41020                     if (!hasInflightRequests(_tileCache)) {
41021                         dispatch$6.call('loaded');     // stop the spinner
41022                     }
41023                 }
41024             },
41025
41026
41027             isDataLoaded: function(loc) {
41028                 var bbox = { minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1] };
41029                 return _tileCache.rtree.collides(bbox);
41030             },
41031
41032
41033             // load the tile that covers the given `loc`
41034             loadTileAtLoc: function(loc, callback) {
41035                 // Back off if the toLoad queue is filling up.. re #6417
41036                 // (Currently `loadTileAtLoc` requests are considered low priority - used by operations to
41037                 // let users safely edit geometries which extend to unloaded tiles.  We can drop some.)
41038                 if (Object.keys(_tileCache.toLoad).length > 50) return;
41039
41040                 var k = geoZoomToScale(_tileZoom$3 + 1);
41041                 var offset = geoRawMercator().scale(k)(loc);
41042                 var projection = geoRawMercator().transform({ k: k, x: -offset[0], y: -offset[1] });
41043                 var tiles = tiler$5.zoomExtent([_tileZoom$3, _tileZoom$3]).getTiles(projection);
41044
41045                 tiles.forEach(function(tile) {
41046                     if (_tileCache.toLoad[tile.id] || _tileCache.loaded[tile.id] || _tileCache.inflight[tile.id]) return;
41047
41048                     _tileCache.toLoad[tile.id] = true;
41049                     this.loadTile(tile, callback);
41050                 }, this);
41051             },
41052
41053
41054             // Load notes from the API in tiles
41055             // GET /api/0.6/notes?bbox=
41056             loadNotes: function(projection, noteOptions) {
41057                 noteOptions = Object.assign({ limit: 10000, closed: 7 }, noteOptions);
41058                 if (_off) return;
41059
41060                 var that = this;
41061                 var path = '/api/0.6/notes?limit=' + noteOptions.limit + '&closed=' + noteOptions.closed + '&bbox=';
41062                 var throttleLoadUsers = throttle(function() {
41063                     var uids = Object.keys(_userCache.toLoad);
41064                     if (!uids.length) return;
41065                     that.loadUsers(uids, function() {});  // eagerly load user details
41066                 }, 750);
41067
41068                 // determine the needed tiles to cover the view
41069                 var tiles = tiler$5.zoomExtent([_noteZoom, _noteZoom]).getTiles(projection);
41070
41071                 // abort inflight requests that are no longer needed
41072                 abortUnwantedRequests$3(_noteCache, tiles);
41073
41074                 // issue new requests..
41075                 tiles.forEach(function(tile) {
41076                     if (_noteCache.loaded[tile.id] || _noteCache.inflight[tile.id]) return;
41077
41078                     var options = { skipSeen: false };
41079                     _noteCache.inflight[tile.id] = that.loadFromAPI(
41080                         path + tile.extent.toParam(),
41081                         function(err) {
41082                             delete _noteCache.inflight[tile.id];
41083                             if (!err) {
41084                                 _noteCache.loaded[tile.id] = true;
41085                             }
41086                             throttleLoadUsers();
41087                             dispatch$6.call('loadedNotes');
41088                         },
41089                         options
41090                     );
41091                 });
41092             },
41093
41094
41095             // Create a note
41096             // POST /api/0.6/notes?params
41097             postNoteCreate: function(note, callback) {
41098                 if (!this.authenticated()) {
41099                     return callback({ message: 'Not Authenticated', status: -3 }, note);
41100                 }
41101                 if (_noteCache.inflightPost[note.id]) {
41102                     return callback({ message: 'Note update already inflight', status: -2 }, note);
41103                 }
41104
41105                 if (!note.loc[0] || !note.loc[1] || !note.newComment) return; // location & description required
41106
41107                 var comment = note.newComment;
41108                 if (note.newCategory && note.newCategory !== 'None') { comment += ' #' + note.newCategory; }
41109
41110                 var path = '/api/0.6/notes?' + utilQsString({ lon: note.loc[0], lat: note.loc[1], text: comment });
41111
41112                 _noteCache.inflightPost[note.id] = oauth.xhr(
41113                     { method: 'POST', path: path },
41114                     wrapcb(this, done, _connectionID)
41115                 );
41116
41117
41118                 function done(err, xml) {
41119                     delete _noteCache.inflightPost[note.id];
41120                     if (err) { return callback(err); }
41121
41122                     // we get the updated note back, remove from caches and reparse..
41123                     this.removeNote(note);
41124
41125                     var options = { skipSeen: false };
41126                     return parseXML(xml, function(err, results) {
41127                         if (err) {
41128                             return callback(err);
41129                         } else {
41130                             return callback(undefined, results[0]);
41131                         }
41132                     }, options);
41133                 }
41134             },
41135
41136
41137             // Update a note
41138             // POST /api/0.6/notes/#id/comment?text=comment
41139             // POST /api/0.6/notes/#id/close?text=comment
41140             // POST /api/0.6/notes/#id/reopen?text=comment
41141             postNoteUpdate: function(note, newStatus, callback) {
41142                 if (!this.authenticated()) {
41143                     return callback({ message: 'Not Authenticated', status: -3 }, note);
41144                 }
41145                 if (_noteCache.inflightPost[note.id]) {
41146                     return callback({ message: 'Note update already inflight', status: -2 }, note);
41147                 }
41148
41149                 var action;
41150                 if (note.status !== 'closed' && newStatus === 'closed') {
41151                     action = 'close';
41152                 } else if (note.status !== 'open' && newStatus === 'open') {
41153                     action = 'reopen';
41154                 } else {
41155                     action = 'comment';
41156                     if (!note.newComment) return; // when commenting, comment required
41157                 }
41158
41159                 var path = '/api/0.6/notes/' + note.id + '/' + action;
41160                 if (note.newComment) {
41161                     path += '?' + utilQsString({ text: note.newComment });
41162                 }
41163
41164                 _noteCache.inflightPost[note.id] = oauth.xhr(
41165                     { method: 'POST', path: path },
41166                     wrapcb(this, done, _connectionID)
41167                 );
41168
41169
41170                 function done(err, xml) {
41171                     delete _noteCache.inflightPost[note.id];
41172                     if (err) { return callback(err); }
41173
41174                     // we get the updated note back, remove from caches and reparse..
41175                     this.removeNote(note);
41176
41177                     // update closed note cache - used to populate `closed:note` changeset tag
41178                     if (action === 'close') {
41179                         _noteCache.closed[note.id] = true;
41180                     } else if (action === 'reopen') {
41181                         delete _noteCache.closed[note.id];
41182                     }
41183
41184                     var options = { skipSeen: false };
41185                     return parseXML(xml, function(err, results) {
41186                         if (err) {
41187                             return callback(err);
41188                         } else {
41189                             return callback(undefined, results[0]);
41190                         }
41191                     }, options);
41192                 }
41193             },
41194
41195
41196             switch: function(options) {
41197                 urlroot = options.urlroot;
41198
41199                 oauth.options(Object.assign({
41200                     url: urlroot,
41201                     loading: authLoading,
41202                     done: authDone
41203                 }, options));
41204
41205                 this.reset();
41206                 this.userChangesets(function() {});  // eagerly load user details/changesets
41207                 dispatch$6.call('change');
41208                 return this;
41209             },
41210
41211
41212             toggle: function(val) {
41213                 _off = !val;
41214                 return this;
41215             },
41216
41217
41218             isChangesetInflight: function() {
41219                 return !!_changeset.inflight;
41220             },
41221
41222
41223             // get/set cached data
41224             // This is used to save/restore the state when entering/exiting the walkthrough
41225             // Also used for testing purposes.
41226             caches: function(obj) {
41227                 function cloneCache(source) {
41228                     var target = {};
41229                     Object.keys(source).forEach(function(k) {
41230                         if (k === 'rtree') {
41231                             target.rtree = new RBush().fromJSON(source.rtree.toJSON());  // clone rbush
41232                         } else if (k === 'note') {
41233                             target.note = {};
41234                             Object.keys(source.note).forEach(function(id) {
41235                                 target.note[id] = osmNote(source.note[id]);   // copy notes
41236                             });
41237                         } else {
41238                             target[k] = JSON.parse(JSON.stringify(source[k]));   // clone deep
41239                         }
41240                     });
41241                     return target;
41242                 }
41243
41244                 if (!arguments.length) {
41245                     return {
41246                         tile: cloneCache(_tileCache),
41247                         note: cloneCache(_noteCache),
41248                         user: cloneCache(_userCache)
41249                     };
41250                 }
41251
41252                 // access caches directly for testing (e.g., loading notes rtree)
41253                 if (obj === 'get') {
41254                     return {
41255                         tile: _tileCache,
41256                         note: _noteCache,
41257                         user: _userCache
41258                     };
41259                 }
41260
41261                 if (obj.tile) {
41262                     _tileCache = obj.tile;
41263                     _tileCache.inflight = {};
41264                 }
41265                 if (obj.note) {
41266                     _noteCache = obj.note;
41267                     _noteCache.inflight = {};
41268                     _noteCache.inflightPost = {};
41269                 }
41270                 if (obj.user) {
41271                     _userCache = obj.user;
41272                 }
41273
41274                 return this;
41275             },
41276
41277
41278             logout: function() {
41279                 _userChangesets = undefined;
41280                 _userDetails = undefined;
41281                 oauth.logout();
41282                 dispatch$6.call('change');
41283                 return this;
41284             },
41285
41286
41287             authenticated: function() {
41288                 return oauth.authenticated();
41289             },
41290
41291
41292             authenticate: function(callback) {
41293                 var that = this;
41294                 var cid = _connectionID;
41295                 _userChangesets = undefined;
41296                 _userDetails = undefined;
41297
41298                 function done(err, res) {
41299                     if (err) {
41300                         if (callback) callback(err);
41301                         return;
41302                     }
41303                     if (that.getConnectionId() !== cid) {
41304                         if (callback) callback({ message: 'Connection Switched', status: -1 });
41305                         return;
41306                     }
41307                     _rateLimitError = undefined;
41308                     dispatch$6.call('change');
41309                     if (callback) callback(err, res);
41310                     that.userChangesets(function() {});  // eagerly load user details/changesets
41311                 }
41312
41313                 return oauth.authenticate(done);
41314             },
41315
41316
41317             imageryBlacklists: function() {
41318                 return _blacklists;
41319             },
41320
41321
41322             tileZoom: function(val) {
41323                 if (!arguments.length) return _tileZoom$3;
41324                 _tileZoom$3 = val;
41325                 return this;
41326             },
41327
41328
41329             // get all cached notes covering the viewport
41330             notes: function(projection) {
41331                 var viewport = projection.clipExtent();
41332                 var min = [viewport[0][0], viewport[1][1]];
41333                 var max = [viewport[1][0], viewport[0][1]];
41334                 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
41335
41336                 return _noteCache.rtree.search(bbox)
41337                     .map(function(d) { return d.data; });
41338             },
41339
41340
41341             // get a single note from the cache
41342             getNote: function(id) {
41343                 return _noteCache.note[id];
41344             },
41345
41346
41347             // remove a single note from the cache
41348             removeNote: function(note) {
41349                 if (!(note instanceof osmNote) || !note.id) return;
41350
41351                 delete _noteCache.note[note.id];
41352                 updateRtree$3(encodeNoteRtree(note), false);  // false = remove
41353             },
41354
41355
41356             // replace a single note in the cache
41357             replaceNote: function(note) {
41358                 if (!(note instanceof osmNote) || !note.id) return;
41359
41360                 _noteCache.note[note.id] = note;
41361                 updateRtree$3(encodeNoteRtree(note), true);  // true = replace
41362                 return note;
41363             },
41364
41365
41366             // Get an array of note IDs closed during this session.
41367             // Used to populate `closed:note` changeset tag
41368             getClosedIDs: function() {
41369                 return Object.keys(_noteCache.closed).sort();
41370             }
41371
41372         };
41373
41374         var apibase$3 = 'https://wiki.openstreetmap.org/w/api.php';
41375         var _inflight$1 = {};
41376         var _wikibaseCache = {};
41377         var _localeIDs = { en: false };
41378
41379
41380         var debouncedRequest = debounce(request, 500, { leading: false });
41381
41382         function request(url, callback) {
41383             if (_inflight$1[url]) return;
41384             var controller = new AbortController();
41385             _inflight$1[url] = controller;
41386
41387             d3_json(url, { signal: controller.signal })
41388                 .then(function(result) {
41389                     delete _inflight$1[url];
41390                     if (callback) callback(null, result);
41391                 })
41392                 .catch(function(err) {
41393                     delete _inflight$1[url];
41394                     if (err.name === 'AbortError') return;
41395                     if (callback) callback(err.message);
41396                 });
41397         }
41398
41399
41400         /**
41401          * Get the best string value from the descriptions/labels result
41402          * Note that if mediawiki doesn't recognize language code, it will return all values.
41403          * In that case, fallback to use English.
41404          * @param values object - either descriptions or labels
41405          * @param langCode String
41406          * @returns localized string
41407          */
41408         function localizedToString(values, langCode) {
41409             if (values) {
41410                 values = values[langCode] || values.en;
41411             }
41412             return values ? values.value : '';
41413         }
41414
41415
41416         var serviceOsmWikibase = {
41417
41418             init: function() {
41419                 _inflight$1 = {};
41420                 _wikibaseCache = {};
41421                 _localeIDs = {};
41422             },
41423
41424
41425             reset: function() {
41426                 Object.values(_inflight$1).forEach(function(controller) { controller.abort(); });
41427                 _inflight$1 = {};
41428             },
41429
41430
41431             /**
41432              * Get the best value for the property, or undefined if not found
41433              * @param entity object from wikibase
41434              * @param property string e.g. 'P4' for image
41435              * @param langCode string e.g. 'fr' for French
41436              */
41437             claimToValue: function(entity, property, langCode) {
41438                 if (!entity.claims[property]) return undefined;
41439                 var locale = _localeIDs[langCode];
41440                 var preferredPick, localePick;
41441
41442                 entity.claims[property].forEach(function(stmt) {
41443                     // If exists, use value limited to the needed language (has a qualifier P26 = locale)
41444                     // Or if not found, use the first value with the "preferred" rank
41445                     if (!preferredPick && stmt.rank === 'preferred') {
41446                         preferredPick = stmt;
41447                     }
41448                     if (locale && stmt.qualifiers && stmt.qualifiers.P26 &&
41449                         stmt.qualifiers.P26[0].datavalue.value.id === locale
41450                     ) {
41451                         localePick = stmt;
41452                     }
41453                 });
41454
41455                 var result = localePick || preferredPick;
41456                 if (result) {
41457                     var datavalue = result.mainsnak.datavalue;
41458                     return datavalue.type === 'wikibase-entityid' ? datavalue.value.id : datavalue.value;
41459                 } else {
41460                     return undefined;
41461                 }
41462             },
41463
41464
41465             /**
41466              * Convert monolingual property into a key-value object (language -> value)
41467              * @param entity object from wikibase
41468              * @param property string e.g. 'P31' for monolingual wiki page title
41469              */
41470             monolingualClaimToValueObj: function(entity, property) {
41471                 if (!entity || !entity.claims[property]) return undefined;
41472
41473                 return entity.claims[property].reduce(function(acc, obj) {
41474                     var value = obj.mainsnak.datavalue.value;
41475                     acc[value.language] = value.text;
41476                     return acc;
41477                 }, {});
41478             },
41479
41480
41481             toSitelink: function(key, value) {
41482                 var result = value ? ('Tag:' + key + '=' + value) : 'Key:' + key;
41483                 return result.replace(/_/g, ' ').trim();
41484             },
41485
41486
41487             //
41488             // Pass params object of the form:
41489             // {
41490             //   key: 'string',
41491             //   value: 'string',
41492             //   rtype: 'string',
41493             //   langCode: 'string'
41494             // }
41495             //
41496             getEntity: function(params, callback) {
41497                 var doRequest = params.debounce ? debouncedRequest : request;
41498                 var that = this;
41499                 var titles = [];
41500                 var result = {};
41501                 var rtypeSitelink = params.rtype ? ('Relation:' + params.rtype).replace(/_/g, ' ').trim() : false;
41502                 var keySitelink = params.key ? this.toSitelink(params.key) : false;
41503                 var tagSitelink = (params.key && params.value) ? this.toSitelink(params.key, params.value) : false;
41504                 var localeSitelink;
41505
41506                 if (params.langCode && _localeIDs[params.langCode] === undefined) {
41507                     // If this is the first time we are asking about this locale,
41508                     // fetch corresponding entity (if it exists), and cache it.
41509                     // If there is no such entry, cache `false` value to avoid re-requesting it.
41510                     localeSitelink = ('Locale:' + params.langCode).replace(/_/g, ' ').trim();
41511                     titles.push(localeSitelink);
41512                 }
41513
41514                 if (rtypeSitelink) {
41515                     if (_wikibaseCache[rtypeSitelink]) {
41516                         result.rtype = _wikibaseCache[rtypeSitelink];
41517                     } else {
41518                         titles.push(rtypeSitelink);
41519                     }
41520                 }
41521
41522                 if (keySitelink) {
41523                     if (_wikibaseCache[keySitelink]) {
41524                         result.key = _wikibaseCache[keySitelink];
41525                     } else {
41526                         titles.push(keySitelink);
41527                     }
41528                 }
41529
41530                 if (tagSitelink) {
41531                     if (_wikibaseCache[tagSitelink]) {
41532                         result.tag = _wikibaseCache[tagSitelink];
41533                     } else {
41534                         titles.push(tagSitelink);
41535                     }
41536                 }
41537
41538                 if (!titles.length) {
41539                     // Nothing to do, we already had everything in the cache
41540                     return callback(null, result);
41541                 }
41542
41543                 // Requesting just the user language code
41544                 // If backend recognizes the code, it will perform proper fallbacks,
41545                 // and the result will contain the requested code. If not, all values are returned:
41546                 // {"zh-tw":{"value":"...","language":"zh-tw","source-language":"zh-hant"}
41547                 // {"pt-br":{"value":"...","language":"pt","for-language":"pt-br"}}
41548                 var obj = {
41549                     action: 'wbgetentities',
41550                     sites: 'wiki',
41551                     titles: titles.join('|'),
41552                     languages: params.langCode,
41553                     languagefallback: 1,
41554                     origin: '*',
41555                     format: 'json',
41556                     // There is an MW Wikibase API bug https://phabricator.wikimedia.org/T212069
41557                     // We shouldn't use v1 until it gets fixed, but should switch to it afterwards
41558                     // formatversion: 2,
41559                 };
41560
41561                 var url = apibase$3 + '?' + utilQsString(obj);
41562                 doRequest(url, function(err, d) {
41563                     if (err) {
41564                         callback(err);
41565                     } else if (!d.success || d.error) {
41566                         callback(d.error.messages.map(function(v) { return v.html['*']; }).join('<br>'));
41567                     } else {
41568                         var localeID = false;
41569                         Object.values(d.entities).forEach(function(res) {
41570                             if (res.missing !== '') {
41571                                 // Simplify access to the localized values
41572                                 res.description = localizedToString(res.descriptions, params.langCode);
41573                                 res.label = localizedToString(res.labels, params.langCode);
41574
41575                                 var title = res.sitelinks.wiki.title;
41576                                 if (title === rtypeSitelink) {
41577                                     _wikibaseCache[rtypeSitelink] = res;
41578                                     result.rtype = res;
41579                                 } else if (title === keySitelink) {
41580                                     _wikibaseCache[keySitelink] = res;
41581                                     result.key = res;
41582                                 } else if (title === tagSitelink) {
41583                                     _wikibaseCache[tagSitelink] = res;
41584                                     result.tag = res;
41585                                 } else if (title === localeSitelink) {
41586                                     localeID = res.id;
41587                                 } else {
41588                                     console.log('Unexpected title ' + title);  // eslint-disable-line no-console
41589                                 }
41590                             }
41591                         });
41592
41593                         if (localeSitelink) {
41594                             // If locale ID is not found, store false to prevent repeated queries
41595                             that.addLocale(params.langCode, localeID);
41596                         }
41597
41598                         callback(null, result);
41599                     }
41600                 });
41601             },
41602
41603
41604             //
41605             // Pass params object of the form:
41606             // {
41607             //   key: 'string',     // required
41608             //   value: 'string'    // optional
41609             // }
41610             //   -or-
41611             // {
41612             //   rtype: 'rtype'     // relation type  (e.g. 'multipolygon')
41613             // }
41614             //
41615             // Get an result object used to display tag documentation
41616             // {
41617             //   title:        'string',
41618             //   description:  'string',
41619             //   editURL:      'string',
41620             //   imageURL:     'string',
41621             //   wiki:         { title: 'string', text: 'string', url: 'string' }
41622             // }
41623             //
41624             getDocs: function(params, callback) {
41625                 var that = this;
41626                 var langCode = _mainLocalizer.localeCode().toLowerCase();
41627                 params.langCode = langCode;
41628
41629                 this.getEntity(params, function(err, data) {
41630                     if (err) {
41631                         callback(err);
41632                         return;
41633                     }
41634
41635                     var entity = data.rtype || data.tag || data.key;
41636                     if (!entity) {
41637                         callback('No entity');
41638                         return;
41639                     }
41640
41641                     // prepare result
41642                     var result = {
41643                         title: entity.title,
41644                         description: entity.description,
41645                         editURL: 'https://wiki.openstreetmap.org/wiki/' + entity.title
41646                     };
41647
41648                     // add image
41649                     if (entity.claims) {
41650                         var imageroot;
41651                         var image = that.claimToValue(entity, 'P4', langCode);
41652                         if (image) {
41653                             imageroot = 'https://commons.wikimedia.org/w/index.php';
41654                         } else {
41655                             image = that.claimToValue(entity, 'P28', langCode);
41656                             if (image) {
41657                                 imageroot = 'https://wiki.openstreetmap.org/w/index.php';
41658                             }
41659                         }
41660                         if (imageroot && image) {
41661                             result.imageURL = imageroot + '?' + utilQsString({
41662                                 title: 'Special:Redirect/file/' + image,
41663                                 width: 400
41664                             });
41665                         }
41666                     }
41667
41668                     // Try to get a wiki page from tag data item first, followed by the corresponding key data item.
41669                     // If neither tag nor key data item contain a wiki page in the needed language nor English,
41670                     // get the first found wiki page from either the tag or the key item.
41671                     var rtypeWiki = that.monolingualClaimToValueObj(data.rtype, 'P31');
41672                     var tagWiki = that.monolingualClaimToValueObj(data.tag, 'P31');
41673                     var keyWiki = that.monolingualClaimToValueObj(data.key, 'P31');
41674
41675                     // If exact language code does not exist, try to find the first part before the '-'
41676                     // BUG: in some cases, a more elaborate fallback logic might be needed
41677                     var langPrefix = langCode.split('-', 2)[0];
41678
41679                     // use the first acceptable wiki page
41680                     result.wiki =
41681                         getWikiInfo(rtypeWiki, langCode, 'inspector.wiki_reference') ||
41682                         getWikiInfo(rtypeWiki, langPrefix, 'inspector.wiki_reference') ||
41683                         getWikiInfo(rtypeWiki, 'en', 'inspector.wiki_en_reference') ||
41684                         getWikiInfo(tagWiki, langCode, 'inspector.wiki_reference') ||
41685                         getWikiInfo(tagWiki, langPrefix, 'inspector.wiki_reference') ||
41686                         getWikiInfo(tagWiki, 'en', 'inspector.wiki_en_reference') ||
41687                         getWikiInfo(keyWiki, langCode, 'inspector.wiki_reference') ||
41688                         getWikiInfo(keyWiki, langPrefix, 'inspector.wiki_reference') ||
41689                         getWikiInfo(keyWiki, 'en', 'inspector.wiki_en_reference');
41690
41691                     callback(null, result);
41692
41693
41694                     // Helper method to get wiki info if a given language exists
41695                     function getWikiInfo(wiki, langCode, tKey) {
41696                         if (wiki && wiki[langCode]) {
41697                             return {
41698                                 title: wiki[langCode],
41699                                 text: tKey,
41700                                 url: 'https://wiki.openstreetmap.org/wiki/' + wiki[langCode]
41701                             };
41702                         }
41703                     }
41704                 });
41705             },
41706
41707
41708             addLocale: function(langCode, qid) {
41709                 // Makes it easier to unit test
41710                 _localeIDs[langCode] = qid;
41711             },
41712
41713
41714             apibase: function(val) {
41715                 if (!arguments.length) return apibase$3;
41716                 apibase$3 = val;
41717                 return this;
41718             }
41719
41720         };
41721
41722         var jsonpCache = {};
41723         window.jsonpCache = jsonpCache;
41724
41725         function jsonpRequest(url, callback) {
41726             var request = {
41727                 abort: function() {}
41728             };
41729
41730             if (window.JSONP_FIX) {
41731                 if (window.JSONP_DELAY === 0) {
41732                     callback(window.JSONP_FIX);
41733                 } else {
41734                     var t = window.setTimeout(function() {
41735                         callback(window.JSONP_FIX);
41736                     }, window.JSONP_DELAY || 0);
41737
41738                     request.abort = function() { window.clearTimeout(t); };
41739                 }
41740
41741                 return request;
41742             }
41743
41744             function rand() {
41745                 var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
41746                 var c = '';
41747                 var i = -1;
41748                 while (++i < 15) c += chars.charAt(Math.floor(Math.random() * 52));
41749                 return c;
41750             }
41751
41752             function create(url) {
41753                 var e = url.match(/callback=(\w+)/);
41754                 var c = e ? e[1] : rand();
41755
41756                 jsonpCache[c] = function(data) {
41757                     if (jsonpCache[c]) {
41758                         callback(data);
41759                     }
41760                     finalize();
41761                 };
41762
41763                 function finalize() {
41764                     delete jsonpCache[c];
41765                     script.remove();
41766                 }
41767
41768                 request.abort = finalize;
41769                 return 'jsonpCache.' + c;
41770             }
41771
41772             var cb = create(url);
41773
41774             var script = select('head')
41775                 .append('script')
41776                 .attr('type', 'text/javascript')
41777                 .attr('src', url.replace(/(\{|%7B)callback(\}|%7D)/, cb));
41778
41779             return request;
41780         }
41781
41782         const bubbleApi = 'https://dev.virtualearth.net/mapcontrol/HumanScaleServices/GetBubbles.ashx?';
41783         const streetsideImagesApi = 'https://t.ssl.ak.tiles.virtualearth.net/tiles/';
41784         const bubbleAppKey = 'AuftgJsO0Xs8Ts4M1xZUQJQXJNsvmh3IV8DkNieCiy3tCwCUMq76-WpkrBtNAuEm';
41785         const pannellumViewerCSS = 'pannellum-streetside/pannellum.css';
41786         const pannellumViewerJS = 'pannellum-streetside/pannellum.js';
41787         const maxResults$2 = 2000;
41788         const tileZoom$2 = 16.5;
41789         const tiler$6 = utilTiler().zoomExtent([tileZoom$2, tileZoom$2]).skipNullIsland(true);
41790         const dispatch$7 = dispatch('loadedBubbles', 'viewerChanged');
41791         const minHfov = 10;         // zoom in degrees:  20, 10, 5
41792         const maxHfov = 90;         // zoom out degrees
41793         const defaultHfov = 45;
41794
41795         let _hires = false;
41796         let _resolution = 512;    // higher numbers are slower - 512, 1024, 2048, 4096
41797         let _currScene = 0;
41798         let _ssCache;
41799         let _pannellumViewer;
41800         let _sceneOptions;
41801         let _dataUrlArray = [];
41802
41803
41804         /**
41805          * abortRequest().
41806          */
41807         function abortRequest$6(i) {
41808           i.abort();
41809         }
41810
41811
41812         /**
41813          * localeTimeStamp().
41814          */
41815         function localeTimestamp(s) {
41816           if (!s) return null;
41817           const options = { day: 'numeric', month: 'short', year: 'numeric' };
41818           const d = new Date(s);
41819           if (isNaN(d.getTime())) return null;
41820           return d.toLocaleString(_mainLocalizer.localeCode(), options);
41821         }
41822
41823
41824         /**
41825          * loadTiles() wraps the process of generating tiles and then fetching image points for each tile.
41826          */
41827         function loadTiles$2(which, url, projection, margin) {
41828           const tiles = tiler$6.margin(margin).getTiles(projection);
41829
41830           // abort inflight requests that are no longer needed
41831           const cache = _ssCache[which];
41832           Object.keys(cache.inflight).forEach(k => {
41833             const wanted = tiles.find(tile => k.indexOf(tile.id + ',') === 0);
41834             if (!wanted) {
41835               abortRequest$6(cache.inflight[k]);
41836               delete cache.inflight[k];
41837             }
41838           });
41839
41840           tiles.forEach(tile => loadNextTilePage$2(which, url, tile));
41841         }
41842
41843
41844         /**
41845          * loadNextTilePage() load data for the next tile page in line.
41846          */
41847         function loadNextTilePage$2(which, url, tile) {
41848           const cache = _ssCache[which];
41849           const nextPage = cache.nextPage[tile.id] || 0;
41850           const id = tile.id + ',' + String(nextPage);
41851           if (cache.loaded[id] || cache.inflight[id]) return;
41852
41853           cache.inflight[id] = getBubbles(url, tile, (bubbles) => {
41854             cache.loaded[id] = true;
41855             delete cache.inflight[id];
41856             if (!bubbles) return;
41857
41858             // [].shift() removes the first element, some statistics info, not a bubble point
41859             bubbles.shift();
41860
41861             const features = bubbles.map(bubble => {
41862               if (cache.points[bubble.id]) return null;  // skip duplicates
41863
41864               const loc = [bubble.lo, bubble.la];
41865               const d = {
41866                 loc: loc,
41867                 key: bubble.id,
41868                 ca: bubble.he,
41869                 captured_at: bubble.cd,
41870                 captured_by: 'microsoft',
41871                 // nbn: bubble.nbn,
41872                 // pbn: bubble.pbn,
41873                 // ad: bubble.ad,
41874                 // rn: bubble.rn,
41875                 pr: bubble.pr,  // previous
41876                 ne: bubble.ne,  // next
41877                 pano: true,
41878                 sequenceKey: null
41879               };
41880
41881               cache.points[bubble.id] = d;
41882
41883               // a sequence starts here
41884               if (bubble.pr === undefined) {
41885                 cache.leaders.push(bubble.id);
41886               }
41887
41888               return {
41889                 minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1], data: d
41890               };
41891
41892             }).filter(Boolean);
41893
41894             cache.rtree.load(features);
41895
41896             connectSequences();
41897
41898             if (which === 'bubbles') {
41899               dispatch$7.call('loadedBubbles');
41900             }
41901           });
41902         }
41903
41904
41905         // call this sometimes to connect the bubbles into sequences
41906         function connectSequences() {
41907           let cache = _ssCache.bubbles;
41908           let keepLeaders = [];
41909
41910           for (let i = 0; i < cache.leaders.length; i++) {
41911             let bubble = cache.points[cache.leaders[i]];
41912             let seen = {};
41913
41914             // try to make a sequence.. use the key of the leader bubble.
41915             let sequence = { key: bubble.key, bubbles: [] };
41916             let complete = false;
41917
41918             do {
41919               sequence.bubbles.push(bubble);
41920               seen[bubble.key] = true;
41921
41922               if (bubble.ne === undefined) {
41923                 complete = true;
41924               } else {
41925                 bubble = cache.points[bubble.ne];  // advance to next
41926               }
41927             } while (bubble && !seen[bubble.key] && !complete);
41928
41929
41930             if (complete) {
41931               _ssCache.sequences[sequence.key] = sequence;
41932
41933               // assign bubbles to the sequence
41934               for (let j = 0; j < sequence.bubbles.length; j++) {
41935                 sequence.bubbles[j].sequenceKey = sequence.key;
41936               }
41937
41938               // create a GeoJSON LineString
41939               sequence.geojson = {
41940                 type: 'LineString',
41941                 properties: { key: sequence.key },
41942                 coordinates: sequence.bubbles.map(d => d.loc)
41943               };
41944
41945             } else {
41946               keepLeaders.push(cache.leaders[i]);
41947             }
41948           }
41949
41950           // couldn't complete these, save for later
41951           cache.leaders = keepLeaders;
41952         }
41953
41954
41955         /**
41956          * getBubbles() handles the request to the server for a tile extent of 'bubbles' (streetside image locations).
41957          */
41958         function getBubbles(url, tile, callback) {
41959           let rect = tile.extent.rectangle();
41960           let urlForRequest = url + utilQsString({
41961             n: rect[3],
41962             s: rect[1],
41963             e: rect[2],
41964             w: rect[0],
41965             c: maxResults$2,
41966             appkey: bubbleAppKey,
41967             jsCallback: '{callback}'
41968           });
41969
41970           return jsonpRequest(urlForRequest, (data) => {
41971             if (!data || data.error) {
41972               callback(null);
41973             } else {
41974               callback(data);
41975             }
41976           });
41977         }
41978
41979
41980         // partition viewport into higher zoom tiles
41981         function partitionViewport$2(projection) {
41982           let z = geoScaleToZoom(projection.scale());
41983           let z2 = (Math.ceil(z * 2) / 2) + 2.5;   // round to next 0.5 and add 2.5
41984           let tiler = utilTiler().zoomExtent([z2, z2]);
41985
41986           return tiler.getTiles(projection)
41987             .map(tile => tile.extent);
41988         }
41989
41990
41991         // no more than `limit` results per partition.
41992         function searchLimited$2(limit, projection, rtree) {
41993           limit = limit || 5;
41994
41995           return partitionViewport$2(projection)
41996             .reduce((result, extent) => {
41997               let found = rtree.search(extent.bbox())
41998                 .slice(0, limit)
41999                 .map(d => d.data);
42000
42001               return (found.length ? result.concat(found) : result);
42002             }, []);
42003         }
42004
42005
42006         /**
42007          * loadImage()
42008          */
42009         function loadImage(imgInfo) {
42010           return new Promise(resolve => {
42011             let img = new Image();
42012             img.onload = () => {
42013               let canvas = document.getElementById('ideditor-canvas' + imgInfo.face);
42014               let ctx = canvas.getContext('2d');
42015               ctx.drawImage(img, imgInfo.x, imgInfo.y);
42016               resolve({ imgInfo: imgInfo, status: 'ok' });
42017             };
42018             img.onerror = () => {
42019               resolve({ data: imgInfo, status: 'error' });
42020             };
42021             img.setAttribute('crossorigin', '');
42022             img.src = imgInfo.url;
42023           });
42024         }
42025
42026
42027         /**
42028          * loadCanvas()
42029          */
42030         function loadCanvas(imageGroup) {
42031           return Promise.all(imageGroup.map(loadImage))
42032             .then((data) => {
42033               let canvas = document.getElementById('ideditor-canvas' + data[0].imgInfo.face);
42034               const which = { '01': 0, '02': 1, '03': 2, '10': 3, '11': 4, '12': 5 };
42035               let face = data[0].imgInfo.face;
42036               _dataUrlArray[which[face]] = canvas.toDataURL('image/jpeg', 1.0);
42037               return { status: 'loadCanvas for face ' + data[0].imgInfo.face + 'ok'};
42038             });
42039         }
42040
42041
42042         /**
42043          * loadFaces()
42044          */
42045         function loadFaces(faceGroup) {
42046           return Promise.all(faceGroup.map(loadCanvas))
42047             .then(() => { return { status: 'loadFaces done' }; });
42048         }
42049
42050
42051         function setupCanvas(selection, reset) {
42052           if (reset) {
42053             selection.selectAll('#ideditor-stitcher-canvases')
42054               .remove();
42055           }
42056
42057           // Add the Streetside working canvases. These are used for 'stitching', or combining,
42058           // multiple images for each of the six faces, before passing to the Pannellum control as DataUrls
42059           selection.selectAll('#ideditor-stitcher-canvases')
42060             .data([0])
42061             .enter()
42062             .append('div')
42063             .attr('id', 'ideditor-stitcher-canvases')
42064             .attr('display', 'none')
42065             .selectAll('canvas')
42066             .data(['canvas01', 'canvas02', 'canvas03', 'canvas10', 'canvas11', 'canvas12'])
42067             .enter()
42068             .append('canvas')
42069             .attr('id', d => 'ideditor-' + d)
42070             .attr('width', _resolution)
42071             .attr('height', _resolution);
42072         }
42073
42074
42075         function qkToXY(qk) {
42076           let x = 0;
42077           let y = 0;
42078           let scale = 256;
42079           for (let i = qk.length; i > 0; i--) {
42080             const key = qk[i-1];
42081             x += (+(key === '1' || key === '3')) * scale;
42082             y += (+(key === '2' || key === '3')) * scale;
42083             scale *= 2;
42084           }
42085           return [x, y];
42086         }
42087
42088
42089         function getQuadKeys() {
42090           let dim = _resolution / 256;
42091           let quadKeys;
42092
42093           if (dim === 16) {
42094             quadKeys = [
42095               '0000','0001','0010','0011','0100','0101','0110','0111',  '1000','1001','1010','1011','1100','1101','1110','1111',
42096               '0002','0003','0012','0013','0102','0103','0112','0113',  '1002','1003','1012','1013','1102','1103','1112','1113',
42097               '0020','0021','0030','0031','0120','0121','0130','0131',  '1020','1021','1030','1031','1120','1121','1130','1131',
42098               '0022','0023','0032','0033','0122','0123','0132','0133',  '1022','1023','1032','1033','1122','1123','1132','1133',
42099               '0200','0201','0210','0211','0300','0301','0310','0311',  '1200','1201','1210','1211','1300','1301','1310','1311',
42100               '0202','0203','0212','0213','0302','0303','0312','0313',  '1202','1203','1212','1213','1302','1303','1312','1313',
42101               '0220','0221','0230','0231','0320','0321','0330','0331',  '1220','1221','1230','1231','1320','1321','1330','1331',
42102               '0222','0223','0232','0233','0322','0323','0332','0333',  '1222','1223','1232','1233','1322','1323','1332','1333',
42103
42104               '2000','2001','2010','2011','2100','2101','2110','2111',  '3000','3001','3010','3011','3100','3101','3110','3111',
42105               '2002','2003','2012','2013','2102','2103','2112','2113',  '3002','3003','3012','3013','3102','3103','3112','3113',
42106               '2020','2021','2030','2031','2120','2121','2130','2131',  '3020','3021','3030','3031','3120','3121','3130','3131',
42107               '2022','2023','2032','2033','2122','2123','2132','2133',  '3022','3023','3032','3033','3122','3123','3132','3133',
42108               '2200','2201','2210','2211','2300','2301','2310','2311',  '3200','3201','3210','3211','3300','3301','3310','3311',
42109               '2202','2203','2212','2213','2302','2303','2312','2313',  '3202','3203','3212','3213','3302','3303','3312','3313',
42110               '2220','2221','2230','2231','2320','2321','2330','2331',  '3220','3221','3230','3231','3320','3321','3330','3331',
42111               '2222','2223','2232','2233','2322','2323','2332','2333',  '3222','3223','3232','3233','3322','3323','3332','3333'
42112             ];
42113
42114           } else if (dim === 8) {
42115             quadKeys = [
42116               '000','001','010','011',  '100','101','110','111',
42117               '002','003','012','013',  '102','103','112','113',
42118               '020','021','030','031',  '120','121','130','131',
42119               '022','023','032','033',  '122','123','132','133',
42120
42121               '200','201','210','211',  '300','301','310','311',
42122               '202','203','212','213',  '302','303','312','313',
42123               '220','221','230','231',  '320','321','330','331',
42124               '222','223','232','233',  '322','323','332','333'
42125             ];
42126
42127           } else if (dim === 4) {
42128             quadKeys = [
42129               '00','01',  '10','11',
42130               '02','03',  '12','13',
42131
42132               '20','21',  '30','31',
42133               '22','23',  '32','33'
42134             ];
42135
42136           } else {  // dim === 2
42137             quadKeys = [
42138               '0', '1',
42139               '2', '3'
42140             ];
42141           }
42142
42143           return quadKeys;
42144         }
42145
42146
42147
42148         var serviceStreetside = {
42149           /**
42150            * init() initialize streetside.
42151            */
42152           init: function() {
42153             if (!_ssCache) {
42154               this.reset();
42155             }
42156
42157             this.event = utilRebind(this, dispatch$7, 'on');
42158           },
42159
42160           /**
42161            * reset() reset the cache.
42162            */
42163           reset: function() {
42164             if (_ssCache) {
42165               Object.values(_ssCache.bubbles.inflight).forEach(abortRequest$6);
42166             }
42167
42168             _ssCache = {
42169               bubbles: { inflight: {}, loaded: {}, nextPage: {}, rtree: new RBush(), points: {}, leaders: [] },
42170               sequences: {}
42171             };
42172           },
42173
42174           /**
42175            * bubbles()
42176            */
42177           bubbles: function(projection) {
42178             const limit = 5;
42179             return searchLimited$2(limit, projection, _ssCache.bubbles.rtree);
42180           },
42181
42182
42183           sequences: function(projection) {
42184             const viewport = projection.clipExtent();
42185             const min = [viewport[0][0], viewport[1][1]];
42186             const max = [viewport[1][0], viewport[0][1]];
42187             const bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
42188             let seen = {};
42189             let results = [];
42190
42191             // all sequences for bubbles in viewport
42192             _ssCache.bubbles.rtree.search(bbox)
42193               .forEach(d => {
42194                 const key = d.data.sequenceKey;
42195                 if (key && !seen[key]) {
42196                     seen[key] = true;
42197                     results.push(_ssCache.sequences[key].geojson);
42198                 }
42199               });
42200
42201             return results;
42202           },
42203
42204
42205           /**
42206            * loadBubbles()
42207            */
42208           loadBubbles: function(projection, margin) {
42209             // by default: request 2 nearby tiles so we can connect sequences.
42210             if (margin === undefined) margin = 2;
42211
42212             loadTiles$2('bubbles', bubbleApi, projection, margin);
42213           },
42214
42215
42216           viewer: function() {
42217             return _pannellumViewer;
42218           },
42219
42220
42221           initViewer: function () {
42222             if (!window.pannellum) return;
42223             if (_pannellumViewer) return;
42224
42225             const sceneID = ++_currScene + '';
42226             const options = {
42227               'default': { firstScene: sceneID },
42228               scenes: {}
42229             };
42230             options.scenes[sceneID] = _sceneOptions;
42231
42232             _pannellumViewer = window.pannellum.viewer('ideditor-viewer-streetside', options);
42233           },
42234
42235
42236           /**
42237            * loadViewer() create the streeside viewer.
42238            */
42239           loadViewer: function(context) {
42240             let that = this;
42241
42242             let pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
42243
42244             // create ms-wrapper, a photo wrapper class
42245             let wrap = context.container().select('.photoviewer').selectAll('.ms-wrapper')
42246               .data([0]);
42247
42248             // inject ms-wrapper into the photoviewer div
42249             // (used by all to house each custom photo viewer)
42250             let wrapEnter = wrap.enter()
42251               .append('div')
42252               .attr('class', 'photo-wrapper ms-wrapper')
42253               .classed('hide', true);
42254
42255             // inject div to support streetside viewer (pannellum) and attribution line
42256             wrapEnter
42257               .append('div')
42258               .attr('id', 'ideditor-viewer-streetside')
42259               .on(pointerPrefix + 'down.streetside', () => {
42260                 select(window)
42261                   .on(pointerPrefix + 'move.streetside', () => {
42262                     dispatch$7.call('viewerChanged');
42263                   }, true);
42264               })
42265               .on(pointerPrefix + 'up.streetside pointercancel.streetside', () => {
42266                 select(window)
42267                   .on(pointerPrefix + 'move.streetside', null);
42268
42269                 // continue dispatching events for a few seconds, in case viewer has inertia.
42270                 let t = timer(elapsed => {
42271                   dispatch$7.call('viewerChanged');
42272                   if (elapsed > 2000) {
42273                     t.stop();
42274                   }
42275                 });
42276               })
42277               .append('div')
42278               .attr('class', 'photo-attribution fillD');
42279
42280             let controlsEnter = wrapEnter
42281               .append('div')
42282               .attr('class', 'photo-controls-wrap')
42283               .append('div')
42284               .attr('class', 'photo-controls');
42285
42286             controlsEnter
42287               .append('button')
42288               .on('click.back', step(-1))
42289               .text('◄');
42290
42291             controlsEnter
42292               .append('button')
42293               .on('click.forward', step(1))
42294               .text('►');
42295
42296
42297             // create working canvas for stitching together images
42298             wrap = wrap
42299               .merge(wrapEnter)
42300               .call(setupCanvas, true);
42301
42302             // load streetside pannellum viewer css
42303             select('head').selectAll('#ideditor-streetside-viewercss')
42304               .data([0])
42305               .enter()
42306               .append('link')
42307               .attr('id', 'ideditor-streetside-viewercss')
42308               .attr('rel', 'stylesheet')
42309               .attr('href', context.asset(pannellumViewerCSS));
42310
42311             // load streetside pannellum viewer js
42312             select('head').selectAll('#ideditor-streetside-viewerjs')
42313               .data([0])
42314               .enter()
42315               .append('script')
42316               .attr('id', 'ideditor-streetside-viewerjs')
42317               .attr('src', context.asset(pannellumViewerJS));
42318
42319
42320             // Register viewer resize handler
42321             context.ui().photoviewer.on('resize.streetside', () => {
42322               if (_pannellumViewer) {
42323                 _pannellumViewer.resize();
42324               }
42325             });
42326
42327
42328             function step(stepBy) {
42329               return () => {
42330                 let viewer = context.container().select('.photoviewer');
42331                 let selected = viewer.empty() ? undefined : viewer.datum();
42332                 if (!selected) return;
42333
42334                 let nextID = (stepBy === 1 ? selected.ne : selected.pr);
42335                 let yaw = _pannellumViewer.getYaw();
42336                 let ca = selected.ca + yaw;
42337                 let origin = selected.loc;
42338
42339                 // construct a search trapezoid pointing out from current bubble
42340                 const meters = 35;
42341                 let p1 = [
42342                   origin[0] + geoMetersToLon(meters / 5, origin[1]),
42343                   origin[1]
42344                 ];
42345                 let p2 = [
42346                   origin[0] + geoMetersToLon(meters / 2, origin[1]),
42347                   origin[1] + geoMetersToLat(meters)
42348                 ];
42349                 let p3 = [
42350                   origin[0] - geoMetersToLon(meters / 2, origin[1]),
42351                   origin[1] + geoMetersToLat(meters)
42352                 ];
42353                 let p4 = [
42354                   origin[0] - geoMetersToLon(meters / 5, origin[1]),
42355                   origin[1]
42356                 ];
42357
42358                 let poly = [p1, p2, p3, p4, p1];
42359
42360                 // rotate it to face forward/backward
42361                 let angle = (stepBy === 1 ? ca : ca + 180) * (Math.PI / 180);
42362                 poly = geoRotate(poly, -angle, origin);
42363
42364                 let extent = poly.reduce((extent, point) => {
42365                   return extent.extend(geoExtent(point));
42366                 }, geoExtent());
42367
42368                 // find nearest other bubble in the search polygon
42369                 let minDist = Infinity;
42370                 _ssCache.bubbles.rtree.search(extent.bbox())
42371                   .forEach(d => {
42372                     if (d.data.key === selected.key) return;
42373                     if (!geoPointInPolygon(d.data.loc, poly)) return;
42374
42375                     let dist = geoVecLength(d.data.loc, selected.loc);
42376                     let theta = selected.ca - d.data.ca;
42377                     let minTheta = Math.min(Math.abs(theta), 360 - Math.abs(theta));
42378                     if (minTheta > 20) {
42379                       dist += 5;  // penalize distance if camera angles don't match
42380                     }
42381
42382                     if (dist < minDist) {
42383                       nextID = d.data.key;
42384                       minDist = dist;
42385                     }
42386                   });
42387
42388                 let nextBubble = nextID && _ssCache.bubbles.points[nextID];
42389                 if (!nextBubble) return;
42390
42391                 context.map().centerEase(nextBubble.loc);
42392
42393                 that.selectImage(context, nextBubble)
42394                   .then(response => {
42395                     if (response.status === 'ok') {
42396                       _sceneOptions.yaw = yaw;
42397                       that.showViewer(context);
42398                     }
42399                   });
42400               };
42401             }
42402           },
42403
42404
42405           /**
42406            * showViewer()
42407            */
42408           showViewer: function(context, yaw) {
42409             if (!_sceneOptions) return;
42410
42411             if (yaw !== undefined) {
42412               _sceneOptions.yaw = yaw;
42413             }
42414
42415             if (!_pannellumViewer) {
42416               this.initViewer();
42417             } else {
42418               // make a new scene
42419               let sceneID = ++_currScene + '';
42420               _pannellumViewer
42421                 .addScene(sceneID, _sceneOptions)
42422                 .loadScene(sceneID);
42423
42424               // remove previous scene
42425               if (_currScene > 2) {
42426                 sceneID = (_currScene - 1) + '';
42427                 _pannellumViewer
42428                   .removeScene(sceneID);
42429               }
42430             }
42431
42432             let wrap = context.container().select('.photoviewer')
42433               .classed('hide', false);
42434
42435             let isHidden = wrap.selectAll('.photo-wrapper.ms-wrapper.hide').size();
42436
42437             if (isHidden) {
42438               wrap
42439                 .selectAll('.photo-wrapper:not(.ms-wrapper)')
42440                 .classed('hide', true);
42441
42442               wrap
42443                 .selectAll('.photo-wrapper.ms-wrapper')
42444                 .classed('hide', false);
42445             }
42446
42447             return this;
42448           },
42449
42450
42451           /**
42452            * hideViewer()
42453            */
42454           hideViewer: function (context) {
42455             let viewer = context.container().select('.photoviewer');
42456             if (!viewer.empty()) viewer.datum(null);
42457
42458             viewer
42459               .classed('hide', true)
42460               .selectAll('.photo-wrapper')
42461               .classed('hide', true);
42462
42463             context.container().selectAll('.viewfield-group, .sequence, .icon-sign')
42464               .classed('currentView', false);
42465
42466             return this.setStyles(context, null, true);
42467           },
42468
42469
42470           /**
42471            * selectImage().
42472            */
42473           selectImage: function (context, d) {
42474             let that = this;
42475             let viewer = context.container().select('.photoviewer');
42476             if (!viewer.empty()) viewer.datum(d);
42477
42478             this.setStyles(context, null, true);
42479
42480             let wrap = context.container().select('.photoviewer .ms-wrapper');
42481             let attribution = wrap.selectAll('.photo-attribution').html('');
42482
42483             wrap.selectAll('.pnlm-load-box')   // display "loading.."
42484               .style('display', 'block');
42485
42486             if (!d) {
42487               return Promise.resolve({ status: 'ok' });
42488             }
42489
42490             let line1 = attribution
42491               .append('div')
42492               .attr('class', 'attribution-row');
42493
42494             const hiresDomId = utilUniqueDomId('streetside-hires');
42495
42496             // Add hires checkbox
42497             let label = line1
42498               .append('label')
42499               .attr('for', hiresDomId)
42500               .attr('class', 'streetside-hires');
42501
42502             label
42503               .append('input')
42504               .attr('type', 'checkbox')
42505               .attr('id', hiresDomId)
42506               .property('checked', _hires)
42507               .on('click', () => {
42508                 event.stopPropagation();
42509
42510                 _hires = !_hires;
42511                 _resolution = _hires ? 1024 : 512;
42512                 wrap.call(setupCanvas, true);
42513
42514                 let viewstate = {
42515                   yaw: _pannellumViewer.getYaw(),
42516                   pitch: _pannellumViewer.getPitch(),
42517                   hfov: _pannellumViewer.getHfov()
42518                 };
42519
42520                 that.selectImage(context, d)
42521                   .then(response => {
42522                     if (response.status === 'ok') {
42523                       _sceneOptions = Object.assign(_sceneOptions, viewstate);
42524                       that.showViewer(context);
42525                     }
42526                   });
42527               });
42528
42529             label
42530               .append('span')
42531               .text(_t('streetside.hires'));
42532
42533
42534             let captureInfo = line1
42535               .append('div')
42536               .attr('class', 'attribution-capture-info');
42537
42538             // Add capture date
42539             if (d.captured_by) {
42540               const yyyy = (new Date()).getFullYear();
42541
42542               captureInfo
42543                 .append('a')
42544                 .attr('class', 'captured_by')
42545                 .attr('target', '_blank')
42546                 .attr('href', 'https://www.microsoft.com/en-us/maps/streetside')
42547                 .text('©' + yyyy + ' Microsoft');
42548
42549               captureInfo
42550                 .append('span')
42551                 .text('|');
42552             }
42553
42554             if (d.captured_at) {
42555               captureInfo
42556                 .append('span')
42557                 .attr('class', 'captured_at')
42558                 .text(localeTimestamp(d.captured_at));
42559             }
42560
42561             // Add image links
42562             let line2 = attribution
42563               .append('div')
42564               .attr('class', 'attribution-row');
42565
42566             line2
42567               .append('a')
42568               .attr('class', 'image-view-link')
42569               .attr('target', '_blank')
42570               .attr('href', 'https://www.bing.com/maps?cp=' + d.loc[1] + '~' + d.loc[0] +
42571                 '&lvl=17&dir=' + d.ca + '&style=x&v=2&sV=1')
42572               .text(_t('streetside.view_on_bing'));
42573
42574             line2
42575               .append('a')
42576               .attr('class', 'image-report-link')
42577               .attr('target', '_blank')
42578               .attr('href', 'https://www.bing.com/maps/privacyreport/streetsideprivacyreport?bubbleid=' +
42579                 encodeURIComponent(d.key) + '&focus=photo&lat=' + d.loc[1] + '&lng=' + d.loc[0] + '&z=17')
42580               .text(_t('streetside.report'));
42581
42582
42583             let bubbleIdQuadKey = d.key.toString(4);
42584             const paddingNeeded = 16 - bubbleIdQuadKey.length;
42585             for (let i = 0; i < paddingNeeded; i++) {
42586               bubbleIdQuadKey = '0' + bubbleIdQuadKey;
42587             }
42588             const imgUrlPrefix = streetsideImagesApi + 'hs' + bubbleIdQuadKey;
42589             const imgUrlSuffix = '.jpg?g=6338&n=z';
42590
42591             // Cubemap face code order matters here: front=01, right=02, back=03, left=10, up=11, down=12
42592             const faceKeys = ['01','02','03','10','11','12'];
42593
42594             // Map images to cube faces
42595             let quadKeys = getQuadKeys();
42596             let faces = faceKeys.map((faceKey) => {
42597               return quadKeys.map((quadKey) =>{
42598                 const xy = qkToXY(quadKey);
42599                 return {
42600                   face: faceKey,
42601                   url: imgUrlPrefix + faceKey + quadKey + imgUrlSuffix,
42602                   x: xy[0],
42603                   y: xy[1]
42604                 };
42605               });
42606             });
42607
42608             return loadFaces(faces)
42609               .then(() => {
42610                 _sceneOptions = {
42611                   showFullscreenCtrl: false,
42612                   autoLoad: true,
42613                   compass: true,
42614                   northOffset: d.ca,
42615                   yaw: 0,
42616                   minHfov: minHfov,
42617                   maxHfov: maxHfov,
42618                   hfov: defaultHfov,
42619                   type: 'cubemap',
42620                   cubeMap: [
42621                     _dataUrlArray[0],
42622                     _dataUrlArray[1],
42623                     _dataUrlArray[2],
42624                     _dataUrlArray[3],
42625                     _dataUrlArray[4],
42626                     _dataUrlArray[5]
42627                   ]
42628                 };
42629                 return { status: 'ok' };
42630               });
42631           },
42632
42633
42634           getSequenceKeyForBubble: function(d) {
42635             return d && d.sequenceKey;
42636           },
42637
42638
42639           // Updates the currently highlighted sequence and selected bubble.
42640           // Reset is only necessary when interacting with the viewport because
42641           // this implicitly changes the currently selected bubble/sequence
42642           setStyles: function (context, hovered, reset) {
42643             if (reset) {  // reset all layers
42644               context.container().selectAll('.viewfield-group')
42645                 .classed('highlighted', false)
42646                 .classed('hovered', false)
42647                 .classed('currentView', false);
42648
42649               context.container().selectAll('.sequence')
42650                 .classed('highlighted', false)
42651                 .classed('currentView', false);
42652             }
42653
42654             let hoveredBubbleKey = hovered && hovered.key;
42655             let hoveredSequenceKey = this.getSequenceKeyForBubble(hovered);
42656             let hoveredSequence = hoveredSequenceKey && _ssCache.sequences[hoveredSequenceKey];
42657             let hoveredBubbleKeys =  (hoveredSequence && hoveredSequence.bubbles.map(d => d.key)) || [];
42658
42659             let viewer = context.container().select('.photoviewer');
42660             let selected = viewer.empty() ? undefined : viewer.datum();
42661             let selectedBubbleKey = selected && selected.key;
42662             let selectedSequenceKey = this.getSequenceKeyForBubble(selected);
42663             let selectedSequence = selectedSequenceKey && _ssCache.sequences[selectedSequenceKey];
42664             let selectedBubbleKeys = (selectedSequence && selectedSequence.bubbles.map(d => d.key)) || [];
42665
42666             // highlight sibling viewfields on either the selected or the hovered sequences
42667             let highlightedBubbleKeys = utilArrayUnion(hoveredBubbleKeys, selectedBubbleKeys);
42668
42669             context.container().selectAll('.layer-streetside-images .viewfield-group')
42670               .classed('highlighted', d => highlightedBubbleKeys.indexOf(d.key) !== -1)
42671               .classed('hovered',     d => d.key === hoveredBubbleKey)
42672               .classed('currentView', d => d.key === selectedBubbleKey);
42673
42674             context.container().selectAll('.layer-streetside-images .sequence')
42675               .classed('highlighted', d => d.properties.key === hoveredSequenceKey)
42676               .classed('currentView', d => d.properties.key === selectedSequenceKey);
42677
42678             // update viewfields if needed
42679             context.container().selectAll('.viewfield-group .viewfield')
42680               .attr('d', viewfieldPath);
42681
42682             function viewfieldPath() {
42683               let d = this.parentNode.__data__;
42684               if (d.pano && d.key !== selectedBubbleKey) {
42685                 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
42686               } else {
42687                 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
42688               }
42689             }
42690
42691             return this;
42692           },
42693
42694
42695           /**
42696            * cache().
42697            */
42698           cache: function () {
42699             return _ssCache;
42700           }
42701         };
42702
42703         var apibase$4 = 'https://taginfo.openstreetmap.org/api/4/';
42704         var _inflight$2 = {};
42705         var _popularKeys = {};
42706         var _taginfoCache = {};
42707
42708         var tag_sorts = {
42709             point: 'count_nodes',
42710             vertex: 'count_nodes',
42711             area: 'count_ways',
42712             line: 'count_ways'
42713         };
42714         var tag_sort_members = {
42715             point: 'count_node_members',
42716             vertex: 'count_node_members',
42717             area: 'count_way_members',
42718             line: 'count_way_members',
42719             relation: 'count_relation_members'
42720         };
42721         var tag_filters = {
42722             point: 'nodes',
42723             vertex: 'nodes',
42724             area: 'ways',
42725             line: 'ways'
42726         };
42727         var tag_members_fractions = {
42728             point: 'count_node_members_fraction',
42729             vertex: 'count_node_members_fraction',
42730             area: 'count_way_members_fraction',
42731             line: 'count_way_members_fraction',
42732             relation: 'count_relation_members_fraction'
42733         };
42734
42735
42736         function sets(params, n, o) {
42737             if (params.geometry && o[params.geometry]) {
42738                 params[n] = o[params.geometry];
42739             }
42740             return params;
42741         }
42742
42743
42744         function setFilter(params) {
42745             return sets(params, 'filter', tag_filters);
42746         }
42747
42748
42749         function setSort(params) {
42750             return sets(params, 'sortname', tag_sorts);
42751         }
42752
42753
42754         function setSortMembers(params) {
42755             return sets(params, 'sortname', tag_sort_members);
42756         }
42757
42758
42759         function clean(params) {
42760             return utilObjectOmit(params, ['geometry', 'debounce']);
42761         }
42762
42763
42764         function filterKeys(type) {
42765             var count_type = type ? 'count_' + type : 'count_all';
42766             return function(d) {
42767                 return parseFloat(d[count_type]) > 2500 || d.in_wiki;
42768             };
42769         }
42770
42771
42772         function filterMultikeys(prefix) {
42773             return function(d) {
42774                 // d.key begins with prefix, and d.key contains no additional ':'s
42775                 var re = new RegExp('^' + prefix + '(.*)$');
42776                 var matches = d.key.match(re) || [];
42777                 return (matches.length === 2 && matches[1].indexOf(':') === -1);
42778             };
42779         }
42780
42781
42782         function filterValues(allowUpperCase) {
42783             return function(d) {
42784                 if (d.value.match(/[;,]/) !== null) return false;  // exclude some punctuation
42785                 if (!allowUpperCase && d.value.match(/[A-Z*]/) !== null) return false;  // exclude uppercase letters
42786                 return parseFloat(d.fraction) > 0.0;
42787             };
42788         }
42789
42790
42791         function filterRoles(geometry) {
42792             return function(d) {
42793                 if (d.role === '') return false; // exclude empty role
42794                 if (d.role.match(/[A-Z*;,]/) !== null) return false;  // exclude uppercase letters and some punctuation
42795                 return parseFloat(d[tag_members_fractions[geometry]]) > 0.0;
42796             };
42797         }
42798
42799
42800         function valKey(d) {
42801             return {
42802                 value: d.key,
42803                 title: d.key
42804             };
42805         }
42806
42807
42808         function valKeyDescription(d) {
42809             var obj = {
42810                 value: d.value,
42811                 title: d.description || d.value
42812             };
42813             if (d.count) {
42814                 obj.count = d.count;
42815             }
42816             return obj;
42817         }
42818
42819
42820         function roleKey(d) {
42821             return {
42822                 value: d.role,
42823                 title: d.role
42824             };
42825         }
42826
42827
42828         // sort keys with ':' lower than keys without ':'
42829         function sortKeys(a, b) {
42830             return (a.key.indexOf(':') === -1 && b.key.indexOf(':') !== -1) ? -1
42831                 : (a.key.indexOf(':') !== -1 && b.key.indexOf(':') === -1) ? 1
42832                 : 0;
42833         }
42834
42835
42836         var debouncedRequest$1 = debounce(request$1, 300, { leading: false });
42837
42838         function request$1(url, params, exactMatch, callback, loaded) {
42839             if (_inflight$2[url]) return;
42840
42841             if (checkCache(url, params, exactMatch, callback)) return;
42842
42843             var controller = new AbortController();
42844             _inflight$2[url] = controller;
42845
42846             d3_json(url, { signal: controller.signal })
42847                 .then(function(result) {
42848                     delete _inflight$2[url];
42849                     if (loaded) loaded(null, result);
42850                 })
42851                 .catch(function(err) {
42852                     delete _inflight$2[url];
42853                     if (err.name === 'AbortError') return;
42854                     if (loaded) loaded(err.message);
42855                 });
42856         }
42857
42858
42859         function checkCache(url, params, exactMatch, callback) {
42860             var rp = params.rp || 25;
42861             var testQuery = params.query || '';
42862             var testUrl = url;
42863
42864             do {
42865                 var hit = _taginfoCache[testUrl];
42866
42867                 // exact match, or shorter match yielding fewer than max results (rp)
42868                 if (hit && (url === testUrl || hit.length < rp)) {
42869                     callback(null, hit);
42870                     return true;
42871                 }
42872
42873                 // don't try to shorten the query
42874                 if (exactMatch || !testQuery.length) return false;
42875
42876                 // do shorten the query to see if we already have a cached result
42877                 // that has returned fewer than max results (rp)
42878                 testQuery = testQuery.slice(0, -1);
42879                 testUrl = url.replace(/&query=(.*?)&/, '&query=' + testQuery + '&');
42880             } while (testQuery.length >= 0);
42881
42882             return false;
42883         }
42884
42885
42886         var serviceTaginfo = {
42887
42888             init: function() {
42889                 _inflight$2 = {};
42890                 _taginfoCache = {};
42891                 _popularKeys = {
42892                     // manually exclude some keys – #5377, #7485
42893                     postal_code: true,
42894                     full_name: true,
42895                     loc_name: true,
42896                     reg_name: true,
42897                     short_name: true,
42898                     sorting_name: true,
42899                     artist_name: true,
42900                     nat_name: true,
42901                     long_name: true,
42902                     'bridge:name': true
42903                 };
42904
42905                 // Fetch popular keys.  We'll exclude these from `values`
42906                 // lookups because they stress taginfo, and they aren't likely
42907                 // to yield meaningful autocomplete results.. see #3955
42908                 var params = {
42909                     rp: 100,
42910                     sortname: 'values_all',
42911                     sortorder: 'desc',
42912                     page: 1,
42913                     debounce: false,
42914                     lang: _mainLocalizer.languageCode()
42915                 };
42916                 this.keys(params, function(err, data) {
42917                     if (err) return;
42918                     data.forEach(function(d) {
42919                         if (d.value === 'opening_hours') return;  // exception
42920                         _popularKeys[d.value] = true;
42921                     });
42922                 });
42923             },
42924
42925
42926             reset: function() {
42927                 Object.values(_inflight$2).forEach(function(controller) { controller.abort(); });
42928                 _inflight$2 = {};
42929             },
42930
42931
42932             keys: function(params, callback) {
42933                 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
42934                 params = clean(setSort(params));
42935                 params = Object.assign({
42936                     rp: 10,
42937                     sortname: 'count_all',
42938                     sortorder: 'desc',
42939                     page: 1,
42940                     lang: _mainLocalizer.languageCode()
42941                 }, params);
42942
42943                 var url = apibase$4 + 'keys/all?' + utilQsString(params);
42944                 doRequest(url, params, false, callback, function(err, d) {
42945                     if (err) {
42946                         callback(err);
42947                     } else {
42948                         var f = filterKeys(params.filter);
42949                         var result = d.data.filter(f).sort(sortKeys).map(valKey);
42950                         _taginfoCache[url] = result;
42951                         callback(null, result);
42952                     }
42953                 });
42954             },
42955
42956
42957             multikeys: function(params, callback) {
42958                 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
42959                 params = clean(setSort(params));
42960                 params = Object.assign({
42961                     rp: 25,
42962                     sortname: 'count_all',
42963                     sortorder: 'desc',
42964                     page: 1,
42965                     lang: _mainLocalizer.languageCode()
42966                 }, params);
42967
42968                 var prefix = params.query;
42969                 var url = apibase$4 + 'keys/all?' + utilQsString(params);
42970                 doRequest(url, params, true, callback, function(err, d) {
42971                     if (err) {
42972                         callback(err);
42973                     } else {
42974                         var f = filterMultikeys(prefix);
42975                         var result = d.data.filter(f).map(valKey);
42976                         _taginfoCache[url] = result;
42977                         callback(null, result);
42978                     }
42979                 });
42980             },
42981
42982
42983             values: function(params, callback) {
42984                 // Exclude popular keys from values lookups.. see #3955
42985                 var key = params.key;
42986                 if (key && _popularKeys[key]) {
42987                     callback(null, []);
42988                     return;
42989                 }
42990
42991                 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
42992                 params = clean(setSort(setFilter(params)));
42993                 params = Object.assign({
42994                     rp: 25,
42995                     sortname: 'count_all',
42996                     sortorder: 'desc',
42997                     page: 1,
42998                     lang: _mainLocalizer.languageCode()
42999                 }, params);
43000
43001                 var url = apibase$4 + 'key/values?' + utilQsString(params);
43002                 doRequest(url, params, false, callback, function(err, d) {
43003                     if (err) {
43004                         callback(err);
43005                     } else {
43006                         // In most cases we prefer taginfo value results with lowercase letters.
43007                         // A few OSM keys expect values to contain uppercase values (see #3377).
43008                         // This is not an exhaustive list (e.g. `name` also has uppercase values)
43009                         // but these are the fields where taginfo value lookup is most useful.
43010                         var re = /network|taxon|genus|species|brand|grape_variety|royal_cypher|listed_status|booth|rating|stars|:output|_hours|_times|_ref|manufacturer|country|target|brewery/;
43011                         var allowUpperCase = re.test(params.key);
43012                         var f = filterValues(allowUpperCase);
43013
43014                         var result = d.data.filter(f).map(valKeyDescription);
43015                         _taginfoCache[url] = result;
43016                         callback(null, result);
43017                     }
43018                 });
43019             },
43020
43021
43022             roles: function(params, callback) {
43023                 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
43024                 var geometry = params.geometry;
43025                 params = clean(setSortMembers(params));
43026                 params = Object.assign({
43027                     rp: 25,
43028                     sortname: 'count_all_members',
43029                     sortorder: 'desc',
43030                     page: 1,
43031                     lang: _mainLocalizer.languageCode()
43032                 }, params);
43033
43034                 var url = apibase$4 + 'relation/roles?' + utilQsString(params);
43035                 doRequest(url, params, true, callback, function(err, d) {
43036                     if (err) {
43037                         callback(err);
43038                     } else {
43039                         var f = filterRoles(geometry);
43040                         var result = d.data.filter(f).map(roleKey);
43041                         _taginfoCache[url] = result;
43042                         callback(null, result);
43043                     }
43044                 });
43045             },
43046
43047
43048             docs: function(params, callback) {
43049                 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
43050                 params = clean(setSort(params));
43051
43052                 var path = 'key/wiki_pages?';
43053                 if (params.value) {
43054                     path = 'tag/wiki_pages?';
43055                 } else if (params.rtype) {
43056                     path = 'relation/wiki_pages?';
43057                 }
43058
43059                 var url = apibase$4 + path + utilQsString(params);
43060                 doRequest(url, params, true, callback, function(err, d) {
43061                     if (err) {
43062                         callback(err);
43063                     } else {
43064                         _taginfoCache[url] = d.data;
43065                         callback(null, d.data);
43066                     }
43067                 });
43068             },
43069
43070
43071             apibase: function(_) {
43072                 if (!arguments.length) return apibase$4;
43073                 apibase$4 = _;
43074                 return this;
43075             }
43076
43077         };
43078
43079         var helpers$1 = createCommonjsModule(function (module, exports) {
43080         Object.defineProperty(exports, "__esModule", { value: true });
43081         /**
43082          * @module helpers
43083          */
43084         /**
43085          * Earth Radius used with the Harvesine formula and approximates using a spherical (non-ellipsoid) Earth.
43086          *
43087          * @memberof helpers
43088          * @type {number}
43089          */
43090         exports.earthRadius = 6371008.8;
43091         /**
43092          * Unit of measurement factors using a spherical (non-ellipsoid) earth radius.
43093          *
43094          * @memberof helpers
43095          * @type {Object}
43096          */
43097         exports.factors = {
43098             centimeters: exports.earthRadius * 100,
43099             centimetres: exports.earthRadius * 100,
43100             degrees: exports.earthRadius / 111325,
43101             feet: exports.earthRadius * 3.28084,
43102             inches: exports.earthRadius * 39.370,
43103             kilometers: exports.earthRadius / 1000,
43104             kilometres: exports.earthRadius / 1000,
43105             meters: exports.earthRadius,
43106             metres: exports.earthRadius,
43107             miles: exports.earthRadius / 1609.344,
43108             millimeters: exports.earthRadius * 1000,
43109             millimetres: exports.earthRadius * 1000,
43110             nauticalmiles: exports.earthRadius / 1852,
43111             radians: 1,
43112             yards: exports.earthRadius / 1.0936,
43113         };
43114         /**
43115          * Units of measurement factors based on 1 meter.
43116          *
43117          * @memberof helpers
43118          * @type {Object}
43119          */
43120         exports.unitsFactors = {
43121             centimeters: 100,
43122             centimetres: 100,
43123             degrees: 1 / 111325,
43124             feet: 3.28084,
43125             inches: 39.370,
43126             kilometers: 1 / 1000,
43127             kilometres: 1 / 1000,
43128             meters: 1,
43129             metres: 1,
43130             miles: 1 / 1609.344,
43131             millimeters: 1000,
43132             millimetres: 1000,
43133             nauticalmiles: 1 / 1852,
43134             radians: 1 / exports.earthRadius,
43135             yards: 1 / 1.0936,
43136         };
43137         /**
43138          * Area of measurement factors based on 1 square meter.
43139          *
43140          * @memberof helpers
43141          * @type {Object}
43142          */
43143         exports.areaFactors = {
43144             acres: 0.000247105,
43145             centimeters: 10000,
43146             centimetres: 10000,
43147             feet: 10.763910417,
43148             inches: 1550.003100006,
43149             kilometers: 0.000001,
43150             kilometres: 0.000001,
43151             meters: 1,
43152             metres: 1,
43153             miles: 3.86e-7,
43154             millimeters: 1000000,
43155             millimetres: 1000000,
43156             yards: 1.195990046,
43157         };
43158         /**
43159          * Wraps a GeoJSON {@link Geometry} in a GeoJSON {@link Feature}.
43160          *
43161          * @name feature
43162          * @param {Geometry} geometry input geometry
43163          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43164          * @param {Object} [options={}] Optional Parameters
43165          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43166          * @param {string|number} [options.id] Identifier associated with the Feature
43167          * @returns {Feature} a GeoJSON Feature
43168          * @example
43169          * var geometry = {
43170          *   "type": "Point",
43171          *   "coordinates": [110, 50]
43172          * };
43173          *
43174          * var feature = turf.feature(geometry);
43175          *
43176          * //=feature
43177          */
43178         function feature(geom, properties, options) {
43179             if (options === void 0) { options = {}; }
43180             var feat = { type: "Feature" };
43181             if (options.id === 0 || options.id) {
43182                 feat.id = options.id;
43183             }
43184             if (options.bbox) {
43185                 feat.bbox = options.bbox;
43186             }
43187             feat.properties = properties || {};
43188             feat.geometry = geom;
43189             return feat;
43190         }
43191         exports.feature = feature;
43192         /**
43193          * Creates a GeoJSON {@link Geometry} from a Geometry string type & coordinates.
43194          * For GeometryCollection type use `helpers.geometryCollection`
43195          *
43196          * @name geometry
43197          * @param {string} type Geometry Type
43198          * @param {Array<any>} coordinates Coordinates
43199          * @param {Object} [options={}] Optional Parameters
43200          * @returns {Geometry} a GeoJSON Geometry
43201          * @example
43202          * var type = "Point";
43203          * var coordinates = [110, 50];
43204          * var geometry = turf.geometry(type, coordinates);
43205          * // => geometry
43206          */
43207         function geometry(type, coordinates, options) {
43208             switch (type) {
43209                 case "Point": return point(coordinates).geometry;
43210                 case "LineString": return lineString(coordinates).geometry;
43211                 case "Polygon": return polygon(coordinates).geometry;
43212                 case "MultiPoint": return multiPoint(coordinates).geometry;
43213                 case "MultiLineString": return multiLineString(coordinates).geometry;
43214                 case "MultiPolygon": return multiPolygon(coordinates).geometry;
43215                 default: throw new Error(type + " is invalid");
43216             }
43217         }
43218         exports.geometry = geometry;
43219         /**
43220          * Creates a {@link Point} {@link Feature} from a Position.
43221          *
43222          * @name point
43223          * @param {Array<number>} coordinates longitude, latitude position (each in decimal degrees)
43224          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43225          * @param {Object} [options={}] Optional Parameters
43226          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43227          * @param {string|number} [options.id] Identifier associated with the Feature
43228          * @returns {Feature<Point>} a Point feature
43229          * @example
43230          * var point = turf.point([-75.343, 39.984]);
43231          *
43232          * //=point
43233          */
43234         function point(coordinates, properties, options) {
43235             if (options === void 0) { options = {}; }
43236             var geom = {
43237                 type: "Point",
43238                 coordinates: coordinates,
43239             };
43240             return feature(geom, properties, options);
43241         }
43242         exports.point = point;
43243         /**
43244          * Creates a {@link Point} {@link FeatureCollection} from an Array of Point coordinates.
43245          *
43246          * @name points
43247          * @param {Array<Array<number>>} coordinates an array of Points
43248          * @param {Object} [properties={}] Translate these properties to each Feature
43249          * @param {Object} [options={}] Optional Parameters
43250          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north]
43251          * associated with the FeatureCollection
43252          * @param {string|number} [options.id] Identifier associated with the FeatureCollection
43253          * @returns {FeatureCollection<Point>} Point Feature
43254          * @example
43255          * var points = turf.points([
43256          *   [-75, 39],
43257          *   [-80, 45],
43258          *   [-78, 50]
43259          * ]);
43260          *
43261          * //=points
43262          */
43263         function points(coordinates, properties, options) {
43264             if (options === void 0) { options = {}; }
43265             return featureCollection(coordinates.map(function (coords) {
43266                 return point(coords, properties);
43267             }), options);
43268         }
43269         exports.points = points;
43270         /**
43271          * Creates a {@link Polygon} {@link Feature} from an Array of LinearRings.
43272          *
43273          * @name polygon
43274          * @param {Array<Array<Array<number>>>} coordinates an array of LinearRings
43275          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43276          * @param {Object} [options={}] Optional Parameters
43277          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43278          * @param {string|number} [options.id] Identifier associated with the Feature
43279          * @returns {Feature<Polygon>} Polygon Feature
43280          * @example
43281          * var polygon = turf.polygon([[[-5, 52], [-4, 56], [-2, 51], [-7, 54], [-5, 52]]], { name: 'poly1' });
43282          *
43283          * //=polygon
43284          */
43285         function polygon(coordinates, properties, options) {
43286             if (options === void 0) { options = {}; }
43287             for (var _i = 0, coordinates_1 = coordinates; _i < coordinates_1.length; _i++) {
43288                 var ring = coordinates_1[_i];
43289                 if (ring.length < 4) {
43290                     throw new Error("Each LinearRing of a Polygon must have 4 or more Positions.");
43291                 }
43292                 for (var j = 0; j < ring[ring.length - 1].length; j++) {
43293                     // Check if first point of Polygon contains two numbers
43294                     if (ring[ring.length - 1][j] !== ring[0][j]) {
43295                         throw new Error("First and last Position are not equivalent.");
43296                     }
43297                 }
43298             }
43299             var geom = {
43300                 type: "Polygon",
43301                 coordinates: coordinates,
43302             };
43303             return feature(geom, properties, options);
43304         }
43305         exports.polygon = polygon;
43306         /**
43307          * Creates a {@link Polygon} {@link FeatureCollection} from an Array of Polygon coordinates.
43308          *
43309          * @name polygons
43310          * @param {Array<Array<Array<Array<number>>>>} coordinates an array of Polygon coordinates
43311          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43312          * @param {Object} [options={}] Optional Parameters
43313          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43314          * @param {string|number} [options.id] Identifier associated with the FeatureCollection
43315          * @returns {FeatureCollection<Polygon>} Polygon FeatureCollection
43316          * @example
43317          * var polygons = turf.polygons([
43318          *   [[[-5, 52], [-4, 56], [-2, 51], [-7, 54], [-5, 52]]],
43319          *   [[[-15, 42], [-14, 46], [-12, 41], [-17, 44], [-15, 42]]],
43320          * ]);
43321          *
43322          * //=polygons
43323          */
43324         function polygons(coordinates, properties, options) {
43325             if (options === void 0) { options = {}; }
43326             return featureCollection(coordinates.map(function (coords) {
43327                 return polygon(coords, properties);
43328             }), options);
43329         }
43330         exports.polygons = polygons;
43331         /**
43332          * Creates a {@link LineString} {@link Feature} from an Array of Positions.
43333          *
43334          * @name lineString
43335          * @param {Array<Array<number>>} coordinates an array of Positions
43336          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43337          * @param {Object} [options={}] Optional Parameters
43338          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43339          * @param {string|number} [options.id] Identifier associated with the Feature
43340          * @returns {Feature<LineString>} LineString Feature
43341          * @example
43342          * var linestring1 = turf.lineString([[-24, 63], [-23, 60], [-25, 65], [-20, 69]], {name: 'line 1'});
43343          * var linestring2 = turf.lineString([[-14, 43], [-13, 40], [-15, 45], [-10, 49]], {name: 'line 2'});
43344          *
43345          * //=linestring1
43346          * //=linestring2
43347          */
43348         function lineString(coordinates, properties, options) {
43349             if (options === void 0) { options = {}; }
43350             if (coordinates.length < 2) {
43351                 throw new Error("coordinates must be an array of two or more positions");
43352             }
43353             var geom = {
43354                 type: "LineString",
43355                 coordinates: coordinates,
43356             };
43357             return feature(geom, properties, options);
43358         }
43359         exports.lineString = lineString;
43360         /**
43361          * Creates a {@link LineString} {@link FeatureCollection} from an Array of LineString coordinates.
43362          *
43363          * @name lineStrings
43364          * @param {Array<Array<Array<number>>>} coordinates an array of LinearRings
43365          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43366          * @param {Object} [options={}] Optional Parameters
43367          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north]
43368          * associated with the FeatureCollection
43369          * @param {string|number} [options.id] Identifier associated with the FeatureCollection
43370          * @returns {FeatureCollection<LineString>} LineString FeatureCollection
43371          * @example
43372          * var linestrings = turf.lineStrings([
43373          *   [[-24, 63], [-23, 60], [-25, 65], [-20, 69]],
43374          *   [[-14, 43], [-13, 40], [-15, 45], [-10, 49]]
43375          * ]);
43376          *
43377          * //=linestrings
43378          */
43379         function lineStrings(coordinates, properties, options) {
43380             if (options === void 0) { options = {}; }
43381             return featureCollection(coordinates.map(function (coords) {
43382                 return lineString(coords, properties);
43383             }), options);
43384         }
43385         exports.lineStrings = lineStrings;
43386         /**
43387          * Takes one or more {@link Feature|Features} and creates a {@link FeatureCollection}.
43388          *
43389          * @name featureCollection
43390          * @param {Feature[]} features input features
43391          * @param {Object} [options={}] Optional Parameters
43392          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43393          * @param {string|number} [options.id] Identifier associated with the Feature
43394          * @returns {FeatureCollection} FeatureCollection of Features
43395          * @example
43396          * var locationA = turf.point([-75.343, 39.984], {name: 'Location A'});
43397          * var locationB = turf.point([-75.833, 39.284], {name: 'Location B'});
43398          * var locationC = turf.point([-75.534, 39.123], {name: 'Location C'});
43399          *
43400          * var collection = turf.featureCollection([
43401          *   locationA,
43402          *   locationB,
43403          *   locationC
43404          * ]);
43405          *
43406          * //=collection
43407          */
43408         function featureCollection(features, options) {
43409             if (options === void 0) { options = {}; }
43410             var fc = { type: "FeatureCollection" };
43411             if (options.id) {
43412                 fc.id = options.id;
43413             }
43414             if (options.bbox) {
43415                 fc.bbox = options.bbox;
43416             }
43417             fc.features = features;
43418             return fc;
43419         }
43420         exports.featureCollection = featureCollection;
43421         /**
43422          * Creates a {@link Feature<MultiLineString>} based on a
43423          * coordinate array. Properties can be added optionally.
43424          *
43425          * @name multiLineString
43426          * @param {Array<Array<Array<number>>>} coordinates an array of LineStrings
43427          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43428          * @param {Object} [options={}] Optional Parameters
43429          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43430          * @param {string|number} [options.id] Identifier associated with the Feature
43431          * @returns {Feature<MultiLineString>} a MultiLineString feature
43432          * @throws {Error} if no coordinates are passed
43433          * @example
43434          * var multiLine = turf.multiLineString([[[0,0],[10,10]]]);
43435          *
43436          * //=multiLine
43437          */
43438         function multiLineString(coordinates, properties, options) {
43439             if (options === void 0) { options = {}; }
43440             var geom = {
43441                 type: "MultiLineString",
43442                 coordinates: coordinates,
43443             };
43444             return feature(geom, properties, options);
43445         }
43446         exports.multiLineString = multiLineString;
43447         /**
43448          * Creates a {@link Feature<MultiPoint>} based on a
43449          * coordinate array. Properties can be added optionally.
43450          *
43451          * @name multiPoint
43452          * @param {Array<Array<number>>} coordinates an array of Positions
43453          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43454          * @param {Object} [options={}] Optional Parameters
43455          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43456          * @param {string|number} [options.id] Identifier associated with the Feature
43457          * @returns {Feature<MultiPoint>} a MultiPoint feature
43458          * @throws {Error} if no coordinates are passed
43459          * @example
43460          * var multiPt = turf.multiPoint([[0,0],[10,10]]);
43461          *
43462          * //=multiPt
43463          */
43464         function multiPoint(coordinates, properties, options) {
43465             if (options === void 0) { options = {}; }
43466             var geom = {
43467                 type: "MultiPoint",
43468                 coordinates: coordinates,
43469             };
43470             return feature(geom, properties, options);
43471         }
43472         exports.multiPoint = multiPoint;
43473         /**
43474          * Creates a {@link Feature<MultiPolygon>} based on a
43475          * coordinate array. Properties can be added optionally.
43476          *
43477          * @name multiPolygon
43478          * @param {Array<Array<Array<Array<number>>>>} coordinates an array of Polygons
43479          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43480          * @param {Object} [options={}] Optional Parameters
43481          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43482          * @param {string|number} [options.id] Identifier associated with the Feature
43483          * @returns {Feature<MultiPolygon>} a multipolygon feature
43484          * @throws {Error} if no coordinates are passed
43485          * @example
43486          * var multiPoly = turf.multiPolygon([[[[0,0],[0,10],[10,10],[10,0],[0,0]]]]);
43487          *
43488          * //=multiPoly
43489          *
43490          */
43491         function multiPolygon(coordinates, properties, options) {
43492             if (options === void 0) { options = {}; }
43493             var geom = {
43494                 type: "MultiPolygon",
43495                 coordinates: coordinates,
43496             };
43497             return feature(geom, properties, options);
43498         }
43499         exports.multiPolygon = multiPolygon;
43500         /**
43501          * Creates a {@link Feature<GeometryCollection>} based on a
43502          * coordinate array. Properties can be added optionally.
43503          *
43504          * @name geometryCollection
43505          * @param {Array<Geometry>} geometries an array of GeoJSON Geometries
43506          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43507          * @param {Object} [options={}] Optional Parameters
43508          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43509          * @param {string|number} [options.id] Identifier associated with the Feature
43510          * @returns {Feature<GeometryCollection>} a GeoJSON GeometryCollection Feature
43511          * @example
43512          * var pt = turf.geometry("Point", [100, 0]);
43513          * var line = turf.geometry("LineString", [[101, 0], [102, 1]]);
43514          * var collection = turf.geometryCollection([pt, line]);
43515          *
43516          * // => collection
43517          */
43518         function geometryCollection(geometries, properties, options) {
43519             if (options === void 0) { options = {}; }
43520             var geom = {
43521                 type: "GeometryCollection",
43522                 geometries: geometries,
43523             };
43524             return feature(geom, properties, options);
43525         }
43526         exports.geometryCollection = geometryCollection;
43527         /**
43528          * Round number to precision
43529          *
43530          * @param {number} num Number
43531          * @param {number} [precision=0] Precision
43532          * @returns {number} rounded number
43533          * @example
43534          * turf.round(120.4321)
43535          * //=120
43536          *
43537          * turf.round(120.4321, 2)
43538          * //=120.43
43539          */
43540         function round(num, precision) {
43541             if (precision === void 0) { precision = 0; }
43542             if (precision && !(precision >= 0)) {
43543                 throw new Error("precision must be a positive number");
43544             }
43545             var multiplier = Math.pow(10, precision || 0);
43546             return Math.round(num * multiplier) / multiplier;
43547         }
43548         exports.round = round;
43549         /**
43550          * Convert a distance measurement (assuming a spherical Earth) from radians to a more friendly unit.
43551          * Valid units: miles, nauticalmiles, inches, yards, meters, metres, kilometers, centimeters, feet
43552          *
43553          * @name radiansToLength
43554          * @param {number} radians in radians across the sphere
43555          * @param {string} [units="kilometers"] can be degrees, radians, miles, or kilometers inches, yards, metres,
43556          * meters, kilometres, kilometers.
43557          * @returns {number} distance
43558          */
43559         function radiansToLength(radians, units) {
43560             if (units === void 0) { units = "kilometers"; }
43561             var factor = exports.factors[units];
43562             if (!factor) {
43563                 throw new Error(units + " units is invalid");
43564             }
43565             return radians * factor;
43566         }
43567         exports.radiansToLength = radiansToLength;
43568         /**
43569          * Convert a distance measurement (assuming a spherical Earth) from a real-world unit into radians
43570          * Valid units: miles, nauticalmiles, inches, yards, meters, metres, kilometers, centimeters, feet
43571          *
43572          * @name lengthToRadians
43573          * @param {number} distance in real units
43574          * @param {string} [units="kilometers"] can be degrees, radians, miles, or kilometers inches, yards, metres,
43575          * meters, kilometres, kilometers.
43576          * @returns {number} radians
43577          */
43578         function lengthToRadians(distance, units) {
43579             if (units === void 0) { units = "kilometers"; }
43580             var factor = exports.factors[units];
43581             if (!factor) {
43582                 throw new Error(units + " units is invalid");
43583             }
43584             return distance / factor;
43585         }
43586         exports.lengthToRadians = lengthToRadians;
43587         /**
43588          * Convert a distance measurement (assuming a spherical Earth) from a real-world unit into degrees
43589          * Valid units: miles, nauticalmiles, inches, yards, meters, metres, centimeters, kilometres, feet
43590          *
43591          * @name lengthToDegrees
43592          * @param {number} distance in real units
43593          * @param {string} [units="kilometers"] can be degrees, radians, miles, or kilometers inches, yards, metres,
43594          * meters, kilometres, kilometers.
43595          * @returns {number} degrees
43596          */
43597         function lengthToDegrees(distance, units) {
43598             return radiansToDegrees(lengthToRadians(distance, units));
43599         }
43600         exports.lengthToDegrees = lengthToDegrees;
43601         /**
43602          * Converts any bearing angle from the north line direction (positive clockwise)
43603          * and returns an angle between 0-360 degrees (positive clockwise), 0 being the north line
43604          *
43605          * @name bearingToAzimuth
43606          * @param {number} bearing angle, between -180 and +180 degrees
43607          * @returns {number} angle between 0 and 360 degrees
43608          */
43609         function bearingToAzimuth(bearing) {
43610             var angle = bearing % 360;
43611             if (angle < 0) {
43612                 angle += 360;
43613             }
43614             return angle;
43615         }
43616         exports.bearingToAzimuth = bearingToAzimuth;
43617         /**
43618          * Converts an angle in radians to degrees
43619          *
43620          * @name radiansToDegrees
43621          * @param {number} radians angle in radians
43622          * @returns {number} degrees between 0 and 360 degrees
43623          */
43624         function radiansToDegrees(radians) {
43625             var degrees = radians % (2 * Math.PI);
43626             return degrees * 180 / Math.PI;
43627         }
43628         exports.radiansToDegrees = radiansToDegrees;
43629         /**
43630          * Converts an angle in degrees to radians
43631          *
43632          * @name degreesToRadians
43633          * @param {number} degrees angle between 0 and 360 degrees
43634          * @returns {number} angle in radians
43635          */
43636         function degreesToRadians(degrees) {
43637             var radians = degrees % 360;
43638             return radians * Math.PI / 180;
43639         }
43640         exports.degreesToRadians = degreesToRadians;
43641         /**
43642          * Converts a length to the requested unit.
43643          * Valid units: miles, nauticalmiles, inches, yards, meters, metres, kilometers, centimeters, feet
43644          *
43645          * @param {number} length to be converted
43646          * @param {Units} [originalUnit="kilometers"] of the length
43647          * @param {Units} [finalUnit="kilometers"] returned unit
43648          * @returns {number} the converted length
43649          */
43650         function convertLength(length, originalUnit, finalUnit) {
43651             if (originalUnit === void 0) { originalUnit = "kilometers"; }
43652             if (finalUnit === void 0) { finalUnit = "kilometers"; }
43653             if (!(length >= 0)) {
43654                 throw new Error("length must be a positive number");
43655             }
43656             return radiansToLength(lengthToRadians(length, originalUnit), finalUnit);
43657         }
43658         exports.convertLength = convertLength;
43659         /**
43660          * Converts a area to the requested unit.
43661          * Valid units: kilometers, kilometres, meters, metres, centimetres, millimeters, acres, miles, yards, feet, inches
43662          * @param {number} area to be converted
43663          * @param {Units} [originalUnit="meters"] of the distance
43664          * @param {Units} [finalUnit="kilometers"] returned unit
43665          * @returns {number} the converted distance
43666          */
43667         function convertArea(area, originalUnit, finalUnit) {
43668             if (originalUnit === void 0) { originalUnit = "meters"; }
43669             if (finalUnit === void 0) { finalUnit = "kilometers"; }
43670             if (!(area >= 0)) {
43671                 throw new Error("area must be a positive number");
43672             }
43673             var startFactor = exports.areaFactors[originalUnit];
43674             if (!startFactor) {
43675                 throw new Error("invalid original units");
43676             }
43677             var finalFactor = exports.areaFactors[finalUnit];
43678             if (!finalFactor) {
43679                 throw new Error("invalid final units");
43680             }
43681             return (area / startFactor) * finalFactor;
43682         }
43683         exports.convertArea = convertArea;
43684         /**
43685          * isNumber
43686          *
43687          * @param {*} num Number to validate
43688          * @returns {boolean} true/false
43689          * @example
43690          * turf.isNumber(123)
43691          * //=true
43692          * turf.isNumber('foo')
43693          * //=false
43694          */
43695         function isNumber(num) {
43696             return !isNaN(num) && num !== null && !Array.isArray(num) && !/^\s*$/.test(num);
43697         }
43698         exports.isNumber = isNumber;
43699         /**
43700          * isObject
43701          *
43702          * @param {*} input variable to validate
43703          * @returns {boolean} true/false
43704          * @example
43705          * turf.isObject({elevation: 10})
43706          * //=true
43707          * turf.isObject('foo')
43708          * //=false
43709          */
43710         function isObject(input) {
43711             return (!!input) && (input.constructor === Object);
43712         }
43713         exports.isObject = isObject;
43714         /**
43715          * Validate BBox
43716          *
43717          * @private
43718          * @param {Array<number>} bbox BBox to validate
43719          * @returns {void}
43720          * @throws Error if BBox is not valid
43721          * @example
43722          * validateBBox([-180, -40, 110, 50])
43723          * //=OK
43724          * validateBBox([-180, -40])
43725          * //=Error
43726          * validateBBox('Foo')
43727          * //=Error
43728          * validateBBox(5)
43729          * //=Error
43730          * validateBBox(null)
43731          * //=Error
43732          * validateBBox(undefined)
43733          * //=Error
43734          */
43735         function validateBBox(bbox) {
43736             if (!bbox) {
43737                 throw new Error("bbox is required");
43738             }
43739             if (!Array.isArray(bbox)) {
43740                 throw new Error("bbox must be an Array");
43741             }
43742             if (bbox.length !== 4 && bbox.length !== 6) {
43743                 throw new Error("bbox must be an Array of 4 or 6 numbers");
43744             }
43745             bbox.forEach(function (num) {
43746                 if (!isNumber(num)) {
43747                     throw new Error("bbox must only contain numbers");
43748                 }
43749             });
43750         }
43751         exports.validateBBox = validateBBox;
43752         /**
43753          * Validate Id
43754          *
43755          * @private
43756          * @param {string|number} id Id to validate
43757          * @returns {void}
43758          * @throws Error if Id is not valid
43759          * @example
43760          * validateId([-180, -40, 110, 50])
43761          * //=Error
43762          * validateId([-180, -40])
43763          * //=Error
43764          * validateId('Foo')
43765          * //=OK
43766          * validateId(5)
43767          * //=OK
43768          * validateId(null)
43769          * //=Error
43770          * validateId(undefined)
43771          * //=Error
43772          */
43773         function validateId(id) {
43774             if (!id) {
43775                 throw new Error("id is required");
43776             }
43777             if (["string", "number"].indexOf(typeof id) === -1) {
43778                 throw new Error("id must be a number or a string");
43779             }
43780         }
43781         exports.validateId = validateId;
43782         // Deprecated methods
43783         function radians2degrees() {
43784             throw new Error("method has been renamed to `radiansToDegrees`");
43785         }
43786         exports.radians2degrees = radians2degrees;
43787         function degrees2radians() {
43788             throw new Error("method has been renamed to `degreesToRadians`");
43789         }
43790         exports.degrees2radians = degrees2radians;
43791         function distanceToDegrees() {
43792             throw new Error("method has been renamed to `lengthToDegrees`");
43793         }
43794         exports.distanceToDegrees = distanceToDegrees;
43795         function distanceToRadians() {
43796             throw new Error("method has been renamed to `lengthToRadians`");
43797         }
43798         exports.distanceToRadians = distanceToRadians;
43799         function radiansToDistance() {
43800             throw new Error("method has been renamed to `radiansToLength`");
43801         }
43802         exports.radiansToDistance = radiansToDistance;
43803         function bearingToAngle() {
43804             throw new Error("method has been renamed to `bearingToAzimuth`");
43805         }
43806         exports.bearingToAngle = bearingToAngle;
43807         function convertDistance() {
43808             throw new Error("method has been renamed to `convertLength`");
43809         }
43810         exports.convertDistance = convertDistance;
43811         });
43812
43813         var invariant = createCommonjsModule(function (module, exports) {
43814         Object.defineProperty(exports, "__esModule", { value: true });
43815
43816         /**
43817          * Unwrap a coordinate from a Point Feature, Geometry or a single coordinate.
43818          *
43819          * @name getCoord
43820          * @param {Array<number>|Geometry<Point>|Feature<Point>} coord GeoJSON Point or an Array of numbers
43821          * @returns {Array<number>} coordinates
43822          * @example
43823          * var pt = turf.point([10, 10]);
43824          *
43825          * var coord = turf.getCoord(pt);
43826          * //= [10, 10]
43827          */
43828         function getCoord(coord) {
43829             if (!coord) {
43830                 throw new Error("coord is required");
43831             }
43832             if (!Array.isArray(coord)) {
43833                 if (coord.type === "Feature" && coord.geometry !== null && coord.geometry.type === "Point") {
43834                     return coord.geometry.coordinates;
43835                 }
43836                 if (coord.type === "Point") {
43837                     return coord.coordinates;
43838                 }
43839             }
43840             if (Array.isArray(coord) && coord.length >= 2 && !Array.isArray(coord[0]) && !Array.isArray(coord[1])) {
43841                 return coord;
43842             }
43843             throw new Error("coord must be GeoJSON Point or an Array of numbers");
43844         }
43845         exports.getCoord = getCoord;
43846         /**
43847          * Unwrap coordinates from a Feature, Geometry Object or an Array
43848          *
43849          * @name getCoords
43850          * @param {Array<any>|Geometry|Feature} coords Feature, Geometry Object or an Array
43851          * @returns {Array<any>} coordinates
43852          * @example
43853          * var poly = turf.polygon([[[119.32, -8.7], [119.55, -8.69], [119.51, -8.54], [119.32, -8.7]]]);
43854          *
43855          * var coords = turf.getCoords(poly);
43856          * //= [[[119.32, -8.7], [119.55, -8.69], [119.51, -8.54], [119.32, -8.7]]]
43857          */
43858         function getCoords(coords) {
43859             if (Array.isArray(coords)) {
43860                 return coords;
43861             }
43862             // Feature
43863             if (coords.type === "Feature") {
43864                 if (coords.geometry !== null) {
43865                     return coords.geometry.coordinates;
43866                 }
43867             }
43868             else {
43869                 // Geometry
43870                 if (coords.coordinates) {
43871                     return coords.coordinates;
43872                 }
43873             }
43874             throw new Error("coords must be GeoJSON Feature, Geometry Object or an Array");
43875         }
43876         exports.getCoords = getCoords;
43877         /**
43878          * Checks if coordinates contains a number
43879          *
43880          * @name containsNumber
43881          * @param {Array<any>} coordinates GeoJSON Coordinates
43882          * @returns {boolean} true if Array contains a number
43883          */
43884         function containsNumber(coordinates) {
43885             if (coordinates.length > 1 && helpers$1.isNumber(coordinates[0]) && helpers$1.isNumber(coordinates[1])) {
43886                 return true;
43887             }
43888             if (Array.isArray(coordinates[0]) && coordinates[0].length) {
43889                 return containsNumber(coordinates[0]);
43890             }
43891             throw new Error("coordinates must only contain numbers");
43892         }
43893         exports.containsNumber = containsNumber;
43894         /**
43895          * Enforce expectations about types of GeoJSON objects for Turf.
43896          *
43897          * @name geojsonType
43898          * @param {GeoJSON} value any GeoJSON object
43899          * @param {string} type expected GeoJSON type
43900          * @param {string} name name of calling function
43901          * @throws {Error} if value is not the expected type.
43902          */
43903         function geojsonType(value, type, name) {
43904             if (!type || !name) {
43905                 throw new Error("type and name required");
43906             }
43907             if (!value || value.type !== type) {
43908                 throw new Error("Invalid input to " + name + ": must be a " + type + ", given " + value.type);
43909             }
43910         }
43911         exports.geojsonType = geojsonType;
43912         /**
43913          * Enforce expectations about types of {@link Feature} inputs for Turf.
43914          * Internally this uses {@link geojsonType} to judge geometry types.
43915          *
43916          * @name featureOf
43917          * @param {Feature} feature a feature with an expected geometry type
43918          * @param {string} type expected GeoJSON type
43919          * @param {string} name name of calling function
43920          * @throws {Error} error if value is not the expected type.
43921          */
43922         function featureOf(feature, type, name) {
43923             if (!feature) {
43924                 throw new Error("No feature passed");
43925             }
43926             if (!name) {
43927                 throw new Error(".featureOf() requires a name");
43928             }
43929             if (!feature || feature.type !== "Feature" || !feature.geometry) {
43930                 throw new Error("Invalid input to " + name + ", Feature with geometry required");
43931             }
43932             if (!feature.geometry || feature.geometry.type !== type) {
43933                 throw new Error("Invalid input to " + name + ": must be a " + type + ", given " + feature.geometry.type);
43934             }
43935         }
43936         exports.featureOf = featureOf;
43937         /**
43938          * Enforce expectations about types of {@link FeatureCollection} inputs for Turf.
43939          * Internally this uses {@link geojsonType} to judge geometry types.
43940          *
43941          * @name collectionOf
43942          * @param {FeatureCollection} featureCollection a FeatureCollection for which features will be judged
43943          * @param {string} type expected GeoJSON type
43944          * @param {string} name name of calling function
43945          * @throws {Error} if value is not the expected type.
43946          */
43947         function collectionOf(featureCollection, type, name) {
43948             if (!featureCollection) {
43949                 throw new Error("No featureCollection passed");
43950             }
43951             if (!name) {
43952                 throw new Error(".collectionOf() requires a name");
43953             }
43954             if (!featureCollection || featureCollection.type !== "FeatureCollection") {
43955                 throw new Error("Invalid input to " + name + ", FeatureCollection required");
43956             }
43957             for (var _i = 0, _a = featureCollection.features; _i < _a.length; _i++) {
43958                 var feature = _a[_i];
43959                 if (!feature || feature.type !== "Feature" || !feature.geometry) {
43960                     throw new Error("Invalid input to " + name + ", Feature with geometry required");
43961                 }
43962                 if (!feature.geometry || feature.geometry.type !== type) {
43963                     throw new Error("Invalid input to " + name + ": must be a " + type + ", given " + feature.geometry.type);
43964                 }
43965             }
43966         }
43967         exports.collectionOf = collectionOf;
43968         /**
43969          * Get Geometry from Feature or Geometry Object
43970          *
43971          * @param {Feature|Geometry} geojson GeoJSON Feature or Geometry Object
43972          * @returns {Geometry|null} GeoJSON Geometry Object
43973          * @throws {Error} if geojson is not a Feature or Geometry Object
43974          * @example
43975          * var point = {
43976          *   "type": "Feature",
43977          *   "properties": {},
43978          *   "geometry": {
43979          *     "type": "Point",
43980          *     "coordinates": [110, 40]
43981          *   }
43982          * }
43983          * var geom = turf.getGeom(point)
43984          * //={"type": "Point", "coordinates": [110, 40]}
43985          */
43986         function getGeom(geojson) {
43987             if (geojson.type === "Feature") {
43988                 return geojson.geometry;
43989             }
43990             return geojson;
43991         }
43992         exports.getGeom = getGeom;
43993         /**
43994          * Get GeoJSON object's type, Geometry type is prioritize.
43995          *
43996          * @param {GeoJSON} geojson GeoJSON object
43997          * @param {string} [name="geojson"] name of the variable to display in error message
43998          * @returns {string} GeoJSON type
43999          * @example
44000          * var point = {
44001          *   "type": "Feature",
44002          *   "properties": {},
44003          *   "geometry": {
44004          *     "type": "Point",
44005          *     "coordinates": [110, 40]
44006          *   }
44007          * }
44008          * var geom = turf.getType(point)
44009          * //="Point"
44010          */
44011         function getType(geojson, name) {
44012             if (geojson.type === "FeatureCollection") {
44013                 return "FeatureCollection";
44014             }
44015             if (geojson.type === "GeometryCollection") {
44016                 return "GeometryCollection";
44017             }
44018             if (geojson.type === "Feature" && geojson.geometry !== null) {
44019                 return geojson.geometry.type;
44020             }
44021             return geojson.type;
44022         }
44023         exports.getType = getType;
44024         });
44025
44026         var lineclip_1 = lineclip;
44027         var _default = lineclip;
44028
44029         lineclip.polyline = lineclip;
44030         lineclip.polygon = polygonclip;
44031
44032
44033         // Cohen-Sutherland line clippign algorithm, adapted to efficiently
44034         // handle polylines rather than just segments
44035
44036         function lineclip(points, bbox, result) {
44037
44038             var len = points.length,
44039                 codeA = bitCode(points[0], bbox),
44040                 part = [],
44041                 i, a, b, codeB, lastCode;
44042
44043             if (!result) result = [];
44044
44045             for (i = 1; i < len; i++) {
44046                 a = points[i - 1];
44047                 b = points[i];
44048                 codeB = lastCode = bitCode(b, bbox);
44049
44050                 while (true) {
44051
44052                     if (!(codeA | codeB)) { // accept
44053                         part.push(a);
44054
44055                         if (codeB !== lastCode) { // segment went outside
44056                             part.push(b);
44057
44058                             if (i < len - 1) { // start a new line
44059                                 result.push(part);
44060                                 part = [];
44061                             }
44062                         } else if (i === len - 1) {
44063                             part.push(b);
44064                         }
44065                         break;
44066
44067                     } else if (codeA & codeB) { // trivial reject
44068                         break;
44069
44070                     } else if (codeA) { // a outside, intersect with clip edge
44071                         a = intersect(a, b, codeA, bbox);
44072                         codeA = bitCode(a, bbox);
44073
44074                     } else { // b outside
44075                         b = intersect(a, b, codeB, bbox);
44076                         codeB = bitCode(b, bbox);
44077                     }
44078                 }
44079
44080                 codeA = lastCode;
44081             }
44082
44083             if (part.length) result.push(part);
44084
44085             return result;
44086         }
44087
44088         // Sutherland-Hodgeman polygon clipping algorithm
44089
44090         function polygonclip(points, bbox) {
44091
44092             var result, edge, prev, prevInside, i, p, inside;
44093
44094             // clip against each side of the clip rectangle
44095             for (edge = 1; edge <= 8; edge *= 2) {
44096                 result = [];
44097                 prev = points[points.length - 1];
44098                 prevInside = !(bitCode(prev, bbox) & edge);
44099
44100                 for (i = 0; i < points.length; i++) {
44101                     p = points[i];
44102                     inside = !(bitCode(p, bbox) & edge);
44103
44104                     // if segment goes through the clip window, add an intersection
44105                     if (inside !== prevInside) result.push(intersect(prev, p, edge, bbox));
44106
44107                     if (inside) result.push(p); // add a point if it's inside
44108
44109                     prev = p;
44110                     prevInside = inside;
44111                 }
44112
44113                 points = result;
44114
44115                 if (!points.length) break;
44116             }
44117
44118             return result;
44119         }
44120
44121         // intersect a segment against one of the 4 lines that make up the bbox
44122
44123         function intersect(a, b, edge, bbox) {
44124             return edge & 8 ? [a[0] + (b[0] - a[0]) * (bbox[3] - a[1]) / (b[1] - a[1]), bbox[3]] : // top
44125                    edge & 4 ? [a[0] + (b[0] - a[0]) * (bbox[1] - a[1]) / (b[1] - a[1]), bbox[1]] : // bottom
44126                    edge & 2 ? [bbox[2], a[1] + (b[1] - a[1]) * (bbox[2] - a[0]) / (b[0] - a[0])] : // right
44127                    edge & 1 ? [bbox[0], a[1] + (b[1] - a[1]) * (bbox[0] - a[0]) / (b[0] - a[0])] : // left
44128                    null;
44129         }
44130
44131         // bit code reflects the point position relative to the bbox:
44132
44133         //         left  mid  right
44134         //    top  1001  1000  1010
44135         //    mid  0001  0000  0010
44136         // bottom  0101  0100  0110
44137
44138         function bitCode(p, bbox) {
44139             var code = 0;
44140
44141             if (p[0] < bbox[0]) code |= 1; // left
44142             else if (p[0] > bbox[2]) code |= 2; // right
44143
44144             if (p[1] < bbox[1]) code |= 4; // bottom
44145             else if (p[1] > bbox[3]) code |= 8; // top
44146
44147             return code;
44148         }
44149         lineclip_1.default = _default;
44150
44151         var bboxClip_1 = createCommonjsModule(function (module, exports) {
44152         var __importStar = (commonjsGlobal && commonjsGlobal.__importStar) || function (mod) {
44153             if (mod && mod.__esModule) return mod;
44154             var result = {};
44155             if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
44156             result["default"] = mod;
44157             return result;
44158         };
44159         Object.defineProperty(exports, "__esModule", { value: true });
44160
44161
44162         var lineclip = __importStar(lineclip_1);
44163         /**
44164          * Takes a {@link Feature} and a bbox and clips the feature to the bbox using
44165          * [lineclip](https://github.com/mapbox/lineclip).
44166          * May result in degenerate edges when clipping Polygons.
44167          *
44168          * @name bboxClip
44169          * @param {Feature<LineString|MultiLineString|Polygon|MultiPolygon>} feature feature to clip to the bbox
44170          * @param {BBox} bbox extent in [minX, minY, maxX, maxY] order
44171          * @returns {Feature<LineString|MultiLineString|Polygon|MultiPolygon>} clipped Feature
44172          * @example
44173          * var bbox = [0, 0, 10, 10];
44174          * var poly = turf.polygon([[[2, 2], [8, 4], [12, 8], [3, 7], [2, 2]]]);
44175          *
44176          * var clipped = turf.bboxClip(poly, bbox);
44177          *
44178          * //addToMap
44179          * var addToMap = [bbox, poly, clipped]
44180          */
44181         function bboxClip(feature, bbox) {
44182             var geom = invariant.getGeom(feature);
44183             var type = geom.type;
44184             var properties = feature.type === "Feature" ? feature.properties : {};
44185             var coords = geom.coordinates;
44186             switch (type) {
44187                 case "LineString":
44188                 case "MultiLineString":
44189                     var lines_1 = [];
44190                     if (type === "LineString") {
44191                         coords = [coords];
44192                     }
44193                     coords.forEach(function (line) {
44194                         lineclip.polyline(line, bbox, lines_1);
44195                     });
44196                     if (lines_1.length === 1) {
44197                         return helpers$1.lineString(lines_1[0], properties);
44198                     }
44199                     return helpers$1.multiLineString(lines_1, properties);
44200                 case "Polygon":
44201                     return helpers$1.polygon(clipPolygon(coords, bbox), properties);
44202                 case "MultiPolygon":
44203                     return helpers$1.multiPolygon(coords.map(function (poly) {
44204                         return clipPolygon(poly, bbox);
44205                     }), properties);
44206                 default:
44207                     throw new Error("geometry " + type + " not supported");
44208             }
44209         }
44210         exports.default = bboxClip;
44211         function clipPolygon(rings, bbox) {
44212             var outRings = [];
44213             for (var _i = 0, rings_1 = rings; _i < rings_1.length; _i++) {
44214                 var ring = rings_1[_i];
44215                 var clipped = lineclip.polygon(ring, bbox);
44216                 if (clipped.length > 0) {
44217                     if (clipped[0][0] !== clipped[clipped.length - 1][0] || clipped[0][1] !== clipped[clipped.length - 1][1]) {
44218                         clipped.push(clipped[0]);
44219                     }
44220                     if (clipped.length >= 4) {
44221                         outRings.push(clipped);
44222                     }
44223                 }
44224             }
44225             return outRings;
44226         }
44227         });
44228
44229         var fastJsonStableStringify = function (data, opts) {
44230             if (!opts) opts = {};
44231             if (typeof opts === 'function') opts = { cmp: opts };
44232             var cycles = (typeof opts.cycles === 'boolean') ? opts.cycles : false;
44233
44234             var cmp = opts.cmp && (function (f) {
44235                 return function (node) {
44236                     return function (a, b) {
44237                         var aobj = { key: a, value: node[a] };
44238                         var bobj = { key: b, value: node[b] };
44239                         return f(aobj, bobj);
44240                     };
44241                 };
44242             })(opts.cmp);
44243
44244             var seen = [];
44245             return (function stringify (node) {
44246                 if (node && node.toJSON && typeof node.toJSON === 'function') {
44247                     node = node.toJSON();
44248                 }
44249
44250                 if (node === undefined) return;
44251                 if (typeof node == 'number') return isFinite(node) ? '' + node : 'null';
44252                 if (typeof node !== 'object') return JSON.stringify(node);
44253
44254                 var i, out;
44255                 if (Array.isArray(node)) {
44256                     out = '[';
44257                     for (i = 0; i < node.length; i++) {
44258                         if (i) out += ',';
44259                         out += stringify(node[i]) || 'null';
44260                     }
44261                     return out + ']';
44262                 }
44263
44264                 if (node === null) return 'null';
44265
44266                 if (seen.indexOf(node) !== -1) {
44267                     if (cycles) return JSON.stringify('__cycle__');
44268                     throw new TypeError('Converting circular structure to JSON');
44269                 }
44270
44271                 var seenIndex = seen.push(node) - 1;
44272                 var keys = Object.keys(node).sort(cmp && cmp(node));
44273                 out = '';
44274                 for (i = 0; i < keys.length; i++) {
44275                     var key = keys[i];
44276                     var value = stringify(node[key]);
44277
44278                     if (!value) continue;
44279                     if (out) out += ',';
44280                     out += JSON.stringify(key) + ':' + value;
44281                 }
44282                 seen.splice(seenIndex, 1);
44283                 return '{' + out + '}';
44284             })(data);
44285         };
44286
44287         function DEFAULT_COMPARE (a, b) { return a > b ? 1 : a < b ? -1 : 0; }
44288
44289         class SplayTree {
44290
44291           constructor(compare = DEFAULT_COMPARE, noDuplicates = false) {
44292             this._compare = compare;
44293             this._root = null;
44294             this._size = 0;
44295             this._noDuplicates = !!noDuplicates;
44296           }
44297
44298
44299           rotateLeft(x) {
44300             var y = x.right;
44301             if (y) {
44302               x.right = y.left;
44303               if (y.left) y.left.parent = x;
44304               y.parent = x.parent;
44305             }
44306
44307             if (!x.parent)                this._root = y;
44308             else if (x === x.parent.left) x.parent.left = y;
44309             else                          x.parent.right = y;
44310             if (y) y.left = x;
44311             x.parent = y;
44312           }
44313
44314
44315           rotateRight(x) {
44316             var y = x.left;
44317             if (y) {
44318               x.left = y.right;
44319               if (y.right) y.right.parent = x;
44320               y.parent = x.parent;
44321             }
44322
44323             if (!x.parent)               this._root = y;
44324             else if(x === x.parent.left) x.parent.left = y;
44325             else                         x.parent.right = y;
44326             if (y) y.right = x;
44327             x.parent = y;
44328           }
44329
44330
44331           _splay(x) {
44332             while (x.parent) {
44333               var p = x.parent;
44334               if (!p.parent) {
44335                 if (p.left === x) this.rotateRight(p);
44336                 else              this.rotateLeft(p);
44337               } else if (p.left === x && p.parent.left === p) {
44338                 this.rotateRight(p.parent);
44339                 this.rotateRight(p);
44340               } else if (p.right === x && p.parent.right === p) {
44341                 this.rotateLeft(p.parent);
44342                 this.rotateLeft(p);
44343               } else if (p.left === x && p.parent.right === p) {
44344                 this.rotateRight(p);
44345                 this.rotateLeft(p);
44346               } else {
44347                 this.rotateLeft(p);
44348                 this.rotateRight(p);
44349               }
44350             }
44351           }
44352
44353
44354           splay(x) {
44355             var p, gp, ggp, l, r;
44356
44357             while (x.parent) {
44358               p = x.parent;
44359               gp = p.parent;
44360
44361               if (gp && gp.parent) {
44362                 ggp = gp.parent;
44363                 if (ggp.left === gp) ggp.left  = x;
44364                 else                 ggp.right = x;
44365                 x.parent = ggp;
44366               } else {
44367                 x.parent = null;
44368                 this._root = x;
44369               }
44370
44371               l = x.left; r = x.right;
44372
44373               if (x === p.left) { // left
44374                 if (gp) {
44375                   if (gp.left === p) {
44376                     /* zig-zig */
44377                     if (p.right) {
44378                       gp.left = p.right;
44379                       gp.left.parent = gp;
44380                     } else gp.left = null;
44381
44382                     p.right   = gp;
44383                     gp.parent = p;
44384                   } else {
44385                     /* zig-zag */
44386                     if (l) {
44387                       gp.right = l;
44388                       l.parent = gp;
44389                     } else gp.right = null;
44390
44391                     x.left    = gp;
44392                     gp.parent = x;
44393                   }
44394                 }
44395                 if (r) {
44396                   p.left = r;
44397                   r.parent = p;
44398                 } else p.left = null;
44399
44400                 x.right  = p;
44401                 p.parent = x;
44402               } else { // right
44403                 if (gp) {
44404                   if (gp.right === p) {
44405                     /* zig-zig */
44406                     if (p.left) {
44407                       gp.right = p.left;
44408                       gp.right.parent = gp;
44409                     } else gp.right = null;
44410
44411                     p.left = gp;
44412                     gp.parent = p;
44413                   } else {
44414                     /* zig-zag */
44415                     if (r) {
44416                       gp.left = r;
44417                       r.parent = gp;
44418                     } else gp.left = null;
44419
44420                     x.right   = gp;
44421                     gp.parent = x;
44422                   }
44423                 }
44424                 if (l) {
44425                   p.right = l;
44426                   l.parent = p;
44427                 } else p.right = null;
44428
44429                 x.left   = p;
44430                 p.parent = x;
44431               }
44432             }
44433           }
44434
44435
44436           replace(u, v) {
44437             if (!u.parent) this._root = v;
44438             else if (u === u.parent.left) u.parent.left = v;
44439             else u.parent.right = v;
44440             if (v) v.parent = u.parent;
44441           }
44442
44443
44444           minNode(u = this._root) {
44445             if (u) while (u.left) u = u.left;
44446             return u;
44447           }
44448
44449
44450           maxNode(u = this._root) {
44451             if (u) while (u.right) u = u.right;
44452             return u;
44453           }
44454
44455
44456           insert(key, data) {
44457             var z = this._root;
44458             var p = null;
44459             var comp = this._compare;
44460             var cmp;
44461
44462             if (this._noDuplicates) {
44463               while (z) {
44464                 p = z;
44465                 cmp = comp(z.key, key);
44466                 if (cmp === 0) return;
44467                 else if (comp(z.key, key) < 0) z = z.right;
44468                 else z = z.left;
44469               }
44470             } else {
44471               while (z) {
44472                 p = z;
44473                 if (comp(z.key, key) < 0) z = z.right;
44474                 else z = z.left;
44475               }
44476             }
44477
44478             z = { key, data, left: null, right: null, parent: p };
44479
44480             if (!p)                          this._root = z;
44481             else if (comp(p.key, z.key) < 0) p.right = z;
44482             else                             p.left  = z;
44483
44484             this.splay(z);
44485             this._size++;
44486             return z;
44487           }
44488
44489
44490           find (key) {
44491             var z    = this._root;
44492             var comp = this._compare;
44493             while (z) {
44494               var cmp = comp(z.key, key);
44495               if      (cmp < 0) z = z.right;
44496               else if (cmp > 0) z = z.left;
44497               else              return z;
44498             }
44499             return null;
44500           }
44501
44502           /**
44503            * Whether the tree contains a node with the given key
44504            * @param  {Key} key
44505            * @return {boolean} true/false
44506            */
44507           contains (key) {
44508             var node       = this._root;
44509             var comparator = this._compare;
44510             while (node)  {
44511               var cmp = comparator(key, node.key);
44512               if      (cmp === 0) return true;
44513               else if (cmp < 0)   node = node.left;
44514               else                node = node.right;
44515             }
44516
44517             return false;
44518           }
44519
44520
44521           remove (key) {
44522             var z = this.find(key);
44523
44524             if (!z) return false;
44525
44526             this.splay(z);
44527
44528             if (!z.left) this.replace(z, z.right);
44529             else if (!z.right) this.replace(z, z.left);
44530             else {
44531               var y = this.minNode(z.right);
44532               if (y.parent !== z) {
44533                 this.replace(y, y.right);
44534                 y.right = z.right;
44535                 y.right.parent = y;
44536               }
44537               this.replace(z, y);
44538               y.left = z.left;
44539               y.left.parent = y;
44540             }
44541
44542             this._size--;
44543             return true;
44544           }
44545
44546
44547           removeNode(z) {
44548             if (!z) return false;
44549
44550             this.splay(z);
44551
44552             if (!z.left) this.replace(z, z.right);
44553             else if (!z.right) this.replace(z, z.left);
44554             else {
44555               var y = this.minNode(z.right);
44556               if (y.parent !== z) {
44557                 this.replace(y, y.right);
44558                 y.right = z.right;
44559                 y.right.parent = y;
44560               }
44561               this.replace(z, y);
44562               y.left = z.left;
44563               y.left.parent = y;
44564             }
44565
44566             this._size--;
44567             return true;
44568           }
44569
44570
44571           erase (key) {
44572             var z = this.find(key);
44573             if (!z) return;
44574
44575             this.splay(z);
44576
44577             var s = z.left;
44578             var t = z.right;
44579
44580             var sMax = null;
44581             if (s) {
44582               s.parent = null;
44583               sMax = this.maxNode(s);
44584               this.splay(sMax);
44585               this._root = sMax;
44586             }
44587             if (t) {
44588               if (s) sMax.right = t;
44589               else   this._root = t;
44590               t.parent = sMax;
44591             }
44592
44593             this._size--;
44594           }
44595
44596           /**
44597            * Removes and returns the node with smallest key
44598            * @return {?Node}
44599            */
44600           pop () {
44601             var node = this._root, returnValue = null;
44602             if (node) {
44603               while (node.left) node = node.left;
44604               returnValue = { key: node.key, data: node.data };
44605               this.remove(node.key);
44606             }
44607             return returnValue;
44608           }
44609
44610
44611           /* eslint-disable class-methods-use-this */
44612
44613           /**
44614            * Successor node
44615            * @param  {Node} node
44616            * @return {?Node}
44617            */
44618           next (node) {
44619             var successor = node;
44620             if (successor) {
44621               if (successor.right) {
44622                 successor = successor.right;
44623                 while (successor && successor.left) successor = successor.left;
44624               } else {
44625                 successor = node.parent;
44626                 while (successor && successor.right === node) {
44627                   node = successor; successor = successor.parent;
44628                 }
44629               }
44630             }
44631             return successor;
44632           }
44633
44634
44635           /**
44636            * Predecessor node
44637            * @param  {Node} node
44638            * @return {?Node}
44639            */
44640           prev (node) {
44641             var predecessor = node;
44642             if (predecessor) {
44643               if (predecessor.left) {
44644                 predecessor = predecessor.left;
44645                 while (predecessor && predecessor.right) predecessor = predecessor.right;
44646               } else {
44647                 predecessor = node.parent;
44648                 while (predecessor && predecessor.left === node) {
44649                   node = predecessor;
44650                   predecessor = predecessor.parent;
44651                 }
44652               }
44653             }
44654             return predecessor;
44655           }
44656           /* eslint-enable class-methods-use-this */
44657
44658
44659           /**
44660            * @param  {forEachCallback} callback
44661            * @return {SplayTree}
44662            */
44663           forEach(callback) {
44664             var current = this._root;
44665             var s = [], done = false, i = 0;
44666
44667             while (!done) {
44668               // Reach the left most Node of the current Node
44669               if (current) {
44670                 // Place pointer to a tree node on the stack
44671                 // before traversing the node's left subtree
44672                 s.push(current);
44673                 current = current.left;
44674               } else {
44675                 // BackTrack from the empty subtree and visit the Node
44676                 // at the top of the stack; however, if the stack is
44677                 // empty you are done
44678                 if (s.length > 0) {
44679                   current = s.pop();
44680                   callback(current, i++);
44681
44682                   // We have visited the node and its left
44683                   // subtree. Now, it's right subtree's turn
44684                   current = current.right;
44685                 } else done = true;
44686               }
44687             }
44688             return this;
44689           }
44690
44691
44692           /**
44693            * Walk key range from `low` to `high`. Stops if `fn` returns a value.
44694            * @param  {Key}      low
44695            * @param  {Key}      high
44696            * @param  {Function} fn
44697            * @param  {*?}       ctx
44698            * @return {SplayTree}
44699            */
44700           range(low, high, fn, ctx) {
44701             const Q = [];
44702             const compare = this._compare;
44703             let node = this._root, cmp;
44704
44705             while (Q.length !== 0 || node) {
44706               if (node) {
44707                 Q.push(node);
44708                 node = node.left;
44709               } else {
44710                 node = Q.pop();
44711                 cmp = compare(node.key, high);
44712                 if (cmp > 0) {
44713                   break;
44714                 } else if (compare(node.key, low) >= 0) {
44715                   if (fn.call(ctx, node)) return this; // stop if smth is returned
44716                 }
44717                 node = node.right;
44718               }
44719             }
44720             return this;
44721           }
44722
44723           /**
44724            * Returns all keys in order
44725            * @return {Array<Key>}
44726            */
44727           keys () {
44728             var current = this._root;
44729             var s = [], r = [], done = false;
44730
44731             while (!done) {
44732               if (current) {
44733                 s.push(current);
44734                 current = current.left;
44735               } else {
44736                 if (s.length > 0) {
44737                   current = s.pop();
44738                   r.push(current.key);
44739                   current = current.right;
44740                 } else done = true;
44741               }
44742             }
44743             return r;
44744           }
44745
44746
44747           /**
44748            * Returns `data` fields of all nodes in order.
44749            * @return {Array<Value>}
44750            */
44751           values () {
44752             var current = this._root;
44753             var s = [], r = [], done = false;
44754
44755             while (!done) {
44756               if (current) {
44757                 s.push(current);
44758                 current = current.left;
44759               } else {
44760                 if (s.length > 0) {
44761                   current = s.pop();
44762                   r.push(current.data);
44763                   current = current.right;
44764                 } else done = true;
44765               }
44766             }
44767             return r;
44768           }
44769
44770
44771           /**
44772            * Returns node at given index
44773            * @param  {number} index
44774            * @return {?Node}
44775            */
44776           at (index) {
44777             // removed after a consideration, more misleading than useful
44778             // index = index % this.size;
44779             // if (index < 0) index = this.size - index;
44780
44781             var current = this._root;
44782             var s = [], done = false, i = 0;
44783
44784             while (!done) {
44785               if (current) {
44786                 s.push(current);
44787                 current = current.left;
44788               } else {
44789                 if (s.length > 0) {
44790                   current = s.pop();
44791                   if (i === index) return current;
44792                   i++;
44793                   current = current.right;
44794                 } else done = true;
44795               }
44796             }
44797             return null;
44798           }
44799
44800           /**
44801            * Bulk-load items. Both array have to be same size
44802            * @param  {Array<Key>}    keys
44803            * @param  {Array<Value>}  [values]
44804            * @param  {Boolean}       [presort=false] Pre-sort keys and values, using
44805            *                                         tree's comparator. Sorting is done
44806            *                                         in-place
44807            * @return {AVLTree}
44808            */
44809           load(keys = [], values = [], presort = false) {
44810             if (this._size !== 0) throw new Error('bulk-load: tree is not empty');
44811             const size = keys.length;
44812             if (presort) sort(keys, values, 0, size - 1, this._compare);
44813             this._root = loadRecursive(null, keys, values, 0, size);
44814             this._size = size;
44815             return this;
44816           }
44817
44818
44819           min() {
44820             var node = this.minNode(this._root);
44821             if (node) return node.key;
44822             else      return null;
44823           }
44824
44825
44826           max() {
44827             var node = this.maxNode(this._root);
44828             if (node) return node.key;
44829             else      return null;
44830           }
44831
44832           isEmpty() { return this._root === null; }
44833           get size() { return this._size; }
44834
44835
44836           /**
44837            * Create a tree and load it with items
44838            * @param  {Array<Key>}          keys
44839            * @param  {Array<Value>?}        [values]
44840
44841            * @param  {Function?}            [comparator]
44842            * @param  {Boolean?}             [presort=false] Pre-sort keys and values, using
44843            *                                               tree's comparator. Sorting is done
44844            *                                               in-place
44845            * @param  {Boolean?}             [noDuplicates=false]   Allow duplicates
44846            * @return {SplayTree}
44847            */
44848           static createTree(keys, values, comparator, presort, noDuplicates) {
44849             return new SplayTree(comparator, noDuplicates).load(keys, values, presort);
44850           }
44851         }
44852
44853
44854         function loadRecursive (parent, keys, values, start, end) {
44855           const size = end - start;
44856           if (size > 0) {
44857             const middle = start + Math.floor(size / 2);
44858             const key    = keys[middle];
44859             const data   = values[middle];
44860             const node   = { key, data, parent };
44861             node.left    = loadRecursive(node, keys, values, start, middle);
44862             node.right   = loadRecursive(node, keys, values, middle + 1, end);
44863             return node;
44864           }
44865           return null;
44866         }
44867
44868
44869         function sort(keys, values, left, right, compare) {
44870           if (left >= right) return;
44871
44872           const pivot = keys[(left + right) >> 1];
44873           let i = left - 1;
44874           let j = right + 1;
44875
44876           while (true) {
44877             do i++; while (compare(keys[i], pivot) < 0);
44878             do j--; while (compare(keys[j], pivot) > 0);
44879             if (i >= j) break;
44880
44881             let tmp = keys[i];
44882             keys[i] = keys[j];
44883             keys[j] = tmp;
44884
44885             tmp = values[i];
44886             values[i] = values[j];
44887             values[j] = tmp;
44888           }
44889
44890           sort(keys, values,  left,     j, compare);
44891           sort(keys, values, j + 1, right, compare);
44892         }
44893
44894         const NORMAL               = 0;
44895         const NON_CONTRIBUTING     = 1;
44896         const SAME_TRANSITION      = 2;
44897         const DIFFERENT_TRANSITION = 3;
44898
44899         const INTERSECTION = 0;
44900         const UNION        = 1;
44901         const DIFFERENCE   = 2;
44902         const XOR          = 3;
44903
44904         /**
44905          * @param  {SweepEvent} event
44906          * @param  {SweepEvent} prev
44907          * @param  {Operation} operation
44908          */
44909         function computeFields (event, prev, operation) {
44910           // compute inOut and otherInOut fields
44911           if (prev === null) {
44912             event.inOut      = false;
44913             event.otherInOut = true;
44914
44915           // previous line segment in sweepline belongs to the same polygon
44916           } else {
44917             if (event.isSubject === prev.isSubject) {
44918               event.inOut      = !prev.inOut;
44919               event.otherInOut = prev.otherInOut;
44920
44921             // previous line segment in sweepline belongs to the clipping polygon
44922             } else {
44923               event.inOut      = !prev.otherInOut;
44924               event.otherInOut = prev.isVertical() ? !prev.inOut : prev.inOut;
44925             }
44926
44927             // compute prevInResult field
44928             if (prev) {
44929               event.prevInResult = (!inResult(prev, operation) || prev.isVertical())
44930                 ? prev.prevInResult : prev;
44931             }
44932           }
44933
44934           // check if the line segment belongs to the Boolean operation
44935           let isInResult = inResult(event, operation);
44936           if (isInResult) {
44937             event.resultTransition = determineResultTransition(event, operation);
44938           } else {
44939             event.resultTransition = 0;
44940           }
44941         }
44942
44943
44944         /* eslint-disable indent */
44945         function inResult(event, operation) {
44946           switch (event.type) {
44947             case NORMAL:
44948               switch (operation) {
44949                 case INTERSECTION:
44950                   return !event.otherInOut;
44951                 case UNION:
44952                   return event.otherInOut;
44953                 case DIFFERENCE:
44954                   // return (event.isSubject && !event.otherInOut) ||
44955                   //         (!event.isSubject && event.otherInOut);
44956                   return (event.isSubject && event.otherInOut) ||
44957                           (!event.isSubject && !event.otherInOut);
44958                 case XOR:
44959                   return true;
44960               }
44961               break;
44962             case SAME_TRANSITION:
44963               return operation === INTERSECTION || operation === UNION;
44964             case DIFFERENT_TRANSITION:
44965               return operation === DIFFERENCE;
44966             case NON_CONTRIBUTING:
44967               return false;
44968           }
44969           return false;
44970         }
44971         /* eslint-enable indent */
44972
44973
44974         function determineResultTransition(event, operation) {
44975           let thisIn = !event.inOut;
44976           let thatIn = !event.otherInOut;
44977
44978           let isIn;
44979           switch (operation) {
44980             case INTERSECTION:
44981               isIn = thisIn && thatIn; break;
44982             case UNION:
44983               isIn = thisIn || thatIn; break;
44984             case XOR:
44985               isIn = thisIn ^ thatIn; break;
44986             case DIFFERENCE:
44987               if (event.isSubject) {
44988                 isIn = thisIn && !thatIn;
44989               } else {
44990                 isIn = thatIn && !thisIn;
44991               }
44992               break;
44993           }
44994           return isIn ? +1 : -1;
44995         }
44996
44997         class SweepEvent {
44998
44999
45000           /**
45001            * Sweepline event
45002            *
45003            * @class {SweepEvent}
45004            * @param {Array.<Number>}  point
45005            * @param {Boolean}         left
45006            * @param {SweepEvent=}     otherEvent
45007            * @param {Boolean}         isSubject
45008            * @param {Number}          edgeType
45009            */
45010           constructor (point, left, otherEvent, isSubject, edgeType) {
45011
45012             /**
45013              * Is left endpoint?
45014              * @type {Boolean}
45015              */
45016             this.left = left;
45017
45018             /**
45019              * @type {Array.<Number>}
45020              */
45021             this.point = point;
45022
45023             /**
45024              * Other edge reference
45025              * @type {SweepEvent}
45026              */
45027             this.otherEvent = otherEvent;
45028
45029             /**
45030              * Belongs to source or clipping polygon
45031              * @type {Boolean}
45032              */
45033             this.isSubject = isSubject;
45034
45035             /**
45036              * Edge contribution type
45037              * @type {Number}
45038              */
45039             this.type = edgeType || NORMAL;
45040
45041
45042             /**
45043              * In-out transition for the sweepline crossing polygon
45044              * @type {Boolean}
45045              */
45046             this.inOut = false;
45047
45048
45049             /**
45050              * @type {Boolean}
45051              */
45052             this.otherInOut = false;
45053
45054             /**
45055              * Previous event in result?
45056              * @type {SweepEvent}
45057              */
45058             this.prevInResult = null;
45059
45060             /**
45061              * Type of result transition (0 = not in result, +1 = out-in, -1, in-out)
45062              * @type {Number}
45063              */
45064             this.resultTransition = 0;
45065
45066             // connection step
45067
45068             /**
45069              * @type {Number}
45070              */
45071             this.otherPos = -1;
45072
45073             /**
45074              * @type {Number}
45075              */
45076             this.outputContourId = -1;
45077
45078             this.isExteriorRing = true;   // TODO: Looks unused, remove?
45079           }
45080
45081
45082           /**
45083            * @param  {Array.<Number>}  p
45084            * @return {Boolean}
45085            */
45086           isBelow (p) {
45087             const p0 = this.point, p1 = this.otherEvent.point;
45088             return this.left
45089               ? (p0[0] - p[0]) * (p1[1] - p[1]) - (p1[0] - p[0]) * (p0[1] - p[1]) > 0
45090               // signedArea(this.point, this.otherEvent.point, p) > 0 :
45091               : (p1[0] - p[0]) * (p0[1] - p[1]) - (p0[0] - p[0]) * (p1[1] - p[1]) > 0;
45092               //signedArea(this.otherEvent.point, this.point, p) > 0;
45093           }
45094
45095
45096           /**
45097            * @param  {Array.<Number>}  p
45098            * @return {Boolean}
45099            */
45100           isAbove (p) {
45101             return !this.isBelow(p);
45102           }
45103
45104
45105           /**
45106            * @return {Boolean}
45107            */
45108           isVertical () {
45109             return this.point[0] === this.otherEvent.point[0];
45110           }
45111
45112
45113           /**
45114            * Does event belong to result?
45115            * @return {Boolean}
45116            */
45117           get inResult() {
45118             return this.resultTransition !== 0;
45119           }
45120
45121
45122           clone () {
45123             const copy = new SweepEvent(
45124               this.point, this.left, this.otherEvent, this.isSubject, this.type);
45125
45126             copy.contourId        = this.contourId;
45127             copy.resultTransition = this.resultTransition;
45128             copy.prevInResult     = this.prevInResult;
45129             copy.isExteriorRing   = this.isExteriorRing;
45130             copy.inOut            = this.inOut;
45131             copy.otherInOut       = this.otherInOut;
45132
45133             return copy;
45134           }
45135         }
45136
45137         function equals(p1, p2) {
45138           if (p1[0] === p2[0]) {
45139             if (p1[1] === p2[1]) {
45140               return true;
45141             } else {
45142               return false;
45143             }
45144           }
45145           return false;
45146         }
45147
45148         // const EPSILON = 1e-9;
45149         // const abs = Math.abs;
45150         // TODO https://github.com/w8r/martinez/issues/6#issuecomment-262847164
45151         // Precision problem.
45152         //
45153         // module.exports = function equals(p1, p2) {
45154         //   return abs(p1[0] - p2[0]) <= EPSILON && abs(p1[1] - p2[1]) <= EPSILON;
45155         // };
45156
45157         const epsilon$1 = 1.1102230246251565e-16;
45158         const splitter = 134217729;
45159         const resulterrbound = (3 + 8 * epsilon$1) * epsilon$1;
45160
45161         // fast_expansion_sum_zeroelim routine from oritinal code
45162         function sum$1(elen, e, flen, f, h) {
45163             let Q, Qnew, hh, bvirt;
45164             let enow = e[0];
45165             let fnow = f[0];
45166             let eindex = 0;
45167             let findex = 0;
45168             if ((fnow > enow) === (fnow > -enow)) {
45169                 Q = enow;
45170                 enow = e[++eindex];
45171             } else {
45172                 Q = fnow;
45173                 fnow = f[++findex];
45174             }
45175             let hindex = 0;
45176             if (eindex < elen && findex < flen) {
45177                 if ((fnow > enow) === (fnow > -enow)) {
45178                     Qnew = enow + Q;
45179                     hh = Q - (Qnew - enow);
45180                     enow = e[++eindex];
45181                 } else {
45182                     Qnew = fnow + Q;
45183                     hh = Q - (Qnew - fnow);
45184                     fnow = f[++findex];
45185                 }
45186                 Q = Qnew;
45187                 if (hh !== 0) {
45188                     h[hindex++] = hh;
45189                 }
45190                 while (eindex < elen && findex < flen) {
45191                     if ((fnow > enow) === (fnow > -enow)) {
45192                         Qnew = Q + enow;
45193                         bvirt = Qnew - Q;
45194                         hh = Q - (Qnew - bvirt) + (enow - bvirt);
45195                         enow = e[++eindex];
45196                     } else {
45197                         Qnew = Q + fnow;
45198                         bvirt = Qnew - Q;
45199                         hh = Q - (Qnew - bvirt) + (fnow - bvirt);
45200                         fnow = f[++findex];
45201                     }
45202                     Q = Qnew;
45203                     if (hh !== 0) {
45204                         h[hindex++] = hh;
45205                     }
45206                 }
45207             }
45208             while (eindex < elen) {
45209                 Qnew = Q + enow;
45210                 bvirt = Qnew - Q;
45211                 hh = Q - (Qnew - bvirt) + (enow - bvirt);
45212                 enow = e[++eindex];
45213                 Q = Qnew;
45214                 if (hh !== 0) {
45215                     h[hindex++] = hh;
45216                 }
45217             }
45218             while (findex < flen) {
45219                 Qnew = Q + fnow;
45220                 bvirt = Qnew - Q;
45221                 hh = Q - (Qnew - bvirt) + (fnow - bvirt);
45222                 fnow = f[++findex];
45223                 Q = Qnew;
45224                 if (hh !== 0) {
45225                     h[hindex++] = hh;
45226                 }
45227             }
45228             if (Q !== 0 || hindex === 0) {
45229                 h[hindex++] = Q;
45230             }
45231             return hindex;
45232         }
45233
45234         function estimate(elen, e) {
45235             let Q = e[0];
45236             for (let i = 1; i < elen; i++) Q += e[i];
45237             return Q;
45238         }
45239
45240         function vec(n) {
45241             return new Float64Array(n);
45242         }
45243
45244         const ccwerrboundA = (3 + 16 * epsilon$1) * epsilon$1;
45245         const ccwerrboundB = (2 + 12 * epsilon$1) * epsilon$1;
45246         const ccwerrboundC = (9 + 64 * epsilon$1) * epsilon$1 * epsilon$1;
45247
45248         const B = vec(4);
45249         const C1 = vec(8);
45250         const C2 = vec(12);
45251         const D = vec(16);
45252         const u = vec(4);
45253
45254         function orient2dadapt(ax, ay, bx, by, cx, cy, detsum) {
45255             let acxtail, acytail, bcxtail, bcytail;
45256             let bvirt, c, ahi, alo, bhi, blo, _i, _j, _0, s1, s0, t1, t0, u3;
45257
45258             const acx = ax - cx;
45259             const bcx = bx - cx;
45260             const acy = ay - cy;
45261             const bcy = by - cy;
45262
45263             s1 = acx * bcy;
45264             c = splitter * acx;
45265             ahi = c - (c - acx);
45266             alo = acx - ahi;
45267             c = splitter * bcy;
45268             bhi = c - (c - bcy);
45269             blo = bcy - bhi;
45270             s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
45271             t1 = acy * bcx;
45272             c = splitter * acy;
45273             ahi = c - (c - acy);
45274             alo = acy - ahi;
45275             c = splitter * bcx;
45276             bhi = c - (c - bcx);
45277             blo = bcx - bhi;
45278             t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
45279             _i = s0 - t0;
45280             bvirt = s0 - _i;
45281             B[0] = s0 - (_i + bvirt) + (bvirt - t0);
45282             _j = s1 + _i;
45283             bvirt = _j - s1;
45284             _0 = s1 - (_j - bvirt) + (_i - bvirt);
45285             _i = _0 - t1;
45286             bvirt = _0 - _i;
45287             B[1] = _0 - (_i + bvirt) + (bvirt - t1);
45288             u3 = _j + _i;
45289             bvirt = u3 - _j;
45290             B[2] = _j - (u3 - bvirt) + (_i - bvirt);
45291             B[3] = u3;
45292
45293             let det = estimate(4, B);
45294             let errbound = ccwerrboundB * detsum;
45295             if (det >= errbound || -det >= errbound) {
45296                 return det;
45297             }
45298
45299             bvirt = ax - acx;
45300             acxtail = ax - (acx + bvirt) + (bvirt - cx);
45301             bvirt = bx - bcx;
45302             bcxtail = bx - (bcx + bvirt) + (bvirt - cx);
45303             bvirt = ay - acy;
45304             acytail = ay - (acy + bvirt) + (bvirt - cy);
45305             bvirt = by - bcy;
45306             bcytail = by - (bcy + bvirt) + (bvirt - cy);
45307
45308             if (acxtail === 0 && acytail === 0 && bcxtail === 0 && bcytail === 0) {
45309                 return det;
45310             }
45311
45312             errbound = ccwerrboundC * detsum + resulterrbound * Math.abs(det);
45313             det += (acx * bcytail + bcy * acxtail) - (acy * bcxtail + bcx * acytail);
45314             if (det >= errbound || -det >= errbound) return det;
45315
45316             s1 = acxtail * bcy;
45317             c = splitter * acxtail;
45318             ahi = c - (c - acxtail);
45319             alo = acxtail - ahi;
45320             c = splitter * bcy;
45321             bhi = c - (c - bcy);
45322             blo = bcy - bhi;
45323             s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
45324             t1 = acytail * bcx;
45325             c = splitter * acytail;
45326             ahi = c - (c - acytail);
45327             alo = acytail - ahi;
45328             c = splitter * bcx;
45329             bhi = c - (c - bcx);
45330             blo = bcx - bhi;
45331             t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
45332             _i = s0 - t0;
45333             bvirt = s0 - _i;
45334             u[0] = s0 - (_i + bvirt) + (bvirt - t0);
45335             _j = s1 + _i;
45336             bvirt = _j - s1;
45337             _0 = s1 - (_j - bvirt) + (_i - bvirt);
45338             _i = _0 - t1;
45339             bvirt = _0 - _i;
45340             u[1] = _0 - (_i + bvirt) + (bvirt - t1);
45341             u3 = _j + _i;
45342             bvirt = u3 - _j;
45343             u[2] = _j - (u3 - bvirt) + (_i - bvirt);
45344             u[3] = u3;
45345             const C1len = sum$1(4, B, 4, u, C1);
45346
45347             s1 = acx * bcytail;
45348             c = splitter * acx;
45349             ahi = c - (c - acx);
45350             alo = acx - ahi;
45351             c = splitter * bcytail;
45352             bhi = c - (c - bcytail);
45353             blo = bcytail - bhi;
45354             s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
45355             t1 = acy * bcxtail;
45356             c = splitter * acy;
45357             ahi = c - (c - acy);
45358             alo = acy - ahi;
45359             c = splitter * bcxtail;
45360             bhi = c - (c - bcxtail);
45361             blo = bcxtail - bhi;
45362             t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
45363             _i = s0 - t0;
45364             bvirt = s0 - _i;
45365             u[0] = s0 - (_i + bvirt) + (bvirt - t0);
45366             _j = s1 + _i;
45367             bvirt = _j - s1;
45368             _0 = s1 - (_j - bvirt) + (_i - bvirt);
45369             _i = _0 - t1;
45370             bvirt = _0 - _i;
45371             u[1] = _0 - (_i + bvirt) + (bvirt - t1);
45372             u3 = _j + _i;
45373             bvirt = u3 - _j;
45374             u[2] = _j - (u3 - bvirt) + (_i - bvirt);
45375             u[3] = u3;
45376             const C2len = sum$1(C1len, C1, 4, u, C2);
45377
45378             s1 = acxtail * bcytail;
45379             c = splitter * acxtail;
45380             ahi = c - (c - acxtail);
45381             alo = acxtail - ahi;
45382             c = splitter * bcytail;
45383             bhi = c - (c - bcytail);
45384             blo = bcytail - bhi;
45385             s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
45386             t1 = acytail * bcxtail;
45387             c = splitter * acytail;
45388             ahi = c - (c - acytail);
45389             alo = acytail - ahi;
45390             c = splitter * bcxtail;
45391             bhi = c - (c - bcxtail);
45392             blo = bcxtail - bhi;
45393             t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
45394             _i = s0 - t0;
45395             bvirt = s0 - _i;
45396             u[0] = s0 - (_i + bvirt) + (bvirt - t0);
45397             _j = s1 + _i;
45398             bvirt = _j - s1;
45399             _0 = s1 - (_j - bvirt) + (_i - bvirt);
45400             _i = _0 - t1;
45401             bvirt = _0 - _i;
45402             u[1] = _0 - (_i + bvirt) + (bvirt - t1);
45403             u3 = _j + _i;
45404             bvirt = u3 - _j;
45405             u[2] = _j - (u3 - bvirt) + (_i - bvirt);
45406             u[3] = u3;
45407             const Dlen = sum$1(C2len, C2, 4, u, D);
45408
45409             return D[Dlen - 1];
45410         }
45411
45412         function orient2d(ax, ay, bx, by, cx, cy) {
45413             const detleft = (ay - cy) * (bx - cx);
45414             const detright = (ax - cx) * (by - cy);
45415             const det = detleft - detright;
45416
45417             if (detleft === 0 || detright === 0 || (detleft > 0) !== (detright > 0)) return det;
45418
45419             const detsum = Math.abs(detleft + detright);
45420             if (Math.abs(det) >= ccwerrboundA * detsum) return det;
45421
45422             return -orient2dadapt(ax, ay, bx, by, cx, cy, detsum);
45423         }
45424
45425         /**
45426          * Signed area of the triangle (p0, p1, p2)
45427          * @param  {Array.<Number>} p0
45428          * @param  {Array.<Number>} p1
45429          * @param  {Array.<Number>} p2
45430          * @return {Number}
45431          */
45432         function signedArea(p0, p1, p2) {
45433           const res = orient2d(p0[0], p0[1], p1[0], p1[1], p2[0], p2[1]);
45434           if (res > 0) return -1;
45435           if (res < 0) return 1;
45436           return 0;
45437         }
45438
45439         /**
45440          * @param  {SweepEvent} e1
45441          * @param  {SweepEvent} e2
45442          * @return {Number}
45443          */
45444         function compareEvents(e1, e2) {
45445           const p1 = e1.point;
45446           const p2 = e2.point;
45447
45448           // Different x-coordinate
45449           if (p1[0] > p2[0]) return 1;
45450           if (p1[0] < p2[0]) return -1;
45451
45452           // Different points, but same x-coordinate
45453           // Event with lower y-coordinate is processed first
45454           if (p1[1] !== p2[1]) return p1[1] > p2[1] ? 1 : -1;
45455
45456           return specialCases(e1, e2, p1);
45457         }
45458
45459
45460         /* eslint-disable no-unused-vars */
45461         function specialCases(e1, e2, p1, p2) {
45462           // Same coordinates, but one is a left endpoint and the other is
45463           // a right endpoint. The right endpoint is processed first
45464           if (e1.left !== e2.left)
45465             return e1.left ? 1 : -1;
45466
45467           // const p2 = e1.otherEvent.point, p3 = e2.otherEvent.point;
45468           // const sa = (p1[0] - p3[0]) * (p2[1] - p3[1]) - (p2[0] - p3[0]) * (p1[1] - p3[1])
45469           // Same coordinates, both events
45470           // are left endpoints or right endpoints.
45471           // not collinear
45472           if (signedArea(p1, e1.otherEvent.point, e2.otherEvent.point) !== 0) {
45473             // the event associate to the bottom segment is processed first
45474             return (!e1.isBelow(e2.otherEvent.point)) ? 1 : -1;
45475           }
45476
45477           return (!e1.isSubject && e2.isSubject) ? 1 : -1;
45478         }
45479         /* eslint-enable no-unused-vars */
45480
45481         /**
45482          * @param  {SweepEvent} se
45483          * @param  {Array.<Number>} p
45484          * @param  {Queue} queue
45485          * @return {Queue}
45486          */
45487         function divideSegment(se, p, queue)  {
45488           const r = new SweepEvent(p, false, se,            se.isSubject);
45489           const l = new SweepEvent(p, true,  se.otherEvent, se.isSubject);
45490
45491           /* eslint-disable no-console */
45492           if (equals(se.point, se.otherEvent.point)) {
45493             console.warn('what is that, a collapsed segment?', se);
45494           }
45495           /* eslint-enable no-console */
45496
45497           r.contourId = l.contourId = se.contourId;
45498
45499           // avoid a rounding error. The left event would be processed after the right event
45500           if (compareEvents(l, se.otherEvent) > 0) {
45501             se.otherEvent.left = true;
45502             l.left = false;
45503           }
45504
45505           // avoid a rounding error. The left event would be processed after the right event
45506           // if (compareEvents(se, r) > 0) {}
45507
45508           se.otherEvent.otherEvent = l;
45509           se.otherEvent = r;
45510
45511           queue.push(l);
45512           queue.push(r);
45513
45514           return queue;
45515         }
45516
45517         //const EPS = 1e-9;
45518
45519         /**
45520          * Finds the magnitude of the cross product of two vectors (if we pretend
45521          * they're in three dimensions)
45522          *
45523          * @param {Object} a First vector
45524          * @param {Object} b Second vector
45525          * @private
45526          * @returns {Number} The magnitude of the cross product
45527          */
45528         function crossProduct(a, b) {
45529           return (a[0] * b[1]) - (a[1] * b[0]);
45530         }
45531
45532         /**
45533          * Finds the dot product of two vectors.
45534          *
45535          * @param {Object} a First vector
45536          * @param {Object} b Second vector
45537          * @private
45538          * @returns {Number} The dot product
45539          */
45540         function dotProduct(a, b) {
45541           return (a[0] * b[0]) + (a[1] * b[1]);
45542         }
45543
45544         /**
45545          * Finds the intersection (if any) between two line segments a and b, given the
45546          * line segments' end points a1, a2 and b1, b2.
45547          *
45548          * This algorithm is based on Schneider and Eberly.
45549          * http://www.cimec.org.ar/~ncalvo/Schneider_Eberly.pdf
45550          * Page 244.
45551          *
45552          * @param {Array.<Number>} a1 point of first line
45553          * @param {Array.<Number>} a2 point of first line
45554          * @param {Array.<Number>} b1 point of second line
45555          * @param {Array.<Number>} b2 point of second line
45556          * @param {Boolean=}       noEndpointTouch whether to skip single touchpoints
45557          *                                         (meaning connected segments) as
45558          *                                         intersections
45559          * @returns {Array.<Array.<Number>>|Null} If the lines intersect, the point of
45560          * intersection. If they overlap, the two end points of the overlapping segment.
45561          * Otherwise, null.
45562          */
45563         function intersection (a1, a2, b1, b2, noEndpointTouch) {
45564           // The algorithm expects our lines in the form P + sd, where P is a point,
45565           // s is on the interval [0, 1], and d is a vector.
45566           // We are passed two points. P can be the first point of each pair. The
45567           // vector, then, could be thought of as the distance (in x and y components)
45568           // from the first point to the second point.
45569           // So first, let's make our vectors:
45570           const va = [a2[0] - a1[0], a2[1] - a1[1]];
45571           const vb = [b2[0] - b1[0], b2[1] - b1[1]];
45572           // We also define a function to convert back to regular point form:
45573
45574           /* eslint-disable arrow-body-style */
45575
45576           function toPoint(p, s, d) {
45577             return [
45578               p[0] + s * d[0],
45579               p[1] + s * d[1]
45580             ];
45581           }
45582
45583           /* eslint-enable arrow-body-style */
45584
45585           // The rest is pretty much a straight port of the algorithm.
45586           const e = [b1[0] - a1[0], b1[1] - a1[1]];
45587           let kross    = crossProduct(va, vb);
45588           let sqrKross = kross * kross;
45589           const sqrLenA  = dotProduct(va, va);
45590           //const sqrLenB  = dotProduct(vb, vb);
45591
45592           // Check for line intersection. This works because of the properties of the
45593           // cross product -- specifically, two vectors are parallel if and only if the
45594           // cross product is the 0 vector. The full calculation involves relative error
45595           // to account for possible very small line segments. See Schneider & Eberly
45596           // for details.
45597           if (sqrKross > 0/* EPS * sqrLenB * sqLenA */) {
45598             // If they're not parallel, then (because these are line segments) they
45599             // still might not actually intersect. This code checks that the
45600             // intersection point of the lines is actually on both line segments.
45601             const s = crossProduct(e, vb) / kross;
45602             if (s < 0 || s > 1) {
45603               // not on line segment a
45604               return null;
45605             }
45606             const t = crossProduct(e, va) / kross;
45607             if (t < 0 || t > 1) {
45608               // not on line segment b
45609               return null;
45610             }
45611             if (s === 0 || s === 1) {
45612               // on an endpoint of line segment a
45613               return noEndpointTouch ? null : [toPoint(a1, s, va)];
45614             }
45615             if (t === 0 || t === 1) {
45616               // on an endpoint of line segment b
45617               return noEndpointTouch ? null : [toPoint(b1, t, vb)];
45618             }
45619             return [toPoint(a1, s, va)];
45620           }
45621
45622           // If we've reached this point, then the lines are either parallel or the
45623           // same, but the segments could overlap partially or fully, or not at all.
45624           // So we need to find the overlap, if any. To do that, we can use e, which is
45625           // the (vector) difference between the two initial points. If this is parallel
45626           // with the line itself, then the two lines are the same line, and there will
45627           // be overlap.
45628           //const sqrLenE = dotProduct(e, e);
45629           kross = crossProduct(e, va);
45630           sqrKross = kross * kross;
45631
45632           if (sqrKross > 0 /* EPS * sqLenB * sqLenE */) {
45633           // Lines are just parallel, not the same. No overlap.
45634             return null;
45635           }
45636
45637           const sa = dotProduct(va, e) / sqrLenA;
45638           const sb = sa + dotProduct(va, vb) / sqrLenA;
45639           const smin = Math.min(sa, sb);
45640           const smax = Math.max(sa, sb);
45641
45642           // this is, essentially, the FindIntersection acting on floats from
45643           // Schneider & Eberly, just inlined into this function.
45644           if (smin <= 1 && smax >= 0) {
45645
45646             // overlap on an end point
45647             if (smin === 1) {
45648               return noEndpointTouch ? null : [toPoint(a1, smin > 0 ? smin : 0, va)];
45649             }
45650
45651             if (smax === 0) {
45652               return noEndpointTouch ? null : [toPoint(a1, smax < 1 ? smax : 1, va)];
45653             }
45654
45655             if (noEndpointTouch && smin === 0 && smax === 1) return null;
45656
45657             // There's overlap on a segment -- two points of intersection. Return both.
45658             return [
45659               toPoint(a1, smin > 0 ? smin : 0, va),
45660               toPoint(a1, smax < 1 ? smax : 1, va)
45661             ];
45662           }
45663
45664           return null;
45665         }
45666
45667         /**
45668          * @param  {SweepEvent} se1
45669          * @param  {SweepEvent} se2
45670          * @param  {Queue}      queue
45671          * @return {Number}
45672          */
45673         function possibleIntersection (se1, se2, queue) {
45674           // that disallows self-intersecting polygons,
45675           // did cost us half a day, so I'll leave it
45676           // out of respect
45677           // if (se1.isSubject === se2.isSubject) return;
45678           const inter = intersection(
45679             se1.point, se1.otherEvent.point,
45680             se2.point, se2.otherEvent.point
45681           );
45682
45683           const nintersections = inter ? inter.length : 0;
45684           if (nintersections === 0) return 0; // no intersection
45685
45686           // the line segments intersect at an endpoint of both line segments
45687           if ((nintersections === 1) &&
45688               (equals(se1.point, se2.point) ||
45689                equals(se1.otherEvent.point, se2.otherEvent.point))) {
45690             return 0;
45691           }
45692
45693           if (nintersections === 2 && se1.isSubject === se2.isSubject) {
45694             // if(se1.contourId === se2.contourId){
45695             // console.warn('Edges of the same polygon overlap',
45696             //   se1.point, se1.otherEvent.point, se2.point, se2.otherEvent.point);
45697             // }
45698             //throw new Error('Edges of the same polygon overlap');
45699             return 0;
45700           }
45701
45702           // The line segments associated to se1 and se2 intersect
45703           if (nintersections === 1) {
45704
45705             // if the intersection point is not an endpoint of se1
45706             if (!equals(se1.point, inter[0]) && !equals(se1.otherEvent.point, inter[0])) {
45707               divideSegment(se1, inter[0], queue);
45708             }
45709
45710             // if the intersection point is not an endpoint of se2
45711             if (!equals(se2.point, inter[0]) && !equals(se2.otherEvent.point, inter[0])) {
45712               divideSegment(se2, inter[0], queue);
45713             }
45714             return 1;
45715           }
45716
45717           // The line segments associated to se1 and se2 overlap
45718           const events        = [];
45719           let leftCoincide  = false;
45720           let rightCoincide = false;
45721
45722           if (equals(se1.point, se2.point)) {
45723             leftCoincide = true; // linked
45724           } else if (compareEvents(se1, se2) === 1) {
45725             events.push(se2, se1);
45726           } else {
45727             events.push(se1, se2);
45728           }
45729
45730           if (equals(se1.otherEvent.point, se2.otherEvent.point)) {
45731             rightCoincide = true;
45732           } else if (compareEvents(se1.otherEvent, se2.otherEvent) === 1) {
45733             events.push(se2.otherEvent, se1.otherEvent);
45734           } else {
45735             events.push(se1.otherEvent, se2.otherEvent);
45736           }
45737
45738           if ((leftCoincide && rightCoincide) || leftCoincide) {
45739             // both line segments are equal or share the left endpoint
45740             se2.type = NON_CONTRIBUTING;
45741             se1.type = (se2.inOut === se1.inOut)
45742               ? SAME_TRANSITION : DIFFERENT_TRANSITION;
45743
45744             if (leftCoincide && !rightCoincide) {
45745               // honestly no idea, but changing events selection from [2, 1]
45746               // to [0, 1] fixes the overlapping self-intersecting polygons issue
45747               divideSegment(events[1].otherEvent, events[0].point, queue);
45748             }
45749             return 2;
45750           }
45751
45752           // the line segments share the right endpoint
45753           if (rightCoincide) {
45754             divideSegment(events[0], events[1].point, queue);
45755             return 3;
45756           }
45757
45758           // no line segment includes totally the other one
45759           if (events[0] !== events[3].otherEvent) {
45760             divideSegment(events[0], events[1].point, queue);
45761             divideSegment(events[1], events[2].point, queue);
45762             return 3;
45763           }
45764
45765           // one line segment includes the other one
45766           divideSegment(events[0], events[1].point, queue);
45767           divideSegment(events[3].otherEvent, events[2].point, queue);
45768
45769           return 3;
45770         }
45771
45772         /**
45773          * @param  {SweepEvent} le1
45774          * @param  {SweepEvent} le2
45775          * @return {Number}
45776          */
45777         function compareSegments(le1, le2) {
45778           if (le1 === le2) return 0;
45779
45780           // Segments are not collinear
45781           if (signedArea(le1.point, le1.otherEvent.point, le2.point) !== 0 ||
45782             signedArea(le1.point, le1.otherEvent.point, le2.otherEvent.point) !== 0) {
45783
45784             // If they share their left endpoint use the right endpoint to sort
45785             if (equals(le1.point, le2.point)) return le1.isBelow(le2.otherEvent.point) ? -1 : 1;
45786
45787             // Different left endpoint: use the left endpoint to sort
45788             if (le1.point[0] === le2.point[0]) return le1.point[1] < le2.point[1] ? -1 : 1;
45789
45790             // has the line segment associated to e1 been inserted
45791             // into S after the line segment associated to e2 ?
45792             if (compareEvents(le1, le2) === 1) return le2.isAbove(le1.point) ? -1 : 1;
45793
45794             // The line segment associated to e2 has been inserted
45795             // into S after the line segment associated to e1
45796             return le1.isBelow(le2.point) ? -1 : 1;
45797           }
45798
45799           if (le1.isSubject === le2.isSubject) { // same polygon
45800             let p1 = le1.point, p2 = le2.point;
45801             if (p1[0] === p2[0] && p1[1] === p2[1]/*equals(le1.point, le2.point)*/) {
45802               p1 = le1.otherEvent.point; p2 = le2.otherEvent.point;
45803               if (p1[0] === p2[0] && p1[1] === p2[1]) return 0;
45804               else return le1.contourId > le2.contourId ? 1 : -1;
45805             }
45806           } else { // Segments are collinear, but belong to separate polygons
45807             return le1.isSubject ? -1 : 1;
45808           }
45809
45810           return compareEvents(le1, le2) === 1 ? 1 : -1;
45811         }
45812
45813         function subdivide(eventQueue, subject, clipping, sbbox, cbbox, operation) {
45814           const sweepLine = new SplayTree(compareSegments);
45815           const sortedEvents = [];
45816
45817           const rightbound = Math.min(sbbox[2], cbbox[2]);
45818
45819           let prev, next, begin;
45820
45821           while (eventQueue.length !== 0) {
45822             let event = eventQueue.pop();
45823             sortedEvents.push(event);
45824
45825             // optimization by bboxes for intersection and difference goes here
45826             if ((operation === INTERSECTION && event.point[0] > rightbound) ||
45827                 (operation === DIFFERENCE   && event.point[0] > sbbox[2])) {
45828               break;
45829             }
45830
45831             if (event.left) {
45832               next  = prev = sweepLine.insert(event);
45833               begin = sweepLine.minNode();
45834
45835               if (prev !== begin) prev = sweepLine.prev(prev);
45836               else                prev = null;
45837
45838               next = sweepLine.next(next);
45839
45840               const prevEvent = prev ? prev.key : null;
45841               let prevprevEvent;
45842               computeFields(event, prevEvent, operation);
45843               if (next) {
45844                 if (possibleIntersection(event, next.key, eventQueue) === 2) {
45845                   computeFields(event, prevEvent, operation);
45846                   computeFields(event, next.key, operation);
45847                 }
45848               }
45849
45850               if (prev) {
45851                 if (possibleIntersection(prev.key, event, eventQueue) === 2) {
45852                   let prevprev = prev;
45853                   if (prevprev !== begin) prevprev = sweepLine.prev(prevprev);
45854                   else                    prevprev = null;
45855
45856                   prevprevEvent = prevprev ? prevprev.key : null;
45857                   computeFields(prevEvent, prevprevEvent, operation);
45858                   computeFields(event,     prevEvent,     operation);
45859                 }
45860               }
45861             } else {
45862               event = event.otherEvent;
45863               next = prev = sweepLine.find(event);
45864
45865               if (prev && next) {
45866
45867                 if (prev !== begin) prev = sweepLine.prev(prev);
45868                 else                prev = null;
45869
45870                 next = sweepLine.next(next);
45871                 sweepLine.remove(event);
45872
45873                 if (next && prev) {
45874                   possibleIntersection(prev.key, next.key, eventQueue);
45875                 }
45876               }
45877             }
45878           }
45879           return sortedEvents;
45880         }
45881
45882         class Contour {
45883
45884           /**
45885            * Contour
45886            *
45887            * @class {Contour}
45888            */
45889           constructor() {
45890             this.points = [];
45891             this.holeIds = [];
45892             this.holeOf = null;
45893             this.depth = null;
45894           }
45895
45896           isExterior() {
45897             return this.holeOf == null;
45898           }
45899
45900         }
45901
45902         /**
45903          * @param  {Array.<SweepEvent>} sortedEvents
45904          * @return {Array.<SweepEvent>}
45905          */
45906         function orderEvents(sortedEvents) {
45907           let event, i, len, tmp;
45908           const resultEvents = [];
45909           for (i = 0, len = sortedEvents.length; i < len; i++) {
45910             event = sortedEvents[i];
45911             if ((event.left && event.inResult) ||
45912               (!event.left && event.otherEvent.inResult)) {
45913               resultEvents.push(event);
45914             }
45915           }
45916           // Due to overlapping edges the resultEvents array can be not wholly sorted
45917           let sorted = false;
45918           while (!sorted) {
45919             sorted = true;
45920             for (i = 0, len = resultEvents.length; i < len; i++) {
45921               if ((i + 1) < len &&
45922                 compareEvents(resultEvents[i], resultEvents[i + 1]) === 1) {
45923                 tmp = resultEvents[i];
45924                 resultEvents[i] = resultEvents[i + 1];
45925                 resultEvents[i + 1] = tmp;
45926                 sorted = false;
45927               }
45928             }
45929           }
45930
45931
45932           for (i = 0, len = resultEvents.length; i < len; i++) {
45933             event = resultEvents[i];
45934             event.otherPos = i;
45935           }
45936
45937           // imagine, the right event is found in the beginning of the queue,
45938           // when his left counterpart is not marked yet
45939           for (i = 0, len = resultEvents.length; i < len; i++) {
45940             event = resultEvents[i];
45941             if (!event.left) {
45942               tmp = event.otherPos;
45943               event.otherPos = event.otherEvent.otherPos;
45944               event.otherEvent.otherPos = tmp;
45945             }
45946           }
45947
45948           return resultEvents;
45949         }
45950
45951
45952         /**
45953          * @param  {Number} pos
45954          * @param  {Array.<SweepEvent>} resultEvents
45955          * @param  {Object>}    processed
45956          * @return {Number}
45957          */
45958         function nextPos(pos, resultEvents, processed, origPos) {
45959           let newPos = pos + 1,
45960               p = resultEvents[pos].point,
45961               p1;
45962           const length = resultEvents.length;
45963
45964           if (newPos < length)
45965             p1 = resultEvents[newPos].point;
45966
45967           while (newPos < length && p1[0] === p[0] && p1[1] === p[1]) {
45968             if (!processed[newPos]) {
45969               return newPos;
45970             } else   {
45971               newPos++;
45972             }
45973             p1 = resultEvents[newPos].point;
45974           }
45975
45976           newPos = pos - 1;
45977
45978           while (processed[newPos] && newPos > origPos) {
45979             newPos--;
45980           }
45981
45982           return newPos;
45983         }
45984
45985
45986         function initializeContourFromContext(event, contours, contourId) {
45987           const contour = new Contour();
45988           if (event.prevInResult != null) {
45989             const prevInResult = event.prevInResult;
45990             // Note that it is valid to query the "previous in result" for its output contour id,
45991             // because we must have already processed it (i.e., assigned an output contour id)
45992             // in an earlier iteration, otherwise it wouldn't be possible that it is "previous in
45993             // result".
45994             const lowerContourId = prevInResult.outputContourId;
45995             const lowerResultTransition = prevInResult.resultTransition;
45996             if (lowerResultTransition > 0) {
45997               // We are inside. Now we have to check if the thing below us is another hole or
45998               // an exterior contour.
45999               const lowerContour = contours[lowerContourId];
46000               if (lowerContour.holeOf != null) {
46001                 // The lower contour is a hole => Connect the new contour as a hole to its parent,
46002                 // and use same depth.
46003                 const parentContourId = lowerContour.holeOf;
46004                 contours[parentContourId].holeIds.push(contourId);
46005                 contour.holeOf = parentContourId;
46006                 contour.depth = contours[lowerContourId].depth;
46007               } else {
46008                 // The lower contour is an exterior contour => Connect the new contour as a hole,
46009                 // and increment depth.
46010                 contours[lowerContourId].holeIds.push(contourId);
46011                 contour.holeOf = lowerContourId;
46012                 contour.depth = contours[lowerContourId].depth + 1;
46013               }
46014             } else {
46015               // We are outside => this contour is an exterior contour of same depth.
46016               contour.holeOf = null;
46017               contour.depth = contours[lowerContourId].depth;
46018             }
46019           } else {
46020             // There is no lower/previous contour => this contour is an exterior contour of depth 0.
46021             contour.holeOf = null;
46022             contour.depth = 0;
46023           }
46024           return contour;
46025         }
46026
46027         /**
46028          * @param  {Array.<SweepEvent>} sortedEvents
46029          * @return {Array.<*>} polygons
46030          */
46031         function connectEdges(sortedEvents) {
46032           let i, len;
46033           const resultEvents = orderEvents(sortedEvents);
46034
46035           // "false"-filled array
46036           const processed = {};
46037           const contours = [];
46038
46039           for (i = 0, len = resultEvents.length; i < len; i++) {
46040
46041             if (processed[i]) {
46042               continue;
46043             }
46044
46045             const contourId = contours.length;
46046             const contour = initializeContourFromContext(resultEvents[i], contours, contourId);
46047
46048             // Helper function that combines marking an event as processed with assigning its output contour ID
46049             const markAsProcessed = (pos) => {
46050               processed[pos] = true;
46051               resultEvents[pos].outputContourId = contourId;
46052             };
46053
46054             let pos = i;
46055             let origPos = i;
46056
46057             const initial = resultEvents[i].point;
46058             contour.points.push(initial);
46059
46060             /* eslint no-constant-condition: "off" */
46061             while (true) {
46062               markAsProcessed(pos);
46063
46064               pos = resultEvents[pos].otherPos;
46065
46066               markAsProcessed(pos);
46067               contour.points.push(resultEvents[pos].point);
46068
46069               pos = nextPos(pos, resultEvents, processed, origPos);
46070
46071               if (pos == origPos) {
46072                 break;
46073               }
46074             }
46075
46076             contours.push(contour);
46077           }
46078
46079           return contours;
46080         }
46081
46082         var tinyqueue = TinyQueue;
46083         var _default$1 = TinyQueue;
46084
46085         function TinyQueue(data, compare) {
46086             if (!(this instanceof TinyQueue)) return new TinyQueue(data, compare);
46087
46088             this.data = data || [];
46089             this.length = this.data.length;
46090             this.compare = compare || defaultCompare$1;
46091
46092             if (this.length > 0) {
46093                 for (var i = (this.length >> 1) - 1; i >= 0; i--) this._down(i);
46094             }
46095         }
46096
46097         function defaultCompare$1(a, b) {
46098             return a < b ? -1 : a > b ? 1 : 0;
46099         }
46100
46101         TinyQueue.prototype = {
46102
46103             push: function (item) {
46104                 this.data.push(item);
46105                 this.length++;
46106                 this._up(this.length - 1);
46107             },
46108
46109             pop: function () {
46110                 if (this.length === 0) return undefined;
46111
46112                 var top = this.data[0];
46113                 this.length--;
46114
46115                 if (this.length > 0) {
46116                     this.data[0] = this.data[this.length];
46117                     this._down(0);
46118                 }
46119                 this.data.pop();
46120
46121                 return top;
46122             },
46123
46124             peek: function () {
46125                 return this.data[0];
46126             },
46127
46128             _up: function (pos) {
46129                 var data = this.data;
46130                 var compare = this.compare;
46131                 var item = data[pos];
46132
46133                 while (pos > 0) {
46134                     var parent = (pos - 1) >> 1;
46135                     var current = data[parent];
46136                     if (compare(item, current) >= 0) break;
46137                     data[pos] = current;
46138                     pos = parent;
46139                 }
46140
46141                 data[pos] = item;
46142             },
46143
46144             _down: function (pos) {
46145                 var data = this.data;
46146                 var compare = this.compare;
46147                 var halfLength = this.length >> 1;
46148                 var item = data[pos];
46149
46150                 while (pos < halfLength) {
46151                     var left = (pos << 1) + 1;
46152                     var right = left + 1;
46153                     var best = data[left];
46154
46155                     if (right < this.length && compare(data[right], best) < 0) {
46156                         left = right;
46157                         best = data[right];
46158                     }
46159                     if (compare(best, item) >= 0) break;
46160
46161                     data[pos] = best;
46162                     pos = left;
46163                 }
46164
46165                 data[pos] = item;
46166             }
46167         };
46168         tinyqueue.default = _default$1;
46169
46170         const max$2 = Math.max;
46171         const min = Math.min;
46172
46173         let contourId = 0;
46174
46175
46176         function processPolygon(contourOrHole, isSubject, depth, Q, bbox, isExteriorRing) {
46177           let i, len, s1, s2, e1, e2;
46178           for (i = 0, len = contourOrHole.length - 1; i < len; i++) {
46179             s1 = contourOrHole[i];
46180             s2 = contourOrHole[i + 1];
46181             e1 = new SweepEvent(s1, false, undefined, isSubject);
46182             e2 = new SweepEvent(s2, false, e1,        isSubject);
46183             e1.otherEvent = e2;
46184
46185             if (s1[0] === s2[0] && s1[1] === s2[1]) {
46186               continue; // skip collapsed edges, or it breaks
46187             }
46188
46189             e1.contourId = e2.contourId = depth;
46190             if (!isExteriorRing) {
46191               e1.isExteriorRing = false;
46192               e2.isExteriorRing = false;
46193             }
46194             if (compareEvents(e1, e2) > 0) {
46195               e2.left = true;
46196             } else {
46197               e1.left = true;
46198             }
46199
46200             const x = s1[0], y = s1[1];
46201             bbox[0] = min(bbox[0], x);
46202             bbox[1] = min(bbox[1], y);
46203             bbox[2] = max$2(bbox[2], x);
46204             bbox[3] = max$2(bbox[3], y);
46205
46206             // Pushing it so the queue is sorted from left to right,
46207             // with object on the left having the highest priority.
46208             Q.push(e1);
46209             Q.push(e2);
46210           }
46211         }
46212
46213
46214         function fillQueue(subject, clipping, sbbox, cbbox, operation) {
46215           const eventQueue = new tinyqueue(null, compareEvents);
46216           let polygonSet, isExteriorRing, i, ii, j, jj; //, k, kk;
46217
46218           for (i = 0, ii = subject.length; i < ii; i++) {
46219             polygonSet = subject[i];
46220             for (j = 0, jj = polygonSet.length; j < jj; j++) {
46221               isExteriorRing = j === 0;
46222               if (isExteriorRing) contourId++;
46223               processPolygon(polygonSet[j], true, contourId, eventQueue, sbbox, isExteriorRing);
46224             }
46225           }
46226
46227           for (i = 0, ii = clipping.length; i < ii; i++) {
46228             polygonSet = clipping[i];
46229             for (j = 0, jj = polygonSet.length; j < jj; j++) {
46230               isExteriorRing = j === 0;
46231               if (operation === DIFFERENCE) isExteriorRing = false;
46232               if (isExteriorRing) contourId++;
46233               processPolygon(polygonSet[j], false, contourId, eventQueue, cbbox, isExteriorRing);
46234             }
46235           }
46236
46237           return eventQueue;
46238         }
46239
46240         const EMPTY = [];
46241
46242
46243         function trivialOperation(subject, clipping, operation) {
46244           let result = null;
46245           if (subject.length * clipping.length === 0) {
46246             if        (operation === INTERSECTION) {
46247               result = EMPTY;
46248             } else if (operation === DIFFERENCE) {
46249               result = subject;
46250             } else if (operation === UNION ||
46251                        operation === XOR) {
46252               result = (subject.length === 0) ? clipping : subject;
46253             }
46254           }
46255           return result;
46256         }
46257
46258
46259         function compareBBoxes(subject, clipping, sbbox, cbbox, operation) {
46260           let result = null;
46261           if (sbbox[0] > cbbox[2] ||
46262               cbbox[0] > sbbox[2] ||
46263               sbbox[1] > cbbox[3] ||
46264               cbbox[1] > sbbox[3]) {
46265             if        (operation === INTERSECTION) {
46266               result = EMPTY;
46267             } else if (operation === DIFFERENCE) {
46268               result = subject;
46269             } else if (operation === UNION ||
46270                        operation === XOR) {
46271               result = subject.concat(clipping);
46272             }
46273           }
46274           return result;
46275         }
46276
46277
46278         function boolean(subject, clipping, operation) {
46279           if (typeof subject[0][0][0] === 'number') {
46280             subject = [subject];
46281           }
46282           if (typeof clipping[0][0][0] === 'number') {
46283             clipping = [clipping];
46284           }
46285           let trivial = trivialOperation(subject, clipping, operation);
46286           if (trivial) {
46287             return trivial === EMPTY ? null : trivial;
46288           }
46289           const sbbox = [Infinity, Infinity, -Infinity, -Infinity];
46290           const cbbox = [Infinity, Infinity, -Infinity, -Infinity];
46291
46292           // console.time('fill queue');
46293           const eventQueue = fillQueue(subject, clipping, sbbox, cbbox, operation);
46294           //console.timeEnd('fill queue');
46295
46296           trivial = compareBBoxes(subject, clipping, sbbox, cbbox, operation);
46297           if (trivial) {
46298             return trivial === EMPTY ? null : trivial;
46299           }
46300           // console.time('subdivide edges');
46301           const sortedEvents = subdivide(eventQueue, subject, clipping, sbbox, cbbox, operation);
46302           //console.timeEnd('subdivide edges');
46303
46304           // console.time('connect vertices');
46305           const contours = connectEdges(sortedEvents);
46306           //console.timeEnd('connect vertices');
46307
46308           // Convert contours to polygons
46309           const polygons = [];
46310           for (let i = 0; i < contours.length; i++) {
46311             let contour = contours[i];
46312             if (contour.isExterior()) {
46313               // The exterior ring goes first
46314               let rings = [contour.points];
46315               // Followed by holes if any
46316               for (let j = 0; j < contour.holeIds.length; j++) {
46317                 let holeId = contour.holeIds[j];
46318                 rings.push(contours[holeId].points);
46319               }
46320               polygons.push(rings);
46321             }
46322           }
46323
46324           return polygons;
46325         }
46326
46327         function union (subject, clipping) {
46328           return boolean(subject, clipping, UNION);
46329         }
46330
46331         var read$6 = function (buffer, offset, isLE, mLen, nBytes) {
46332           var e, m;
46333           var eLen = (nBytes * 8) - mLen - 1;
46334           var eMax = (1 << eLen) - 1;
46335           var eBias = eMax >> 1;
46336           var nBits = -7;
46337           var i = isLE ? (nBytes - 1) : 0;
46338           var d = isLE ? -1 : 1;
46339           var s = buffer[offset + i];
46340
46341           i += d;
46342
46343           e = s & ((1 << (-nBits)) - 1);
46344           s >>= (-nBits);
46345           nBits += eLen;
46346           for (; nBits > 0; e = (e * 256) + buffer[offset + i], i += d, nBits -= 8) {}
46347
46348           m = e & ((1 << (-nBits)) - 1);
46349           e >>= (-nBits);
46350           nBits += mLen;
46351           for (; nBits > 0; m = (m * 256) + buffer[offset + i], i += d, nBits -= 8) {}
46352
46353           if (e === 0) {
46354             e = 1 - eBias;
46355           } else if (e === eMax) {
46356             return m ? NaN : ((s ? -1 : 1) * Infinity)
46357           } else {
46358             m = m + Math.pow(2, mLen);
46359             e = e - eBias;
46360           }
46361           return (s ? -1 : 1) * m * Math.pow(2, e - mLen)
46362         };
46363
46364         var write$6 = function (buffer, value, offset, isLE, mLen, nBytes) {
46365           var e, m, c;
46366           var eLen = (nBytes * 8) - mLen - 1;
46367           var eMax = (1 << eLen) - 1;
46368           var eBias = eMax >> 1;
46369           var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0);
46370           var i = isLE ? 0 : (nBytes - 1);
46371           var d = isLE ? 1 : -1;
46372           var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0;
46373
46374           value = Math.abs(value);
46375
46376           if (isNaN(value) || value === Infinity) {
46377             m = isNaN(value) ? 1 : 0;
46378             e = eMax;
46379           } else {
46380             e = Math.floor(Math.log(value) / Math.LN2);
46381             if (value * (c = Math.pow(2, -e)) < 1) {
46382               e--;
46383               c *= 2;
46384             }
46385             if (e + eBias >= 1) {
46386               value += rt / c;
46387             } else {
46388               value += rt * Math.pow(2, 1 - eBias);
46389             }
46390             if (value * c >= 2) {
46391               e++;
46392               c /= 2;
46393             }
46394
46395             if (e + eBias >= eMax) {
46396               m = 0;
46397               e = eMax;
46398             } else if (e + eBias >= 1) {
46399               m = ((value * c) - 1) * Math.pow(2, mLen);
46400               e = e + eBias;
46401             } else {
46402               m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen);
46403               e = 0;
46404             }
46405           }
46406
46407           for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {}
46408
46409           e = (e << mLen) | m;
46410           eLen += mLen;
46411           for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {}
46412
46413           buffer[offset + i - d] |= s * 128;
46414         };
46415
46416         var ieee754 = {
46417                 read: read$6,
46418                 write: write$6
46419         };
46420
46421         var pbf = Pbf;
46422
46423
46424
46425         function Pbf(buf) {
46426             this.buf = ArrayBuffer.isView && ArrayBuffer.isView(buf) ? buf : new Uint8Array(buf || 0);
46427             this.pos = 0;
46428             this.type = 0;
46429             this.length = this.buf.length;
46430         }
46431
46432         Pbf.Varint  = 0; // varint: int32, int64, uint32, uint64, sint32, sint64, bool, enum
46433         Pbf.Fixed64 = 1; // 64-bit: double, fixed64, sfixed64
46434         Pbf.Bytes   = 2; // length-delimited: string, bytes, embedded messages, packed repeated fields
46435         Pbf.Fixed32 = 5; // 32-bit: float, fixed32, sfixed32
46436
46437         var SHIFT_LEFT_32 = (1 << 16) * (1 << 16),
46438             SHIFT_RIGHT_32 = 1 / SHIFT_LEFT_32;
46439
46440         // Threshold chosen based on both benchmarking and knowledge about browser string
46441         // data structures (which currently switch structure types at 12 bytes or more)
46442         var TEXT_DECODER_MIN_LENGTH = 12;
46443         var utf8TextDecoder = typeof TextDecoder === 'undefined' ? null : new TextDecoder('utf8');
46444
46445         Pbf.prototype = {
46446
46447             destroy: function() {
46448                 this.buf = null;
46449             },
46450
46451             // === READING =================================================================
46452
46453             readFields: function(readField, result, end) {
46454                 end = end || this.length;
46455
46456                 while (this.pos < end) {
46457                     var val = this.readVarint(),
46458                         tag = val >> 3,
46459                         startPos = this.pos;
46460
46461                     this.type = val & 0x7;
46462                     readField(tag, result, this);
46463
46464                     if (this.pos === startPos) this.skip(val);
46465                 }
46466                 return result;
46467             },
46468
46469             readMessage: function(readField, result) {
46470                 return this.readFields(readField, result, this.readVarint() + this.pos);
46471             },
46472
46473             readFixed32: function() {
46474                 var val = readUInt32(this.buf, this.pos);
46475                 this.pos += 4;
46476                 return val;
46477             },
46478
46479             readSFixed32: function() {
46480                 var val = readInt32(this.buf, this.pos);
46481                 this.pos += 4;
46482                 return val;
46483             },
46484
46485             // 64-bit int handling is based on github.com/dpw/node-buffer-more-ints (MIT-licensed)
46486
46487             readFixed64: function() {
46488                 var val = readUInt32(this.buf, this.pos) + readUInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32;
46489                 this.pos += 8;
46490                 return val;
46491             },
46492
46493             readSFixed64: function() {
46494                 var val = readUInt32(this.buf, this.pos) + readInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32;
46495                 this.pos += 8;
46496                 return val;
46497             },
46498
46499             readFloat: function() {
46500                 var val = ieee754.read(this.buf, this.pos, true, 23, 4);
46501                 this.pos += 4;
46502                 return val;
46503             },
46504
46505             readDouble: function() {
46506                 var val = ieee754.read(this.buf, this.pos, true, 52, 8);
46507                 this.pos += 8;
46508                 return val;
46509             },
46510
46511             readVarint: function(isSigned) {
46512                 var buf = this.buf,
46513                     val, b;
46514
46515                 b = buf[this.pos++]; val  =  b & 0x7f;        if (b < 0x80) return val;
46516                 b = buf[this.pos++]; val |= (b & 0x7f) << 7;  if (b < 0x80) return val;
46517                 b = buf[this.pos++]; val |= (b & 0x7f) << 14; if (b < 0x80) return val;
46518                 b = buf[this.pos++]; val |= (b & 0x7f) << 21; if (b < 0x80) return val;
46519                 b = buf[this.pos];   val |= (b & 0x0f) << 28;
46520
46521                 return readVarintRemainder(val, isSigned, this);
46522             },
46523
46524             readVarint64: function() { // for compatibility with v2.0.1
46525                 return this.readVarint(true);
46526             },
46527
46528             readSVarint: function() {
46529                 var num = this.readVarint();
46530                 return num % 2 === 1 ? (num + 1) / -2 : num / 2; // zigzag encoding
46531             },
46532
46533             readBoolean: function() {
46534                 return Boolean(this.readVarint());
46535             },
46536
46537             readString: function() {
46538                 var end = this.readVarint() + this.pos;
46539                 var pos = this.pos;
46540                 this.pos = end;
46541
46542                 if (end - pos >= TEXT_DECODER_MIN_LENGTH && utf8TextDecoder) {
46543                     // longer strings are fast with the built-in browser TextDecoder API
46544                     return readUtf8TextDecoder(this.buf, pos, end);
46545                 }
46546                 // short strings are fast with our custom implementation
46547                 return readUtf8(this.buf, pos, end);
46548             },
46549
46550             readBytes: function() {
46551                 var end = this.readVarint() + this.pos,
46552                     buffer = this.buf.subarray(this.pos, end);
46553                 this.pos = end;
46554                 return buffer;
46555             },
46556
46557             // verbose for performance reasons; doesn't affect gzipped size
46558
46559             readPackedVarint: function(arr, isSigned) {
46560                 if (this.type !== Pbf.Bytes) return arr.push(this.readVarint(isSigned));
46561                 var end = readPackedEnd(this);
46562                 arr = arr || [];
46563                 while (this.pos < end) arr.push(this.readVarint(isSigned));
46564                 return arr;
46565             },
46566             readPackedSVarint: function(arr) {
46567                 if (this.type !== Pbf.Bytes) return arr.push(this.readSVarint());
46568                 var end = readPackedEnd(this);
46569                 arr = arr || [];
46570                 while (this.pos < end) arr.push(this.readSVarint());
46571                 return arr;
46572             },
46573             readPackedBoolean: function(arr) {
46574                 if (this.type !== Pbf.Bytes) return arr.push(this.readBoolean());
46575                 var end = readPackedEnd(this);
46576                 arr = arr || [];
46577                 while (this.pos < end) arr.push(this.readBoolean());
46578                 return arr;
46579             },
46580             readPackedFloat: function(arr) {
46581                 if (this.type !== Pbf.Bytes) return arr.push(this.readFloat());
46582                 var end = readPackedEnd(this);
46583                 arr = arr || [];
46584                 while (this.pos < end) arr.push(this.readFloat());
46585                 return arr;
46586             },
46587             readPackedDouble: function(arr) {
46588                 if (this.type !== Pbf.Bytes) return arr.push(this.readDouble());
46589                 var end = readPackedEnd(this);
46590                 arr = arr || [];
46591                 while (this.pos < end) arr.push(this.readDouble());
46592                 return arr;
46593             },
46594             readPackedFixed32: function(arr) {
46595                 if (this.type !== Pbf.Bytes) return arr.push(this.readFixed32());
46596                 var end = readPackedEnd(this);
46597                 arr = arr || [];
46598                 while (this.pos < end) arr.push(this.readFixed32());
46599                 return arr;
46600             },
46601             readPackedSFixed32: function(arr) {
46602                 if (this.type !== Pbf.Bytes) return arr.push(this.readSFixed32());
46603                 var end = readPackedEnd(this);
46604                 arr = arr || [];
46605                 while (this.pos < end) arr.push(this.readSFixed32());
46606                 return arr;
46607             },
46608             readPackedFixed64: function(arr) {
46609                 if (this.type !== Pbf.Bytes) return arr.push(this.readFixed64());
46610                 var end = readPackedEnd(this);
46611                 arr = arr || [];
46612                 while (this.pos < end) arr.push(this.readFixed64());
46613                 return arr;
46614             },
46615             readPackedSFixed64: function(arr) {
46616                 if (this.type !== Pbf.Bytes) return arr.push(this.readSFixed64());
46617                 var end = readPackedEnd(this);
46618                 arr = arr || [];
46619                 while (this.pos < end) arr.push(this.readSFixed64());
46620                 return arr;
46621             },
46622
46623             skip: function(val) {
46624                 var type = val & 0x7;
46625                 if (type === Pbf.Varint) while (this.buf[this.pos++] > 0x7f) {}
46626                 else if (type === Pbf.Bytes) this.pos = this.readVarint() + this.pos;
46627                 else if (type === Pbf.Fixed32) this.pos += 4;
46628                 else if (type === Pbf.Fixed64) this.pos += 8;
46629                 else throw new Error('Unimplemented type: ' + type);
46630             },
46631
46632             // === WRITING =================================================================
46633
46634             writeTag: function(tag, type) {
46635                 this.writeVarint((tag << 3) | type);
46636             },
46637
46638             realloc: function(min) {
46639                 var length = this.length || 16;
46640
46641                 while (length < this.pos + min) length *= 2;
46642
46643                 if (length !== this.length) {
46644                     var buf = new Uint8Array(length);
46645                     buf.set(this.buf);
46646                     this.buf = buf;
46647                     this.length = length;
46648                 }
46649             },
46650
46651             finish: function() {
46652                 this.length = this.pos;
46653                 this.pos = 0;
46654                 return this.buf.subarray(0, this.length);
46655             },
46656
46657             writeFixed32: function(val) {
46658                 this.realloc(4);
46659                 writeInt32(this.buf, val, this.pos);
46660                 this.pos += 4;
46661             },
46662
46663             writeSFixed32: function(val) {
46664                 this.realloc(4);
46665                 writeInt32(this.buf, val, this.pos);
46666                 this.pos += 4;
46667             },
46668
46669             writeFixed64: function(val) {
46670                 this.realloc(8);
46671                 writeInt32(this.buf, val & -1, this.pos);
46672                 writeInt32(this.buf, Math.floor(val * SHIFT_RIGHT_32), this.pos + 4);
46673                 this.pos += 8;
46674             },
46675
46676             writeSFixed64: function(val) {
46677                 this.realloc(8);
46678                 writeInt32(this.buf, val & -1, this.pos);
46679                 writeInt32(this.buf, Math.floor(val * SHIFT_RIGHT_32), this.pos + 4);
46680                 this.pos += 8;
46681             },
46682
46683             writeVarint: function(val) {
46684                 val = +val || 0;
46685
46686                 if (val > 0xfffffff || val < 0) {
46687                     writeBigVarint(val, this);
46688                     return;
46689                 }
46690
46691                 this.realloc(4);
46692
46693                 this.buf[this.pos++] =           val & 0x7f  | (val > 0x7f ? 0x80 : 0); if (val <= 0x7f) return;
46694                 this.buf[this.pos++] = ((val >>>= 7) & 0x7f) | (val > 0x7f ? 0x80 : 0); if (val <= 0x7f) return;
46695                 this.buf[this.pos++] = ((val >>>= 7) & 0x7f) | (val > 0x7f ? 0x80 : 0); if (val <= 0x7f) return;
46696                 this.buf[this.pos++] =   (val >>> 7) & 0x7f;
46697             },
46698
46699             writeSVarint: function(val) {
46700                 this.writeVarint(val < 0 ? -val * 2 - 1 : val * 2);
46701             },
46702
46703             writeBoolean: function(val) {
46704                 this.writeVarint(Boolean(val));
46705             },
46706
46707             writeString: function(str) {
46708                 str = String(str);
46709                 this.realloc(str.length * 4);
46710
46711                 this.pos++; // reserve 1 byte for short string length
46712
46713                 var startPos = this.pos;
46714                 // write the string directly to the buffer and see how much was written
46715                 this.pos = writeUtf8(this.buf, str, this.pos);
46716                 var len = this.pos - startPos;
46717
46718                 if (len >= 0x80) makeRoomForExtraLength(startPos, len, this);
46719
46720                 // finally, write the message length in the reserved place and restore the position
46721                 this.pos = startPos - 1;
46722                 this.writeVarint(len);
46723                 this.pos += len;
46724             },
46725
46726             writeFloat: function(val) {
46727                 this.realloc(4);
46728                 ieee754.write(this.buf, val, this.pos, true, 23, 4);
46729                 this.pos += 4;
46730             },
46731
46732             writeDouble: function(val) {
46733                 this.realloc(8);
46734                 ieee754.write(this.buf, val, this.pos, true, 52, 8);
46735                 this.pos += 8;
46736             },
46737
46738             writeBytes: function(buffer) {
46739                 var len = buffer.length;
46740                 this.writeVarint(len);
46741                 this.realloc(len);
46742                 for (var i = 0; i < len; i++) this.buf[this.pos++] = buffer[i];
46743             },
46744
46745             writeRawMessage: function(fn, obj) {
46746                 this.pos++; // reserve 1 byte for short message length
46747
46748                 // write the message directly to the buffer and see how much was written
46749                 var startPos = this.pos;
46750                 fn(obj, this);
46751                 var len = this.pos - startPos;
46752
46753                 if (len >= 0x80) makeRoomForExtraLength(startPos, len, this);
46754
46755                 // finally, write the message length in the reserved place and restore the position
46756                 this.pos = startPos - 1;
46757                 this.writeVarint(len);
46758                 this.pos += len;
46759             },
46760
46761             writeMessage: function(tag, fn, obj) {
46762                 this.writeTag(tag, Pbf.Bytes);
46763                 this.writeRawMessage(fn, obj);
46764             },
46765
46766             writePackedVarint:   function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedVarint, arr);   },
46767             writePackedSVarint:  function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedSVarint, arr);  },
46768             writePackedBoolean:  function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedBoolean, arr);  },
46769             writePackedFloat:    function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedFloat, arr);    },
46770             writePackedDouble:   function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedDouble, arr);   },
46771             writePackedFixed32:  function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedFixed32, arr);  },
46772             writePackedSFixed32: function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedSFixed32, arr); },
46773             writePackedFixed64:  function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedFixed64, arr);  },
46774             writePackedSFixed64: function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedSFixed64, arr); },
46775
46776             writeBytesField: function(tag, buffer) {
46777                 this.writeTag(tag, Pbf.Bytes);
46778                 this.writeBytes(buffer);
46779             },
46780             writeFixed32Field: function(tag, val) {
46781                 this.writeTag(tag, Pbf.Fixed32);
46782                 this.writeFixed32(val);
46783             },
46784             writeSFixed32Field: function(tag, val) {
46785                 this.writeTag(tag, Pbf.Fixed32);
46786                 this.writeSFixed32(val);
46787             },
46788             writeFixed64Field: function(tag, val) {
46789                 this.writeTag(tag, Pbf.Fixed64);
46790                 this.writeFixed64(val);
46791             },
46792             writeSFixed64Field: function(tag, val) {
46793                 this.writeTag(tag, Pbf.Fixed64);
46794                 this.writeSFixed64(val);
46795             },
46796             writeVarintField: function(tag, val) {
46797                 this.writeTag(tag, Pbf.Varint);
46798                 this.writeVarint(val);
46799             },
46800             writeSVarintField: function(tag, val) {
46801                 this.writeTag(tag, Pbf.Varint);
46802                 this.writeSVarint(val);
46803             },
46804             writeStringField: function(tag, str) {
46805                 this.writeTag(tag, Pbf.Bytes);
46806                 this.writeString(str);
46807             },
46808             writeFloatField: function(tag, val) {
46809                 this.writeTag(tag, Pbf.Fixed32);
46810                 this.writeFloat(val);
46811             },
46812             writeDoubleField: function(tag, val) {
46813                 this.writeTag(tag, Pbf.Fixed64);
46814                 this.writeDouble(val);
46815             },
46816             writeBooleanField: function(tag, val) {
46817                 this.writeVarintField(tag, Boolean(val));
46818             }
46819         };
46820
46821         function readVarintRemainder(l, s, p) {
46822             var buf = p.buf,
46823                 h, b;
46824
46825             b = buf[p.pos++]; h  = (b & 0x70) >> 4;  if (b < 0x80) return toNum(l, h, s);
46826             b = buf[p.pos++]; h |= (b & 0x7f) << 3;  if (b < 0x80) return toNum(l, h, s);
46827             b = buf[p.pos++]; h |= (b & 0x7f) << 10; if (b < 0x80) return toNum(l, h, s);
46828             b = buf[p.pos++]; h |= (b & 0x7f) << 17; if (b < 0x80) return toNum(l, h, s);
46829             b = buf[p.pos++]; h |= (b & 0x7f) << 24; if (b < 0x80) return toNum(l, h, s);
46830             b = buf[p.pos++]; h |= (b & 0x01) << 31; if (b < 0x80) return toNum(l, h, s);
46831
46832             throw new Error('Expected varint not more than 10 bytes');
46833         }
46834
46835         function readPackedEnd(pbf) {
46836             return pbf.type === Pbf.Bytes ?
46837                 pbf.readVarint() + pbf.pos : pbf.pos + 1;
46838         }
46839
46840         function toNum(low, high, isSigned) {
46841             if (isSigned) {
46842                 return high * 0x100000000 + (low >>> 0);
46843             }
46844
46845             return ((high >>> 0) * 0x100000000) + (low >>> 0);
46846         }
46847
46848         function writeBigVarint(val, pbf) {
46849             var low, high;
46850
46851             if (val >= 0) {
46852                 low  = (val % 0x100000000) | 0;
46853                 high = (val / 0x100000000) | 0;
46854             } else {
46855                 low  = ~(-val % 0x100000000);
46856                 high = ~(-val / 0x100000000);
46857
46858                 if (low ^ 0xffffffff) {
46859                     low = (low + 1) | 0;
46860                 } else {
46861                     low = 0;
46862                     high = (high + 1) | 0;
46863                 }
46864             }
46865
46866             if (val >= 0x10000000000000000 || val < -0x10000000000000000) {
46867                 throw new Error('Given varint doesn\'t fit into 10 bytes');
46868             }
46869
46870             pbf.realloc(10);
46871
46872             writeBigVarintLow(low, high, pbf);
46873             writeBigVarintHigh(high, pbf);
46874         }
46875
46876         function writeBigVarintLow(low, high, pbf) {
46877             pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7;
46878             pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7;
46879             pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7;
46880             pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7;
46881             pbf.buf[pbf.pos]   = low & 0x7f;
46882         }
46883
46884         function writeBigVarintHigh(high, pbf) {
46885             var lsb = (high & 0x07) << 4;
46886
46887             pbf.buf[pbf.pos++] |= lsb         | ((high >>>= 3) ? 0x80 : 0); if (!high) return;
46888             pbf.buf[pbf.pos++]  = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) return;
46889             pbf.buf[pbf.pos++]  = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) return;
46890             pbf.buf[pbf.pos++]  = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) return;
46891             pbf.buf[pbf.pos++]  = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) return;
46892             pbf.buf[pbf.pos++]  = high & 0x7f;
46893         }
46894
46895         function makeRoomForExtraLength(startPos, len, pbf) {
46896             var extraLen =
46897                 len <= 0x3fff ? 1 :
46898                 len <= 0x1fffff ? 2 :
46899                 len <= 0xfffffff ? 3 : Math.floor(Math.log(len) / (Math.LN2 * 7));
46900
46901             // if 1 byte isn't enough for encoding message length, shift the data to the right
46902             pbf.realloc(extraLen);
46903             for (var i = pbf.pos - 1; i >= startPos; i--) pbf.buf[i + extraLen] = pbf.buf[i];
46904         }
46905
46906         function writePackedVarint(arr, pbf)   { for (var i = 0; i < arr.length; i++) pbf.writeVarint(arr[i]);   }
46907         function writePackedSVarint(arr, pbf)  { for (var i = 0; i < arr.length; i++) pbf.writeSVarint(arr[i]);  }
46908         function writePackedFloat(arr, pbf)    { for (var i = 0; i < arr.length; i++) pbf.writeFloat(arr[i]);    }
46909         function writePackedDouble(arr, pbf)   { for (var i = 0; i < arr.length; i++) pbf.writeDouble(arr[i]);   }
46910         function writePackedBoolean(arr, pbf)  { for (var i = 0; i < arr.length; i++) pbf.writeBoolean(arr[i]);  }
46911         function writePackedFixed32(arr, pbf)  { for (var i = 0; i < arr.length; i++) pbf.writeFixed32(arr[i]);  }
46912         function writePackedSFixed32(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeSFixed32(arr[i]); }
46913         function writePackedFixed64(arr, pbf)  { for (var i = 0; i < arr.length; i++) pbf.writeFixed64(arr[i]);  }
46914         function writePackedSFixed64(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeSFixed64(arr[i]); }
46915
46916         // Buffer code below from https://github.com/feross/buffer, MIT-licensed
46917
46918         function readUInt32(buf, pos) {
46919             return ((buf[pos]) |
46920                 (buf[pos + 1] << 8) |
46921                 (buf[pos + 2] << 16)) +
46922                 (buf[pos + 3] * 0x1000000);
46923         }
46924
46925         function writeInt32(buf, val, pos) {
46926             buf[pos] = val;
46927             buf[pos + 1] = (val >>> 8);
46928             buf[pos + 2] = (val >>> 16);
46929             buf[pos + 3] = (val >>> 24);
46930         }
46931
46932         function readInt32(buf, pos) {
46933             return ((buf[pos]) |
46934                 (buf[pos + 1] << 8) |
46935                 (buf[pos + 2] << 16)) +
46936                 (buf[pos + 3] << 24);
46937         }
46938
46939         function readUtf8(buf, pos, end) {
46940             var str = '';
46941             var i = pos;
46942
46943             while (i < end) {
46944                 var b0 = buf[i];
46945                 var c = null; // codepoint
46946                 var bytesPerSequence =
46947                     b0 > 0xEF ? 4 :
46948                     b0 > 0xDF ? 3 :
46949                     b0 > 0xBF ? 2 : 1;
46950
46951                 if (i + bytesPerSequence > end) break;
46952
46953                 var b1, b2, b3;
46954
46955                 if (bytesPerSequence === 1) {
46956                     if (b0 < 0x80) {
46957                         c = b0;
46958                     }
46959                 } else if (bytesPerSequence === 2) {
46960                     b1 = buf[i + 1];
46961                     if ((b1 & 0xC0) === 0x80) {
46962                         c = (b0 & 0x1F) << 0x6 | (b1 & 0x3F);
46963                         if (c <= 0x7F) {
46964                             c = null;
46965                         }
46966                     }
46967                 } else if (bytesPerSequence === 3) {
46968                     b1 = buf[i + 1];
46969                     b2 = buf[i + 2];
46970                     if ((b1 & 0xC0) === 0x80 && (b2 & 0xC0) === 0x80) {
46971                         c = (b0 & 0xF) << 0xC | (b1 & 0x3F) << 0x6 | (b2 & 0x3F);
46972                         if (c <= 0x7FF || (c >= 0xD800 && c <= 0xDFFF)) {
46973                             c = null;
46974                         }
46975                     }
46976                 } else if (bytesPerSequence === 4) {
46977                     b1 = buf[i + 1];
46978                     b2 = buf[i + 2];
46979                     b3 = buf[i + 3];
46980                     if ((b1 & 0xC0) === 0x80 && (b2 & 0xC0) === 0x80 && (b3 & 0xC0) === 0x80) {
46981                         c = (b0 & 0xF) << 0x12 | (b1 & 0x3F) << 0xC | (b2 & 0x3F) << 0x6 | (b3 & 0x3F);
46982                         if (c <= 0xFFFF || c >= 0x110000) {
46983                             c = null;
46984                         }
46985                     }
46986                 }
46987
46988                 if (c === null) {
46989                     c = 0xFFFD;
46990                     bytesPerSequence = 1;
46991
46992                 } else if (c > 0xFFFF) {
46993                     c -= 0x10000;
46994                     str += String.fromCharCode(c >>> 10 & 0x3FF | 0xD800);
46995                     c = 0xDC00 | c & 0x3FF;
46996                 }
46997
46998                 str += String.fromCharCode(c);
46999                 i += bytesPerSequence;
47000             }
47001
47002             return str;
47003         }
47004
47005         function readUtf8TextDecoder(buf, pos, end) {
47006             return utf8TextDecoder.decode(buf.subarray(pos, end));
47007         }
47008
47009         function writeUtf8(buf, str, pos) {
47010             for (var i = 0, c, lead; i < str.length; i++) {
47011                 c = str.charCodeAt(i); // code point
47012
47013                 if (c > 0xD7FF && c < 0xE000) {
47014                     if (lead) {
47015                         if (c < 0xDC00) {
47016                             buf[pos++] = 0xEF;
47017                             buf[pos++] = 0xBF;
47018                             buf[pos++] = 0xBD;
47019                             lead = c;
47020                             continue;
47021                         } else {
47022                             c = lead - 0xD800 << 10 | c - 0xDC00 | 0x10000;
47023                             lead = null;
47024                         }
47025                     } else {
47026                         if (c > 0xDBFF || (i + 1 === str.length)) {
47027                             buf[pos++] = 0xEF;
47028                             buf[pos++] = 0xBF;
47029                             buf[pos++] = 0xBD;
47030                         } else {
47031                             lead = c;
47032                         }
47033                         continue;
47034                     }
47035                 } else if (lead) {
47036                     buf[pos++] = 0xEF;
47037                     buf[pos++] = 0xBF;
47038                     buf[pos++] = 0xBD;
47039                     lead = null;
47040                 }
47041
47042                 if (c < 0x80) {
47043                     buf[pos++] = c;
47044                 } else {
47045                     if (c < 0x800) {
47046                         buf[pos++] = c >> 0x6 | 0xC0;
47047                     } else {
47048                         if (c < 0x10000) {
47049                             buf[pos++] = c >> 0xC | 0xE0;
47050                         } else {
47051                             buf[pos++] = c >> 0x12 | 0xF0;
47052                             buf[pos++] = c >> 0xC & 0x3F | 0x80;
47053                         }
47054                         buf[pos++] = c >> 0x6 & 0x3F | 0x80;
47055                     }
47056                     buf[pos++] = c & 0x3F | 0x80;
47057                 }
47058             }
47059             return pos;
47060         }
47061
47062         var pointGeometry = Point;
47063
47064         /**
47065          * A standalone point geometry with useful accessor, comparison, and
47066          * modification methods.
47067          *
47068          * @class Point
47069          * @param {Number} x the x-coordinate. this could be longitude or screen
47070          * pixels, or any other sort of unit.
47071          * @param {Number} y the y-coordinate. this could be latitude or screen
47072          * pixels, or any other sort of unit.
47073          * @example
47074          * var point = new Point(-77, 38);
47075          */
47076         function Point(x, y) {
47077             this.x = x;
47078             this.y = y;
47079         }
47080
47081         Point.prototype = {
47082
47083             /**
47084              * Clone this point, returning a new point that can be modified
47085              * without affecting the old one.
47086              * @return {Point} the clone
47087              */
47088             clone: function() { return new Point(this.x, this.y); },
47089
47090             /**
47091              * Add this point's x & y coordinates to another point,
47092              * yielding a new point.
47093              * @param {Point} p the other point
47094              * @return {Point} output point
47095              */
47096             add:     function(p) { return this.clone()._add(p); },
47097
47098             /**
47099              * Subtract this point's x & y coordinates to from point,
47100              * yielding a new point.
47101              * @param {Point} p the other point
47102              * @return {Point} output point
47103              */
47104             sub:     function(p) { return this.clone()._sub(p); },
47105
47106             /**
47107              * Multiply this point's x & y coordinates by point,
47108              * yielding a new point.
47109              * @param {Point} p the other point
47110              * @return {Point} output point
47111              */
47112             multByPoint:    function(p) { return this.clone()._multByPoint(p); },
47113
47114             /**
47115              * Divide this point's x & y coordinates by point,
47116              * yielding a new point.
47117              * @param {Point} p the other point
47118              * @return {Point} output point
47119              */
47120             divByPoint:     function(p) { return this.clone()._divByPoint(p); },
47121
47122             /**
47123              * Multiply this point's x & y coordinates by a factor,
47124              * yielding a new point.
47125              * @param {Point} k factor
47126              * @return {Point} output point
47127              */
47128             mult:    function(k) { return this.clone()._mult(k); },
47129
47130             /**
47131              * Divide this point's x & y coordinates by a factor,
47132              * yielding a new point.
47133              * @param {Point} k factor
47134              * @return {Point} output point
47135              */
47136             div:     function(k) { return this.clone()._div(k); },
47137
47138             /**
47139              * Rotate this point around the 0, 0 origin by an angle a,
47140              * given in radians
47141              * @param {Number} a angle to rotate around, in radians
47142              * @return {Point} output point
47143              */
47144             rotate:  function(a) { return this.clone()._rotate(a); },
47145
47146             /**
47147              * Rotate this point around p point by an angle a,
47148              * given in radians
47149              * @param {Number} a angle to rotate around, in radians
47150              * @param {Point} p Point to rotate around
47151              * @return {Point} output point
47152              */
47153             rotateAround:  function(a,p) { return this.clone()._rotateAround(a,p); },
47154
47155             /**
47156              * Multiply this point by a 4x1 transformation matrix
47157              * @param {Array<Number>} m transformation matrix
47158              * @return {Point} output point
47159              */
47160             matMult: function(m) { return this.clone()._matMult(m); },
47161
47162             /**
47163              * Calculate this point but as a unit vector from 0, 0, meaning
47164              * that the distance from the resulting point to the 0, 0
47165              * coordinate will be equal to 1 and the angle from the resulting
47166              * point to the 0, 0 coordinate will be the same as before.
47167              * @return {Point} unit vector point
47168              */
47169             unit:    function() { return this.clone()._unit(); },
47170
47171             /**
47172              * Compute a perpendicular point, where the new y coordinate
47173              * is the old x coordinate and the new x coordinate is the old y
47174              * coordinate multiplied by -1
47175              * @return {Point} perpendicular point
47176              */
47177             perp:    function() { return this.clone()._perp(); },
47178
47179             /**
47180              * Return a version of this point with the x & y coordinates
47181              * rounded to integers.
47182              * @return {Point} rounded point
47183              */
47184             round:   function() { return this.clone()._round(); },
47185
47186             /**
47187              * Return the magitude of this point: this is the Euclidean
47188              * distance from the 0, 0 coordinate to this point's x and y
47189              * coordinates.
47190              * @return {Number} magnitude
47191              */
47192             mag: function() {
47193                 return Math.sqrt(this.x * this.x + this.y * this.y);
47194             },
47195
47196             /**
47197              * Judge whether this point is equal to another point, returning
47198              * true or false.
47199              * @param {Point} other the other point
47200              * @return {boolean} whether the points are equal
47201              */
47202             equals: function(other) {
47203                 return this.x === other.x &&
47204                        this.y === other.y;
47205             },
47206
47207             /**
47208              * Calculate the distance from this point to another point
47209              * @param {Point} p the other point
47210              * @return {Number} distance
47211              */
47212             dist: function(p) {
47213                 return Math.sqrt(this.distSqr(p));
47214             },
47215
47216             /**
47217              * Calculate the distance from this point to another point,
47218              * without the square root step. Useful if you're comparing
47219              * relative distances.
47220              * @param {Point} p the other point
47221              * @return {Number} distance
47222              */
47223             distSqr: function(p) {
47224                 var dx = p.x - this.x,
47225                     dy = p.y - this.y;
47226                 return dx * dx + dy * dy;
47227             },
47228
47229             /**
47230              * Get the angle from the 0, 0 coordinate to this point, in radians
47231              * coordinates.
47232              * @return {Number} angle
47233              */
47234             angle: function() {
47235                 return Math.atan2(this.y, this.x);
47236             },
47237
47238             /**
47239              * Get the angle from this point to another point, in radians
47240              * @param {Point} b the other point
47241              * @return {Number} angle
47242              */
47243             angleTo: function(b) {
47244                 return Math.atan2(this.y - b.y, this.x - b.x);
47245             },
47246
47247             /**
47248              * Get the angle between this point and another point, in radians
47249              * @param {Point} b the other point
47250              * @return {Number} angle
47251              */
47252             angleWith: function(b) {
47253                 return this.angleWithSep(b.x, b.y);
47254             },
47255
47256             /*
47257              * Find the angle of the two vectors, solving the formula for
47258              * the cross product a x b = |a||b|sin(θ) for θ.
47259              * @param {Number} x the x-coordinate
47260              * @param {Number} y the y-coordinate
47261              * @return {Number} the angle in radians
47262              */
47263             angleWithSep: function(x, y) {
47264                 return Math.atan2(
47265                     this.x * y - this.y * x,
47266                     this.x * x + this.y * y);
47267             },
47268
47269             _matMult: function(m) {
47270                 var x = m[0] * this.x + m[1] * this.y,
47271                     y = m[2] * this.x + m[3] * this.y;
47272                 this.x = x;
47273                 this.y = y;
47274                 return this;
47275             },
47276
47277             _add: function(p) {
47278                 this.x += p.x;
47279                 this.y += p.y;
47280                 return this;
47281             },
47282
47283             _sub: function(p) {
47284                 this.x -= p.x;
47285                 this.y -= p.y;
47286                 return this;
47287             },
47288
47289             _mult: function(k) {
47290                 this.x *= k;
47291                 this.y *= k;
47292                 return this;
47293             },
47294
47295             _div: function(k) {
47296                 this.x /= k;
47297                 this.y /= k;
47298                 return this;
47299             },
47300
47301             _multByPoint: function(p) {
47302                 this.x *= p.x;
47303                 this.y *= p.y;
47304                 return this;
47305             },
47306
47307             _divByPoint: function(p) {
47308                 this.x /= p.x;
47309                 this.y /= p.y;
47310                 return this;
47311             },
47312
47313             _unit: function() {
47314                 this._div(this.mag());
47315                 return this;
47316             },
47317
47318             _perp: function() {
47319                 var y = this.y;
47320                 this.y = this.x;
47321                 this.x = -y;
47322                 return this;
47323             },
47324
47325             _rotate: function(angle) {
47326                 var cos = Math.cos(angle),
47327                     sin = Math.sin(angle),
47328                     x = cos * this.x - sin * this.y,
47329                     y = sin * this.x + cos * this.y;
47330                 this.x = x;
47331                 this.y = y;
47332                 return this;
47333             },
47334
47335             _rotateAround: function(angle, p) {
47336                 var cos = Math.cos(angle),
47337                     sin = Math.sin(angle),
47338                     x = p.x + cos * (this.x - p.x) - sin * (this.y - p.y),
47339                     y = p.y + sin * (this.x - p.x) + cos * (this.y - p.y);
47340                 this.x = x;
47341                 this.y = y;
47342                 return this;
47343             },
47344
47345             _round: function() {
47346                 this.x = Math.round(this.x);
47347                 this.y = Math.round(this.y);
47348                 return this;
47349             }
47350         };
47351
47352         /**
47353          * Construct a point from an array if necessary, otherwise if the input
47354          * is already a Point, or an unknown type, return it unchanged
47355          * @param {Array<Number>|Point|*} a any kind of input value
47356          * @return {Point} constructed point, or passed-through value.
47357          * @example
47358          * // this
47359          * var point = Point.convert([0, 1]);
47360          * // is equivalent to
47361          * var point = new Point(0, 1);
47362          */
47363         Point.convert = function (a) {
47364             if (a instanceof Point) {
47365                 return a;
47366             }
47367             if (Array.isArray(a)) {
47368                 return new Point(a[0], a[1]);
47369             }
47370             return a;
47371         };
47372
47373         var vectortilefeature = VectorTileFeature;
47374
47375         function VectorTileFeature(pbf, end, extent, keys, values) {
47376             // Public
47377             this.properties = {};
47378             this.extent = extent;
47379             this.type = 0;
47380
47381             // Private
47382             this._pbf = pbf;
47383             this._geometry = -1;
47384             this._keys = keys;
47385             this._values = values;
47386
47387             pbf.readFields(readFeature, this, end);
47388         }
47389
47390         function readFeature(tag, feature, pbf) {
47391             if (tag == 1) feature.id = pbf.readVarint();
47392             else if (tag == 2) readTag(pbf, feature);
47393             else if (tag == 3) feature.type = pbf.readVarint();
47394             else if (tag == 4) feature._geometry = pbf.pos;
47395         }
47396
47397         function readTag(pbf, feature) {
47398             var end = pbf.readVarint() + pbf.pos;
47399
47400             while (pbf.pos < end) {
47401                 var key = feature._keys[pbf.readVarint()],
47402                     value = feature._values[pbf.readVarint()];
47403                 feature.properties[key] = value;
47404             }
47405         }
47406
47407         VectorTileFeature.types = ['Unknown', 'Point', 'LineString', 'Polygon'];
47408
47409         VectorTileFeature.prototype.loadGeometry = function() {
47410             var pbf = this._pbf;
47411             pbf.pos = this._geometry;
47412
47413             var end = pbf.readVarint() + pbf.pos,
47414                 cmd = 1,
47415                 length = 0,
47416                 x = 0,
47417                 y = 0,
47418                 lines = [],
47419                 line;
47420
47421             while (pbf.pos < end) {
47422                 if (length <= 0) {
47423                     var cmdLen = pbf.readVarint();
47424                     cmd = cmdLen & 0x7;
47425                     length = cmdLen >> 3;
47426                 }
47427
47428                 length--;
47429
47430                 if (cmd === 1 || cmd === 2) {
47431                     x += pbf.readSVarint();
47432                     y += pbf.readSVarint();
47433
47434                     if (cmd === 1) { // moveTo
47435                         if (line) lines.push(line);
47436                         line = [];
47437                     }
47438
47439                     line.push(new pointGeometry(x, y));
47440
47441                 } else if (cmd === 7) {
47442
47443                     // Workaround for https://github.com/mapbox/mapnik-vector-tile/issues/90
47444                     if (line) {
47445                         line.push(line[0].clone()); // closePolygon
47446                     }
47447
47448                 } else {
47449                     throw new Error('unknown command ' + cmd);
47450                 }
47451             }
47452
47453             if (line) lines.push(line);
47454
47455             return lines;
47456         };
47457
47458         VectorTileFeature.prototype.bbox = function() {
47459             var pbf = this._pbf;
47460             pbf.pos = this._geometry;
47461
47462             var end = pbf.readVarint() + pbf.pos,
47463                 cmd = 1,
47464                 length = 0,
47465                 x = 0,
47466                 y = 0,
47467                 x1 = Infinity,
47468                 x2 = -Infinity,
47469                 y1 = Infinity,
47470                 y2 = -Infinity;
47471
47472             while (pbf.pos < end) {
47473                 if (length <= 0) {
47474                     var cmdLen = pbf.readVarint();
47475                     cmd = cmdLen & 0x7;
47476                     length = cmdLen >> 3;
47477                 }
47478
47479                 length--;
47480
47481                 if (cmd === 1 || cmd === 2) {
47482                     x += pbf.readSVarint();
47483                     y += pbf.readSVarint();
47484                     if (x < x1) x1 = x;
47485                     if (x > x2) x2 = x;
47486                     if (y < y1) y1 = y;
47487                     if (y > y2) y2 = y;
47488
47489                 } else if (cmd !== 7) {
47490                     throw new Error('unknown command ' + cmd);
47491                 }
47492             }
47493
47494             return [x1, y1, x2, y2];
47495         };
47496
47497         VectorTileFeature.prototype.toGeoJSON = function(x, y, z) {
47498             var size = this.extent * Math.pow(2, z),
47499                 x0 = this.extent * x,
47500                 y0 = this.extent * y,
47501                 coords = this.loadGeometry(),
47502                 type = VectorTileFeature.types[this.type],
47503                 i, j;
47504
47505             function project(line) {
47506                 for (var j = 0; j < line.length; j++) {
47507                     var p = line[j], y2 = 180 - (p.y + y0) * 360 / size;
47508                     line[j] = [
47509                         (p.x + x0) * 360 / size - 180,
47510                         360 / Math.PI * Math.atan(Math.exp(y2 * Math.PI / 180)) - 90
47511                     ];
47512                 }
47513             }
47514
47515             switch (this.type) {
47516             case 1:
47517                 var points = [];
47518                 for (i = 0; i < coords.length; i++) {
47519                     points[i] = coords[i][0];
47520                 }
47521                 coords = points;
47522                 project(coords);
47523                 break;
47524
47525             case 2:
47526                 for (i = 0; i < coords.length; i++) {
47527                     project(coords[i]);
47528                 }
47529                 break;
47530
47531             case 3:
47532                 coords = classifyRings(coords);
47533                 for (i = 0; i < coords.length; i++) {
47534                     for (j = 0; j < coords[i].length; j++) {
47535                         project(coords[i][j]);
47536                     }
47537                 }
47538                 break;
47539             }
47540
47541             if (coords.length === 1) {
47542                 coords = coords[0];
47543             } else {
47544                 type = 'Multi' + type;
47545             }
47546
47547             var result = {
47548                 type: "Feature",
47549                 geometry: {
47550                     type: type,
47551                     coordinates: coords
47552                 },
47553                 properties: this.properties
47554             };
47555
47556             if ('id' in this) {
47557                 result.id = this.id;
47558             }
47559
47560             return result;
47561         };
47562
47563         // classifies an array of rings into polygons with outer rings and holes
47564
47565         function classifyRings(rings) {
47566             var len = rings.length;
47567
47568             if (len <= 1) return [rings];
47569
47570             var polygons = [],
47571                 polygon,
47572                 ccw;
47573
47574             for (var i = 0; i < len; i++) {
47575                 var area = signedArea$1(rings[i]);
47576                 if (area === 0) continue;
47577
47578                 if (ccw === undefined) ccw = area < 0;
47579
47580                 if (ccw === area < 0) {
47581                     if (polygon) polygons.push(polygon);
47582                     polygon = [rings[i]];
47583
47584                 } else {
47585                     polygon.push(rings[i]);
47586                 }
47587             }
47588             if (polygon) polygons.push(polygon);
47589
47590             return polygons;
47591         }
47592
47593         function signedArea$1(ring) {
47594             var sum = 0;
47595             for (var i = 0, len = ring.length, j = len - 1, p1, p2; i < len; j = i++) {
47596                 p1 = ring[i];
47597                 p2 = ring[j];
47598                 sum += (p2.x - p1.x) * (p1.y + p2.y);
47599             }
47600             return sum;
47601         }
47602
47603         var vectortilelayer = VectorTileLayer;
47604
47605         function VectorTileLayer(pbf, end) {
47606             // Public
47607             this.version = 1;
47608             this.name = null;
47609             this.extent = 4096;
47610             this.length = 0;
47611
47612             // Private
47613             this._pbf = pbf;
47614             this._keys = [];
47615             this._values = [];
47616             this._features = [];
47617
47618             pbf.readFields(readLayer, this, end);
47619
47620             this.length = this._features.length;
47621         }
47622
47623         function readLayer(tag, layer, pbf) {
47624             if (tag === 15) layer.version = pbf.readVarint();
47625             else if (tag === 1) layer.name = pbf.readString();
47626             else if (tag === 5) layer.extent = pbf.readVarint();
47627             else if (tag === 2) layer._features.push(pbf.pos);
47628             else if (tag === 3) layer._keys.push(pbf.readString());
47629             else if (tag === 4) layer._values.push(readValueMessage(pbf));
47630         }
47631
47632         function readValueMessage(pbf) {
47633             var value = null,
47634                 end = pbf.readVarint() + pbf.pos;
47635
47636             while (pbf.pos < end) {
47637                 var tag = pbf.readVarint() >> 3;
47638
47639                 value = tag === 1 ? pbf.readString() :
47640                     tag === 2 ? pbf.readFloat() :
47641                     tag === 3 ? pbf.readDouble() :
47642                     tag === 4 ? pbf.readVarint64() :
47643                     tag === 5 ? pbf.readVarint() :
47644                     tag === 6 ? pbf.readSVarint() :
47645                     tag === 7 ? pbf.readBoolean() : null;
47646             }
47647
47648             return value;
47649         }
47650
47651         // return feature `i` from this layer as a `VectorTileFeature`
47652         VectorTileLayer.prototype.feature = function(i) {
47653             if (i < 0 || i >= this._features.length) throw new Error('feature index out of bounds');
47654
47655             this._pbf.pos = this._features[i];
47656
47657             var end = this._pbf.readVarint() + this._pbf.pos;
47658             return new vectortilefeature(this._pbf, end, this.extent, this._keys, this._values);
47659         };
47660
47661         var vectortile = VectorTile;
47662
47663         function VectorTile(pbf, end) {
47664             this.layers = pbf.readFields(readTile, {}, end);
47665         }
47666
47667         function readTile(tag, layers, pbf) {
47668             if (tag === 3) {
47669                 var layer = new vectortilelayer(pbf, pbf.readVarint() + pbf.pos);
47670                 if (layer.length) layers[layer.name] = layer;
47671             }
47672         }
47673
47674         var VectorTile$1 = vectortile;
47675         var VectorTileFeature$1 = vectortilefeature;
47676         var VectorTileLayer$1 = vectortilelayer;
47677
47678         var vectorTile = {
47679                 VectorTile: VectorTile$1,
47680                 VectorTileFeature: VectorTileFeature$1,
47681                 VectorTileLayer: VectorTileLayer$1
47682         };
47683
47684         var tiler$7 = utilTiler().tileSize(512).margin(1);
47685         var dispatch$8 = dispatch('loadedData');
47686         var _vtCache;
47687
47688
47689         function abortRequest$7(controller) {
47690             controller.abort();
47691         }
47692
47693
47694         function vtToGeoJSON(data, tile, mergeCache) {
47695             var vectorTile$1 = new vectorTile.VectorTile(new pbf(data));
47696             var layers = Object.keys(vectorTile$1.layers);
47697             if (!Array.isArray(layers)) { layers = [layers]; }
47698
47699             var features = [];
47700             layers.forEach(function(layerID) {
47701                 var layer = vectorTile$1.layers[layerID];
47702                 if (layer) {
47703                     for (var i = 0; i < layer.length; i++) {
47704                         var feature = layer.feature(i).toGeoJSON(tile.xyz[0], tile.xyz[1], tile.xyz[2]);
47705                         var geometry = feature.geometry;
47706
47707                         // Treat all Polygons as MultiPolygons
47708                         if (geometry.type === 'Polygon') {
47709                             geometry.type = 'MultiPolygon';
47710                             geometry.coordinates = [geometry.coordinates];
47711                         }
47712
47713                         // Clip to tile bounds
47714                         if (geometry.type === 'MultiPolygon') {
47715                             var isClipped = false;
47716                             var featureClip = bboxClip_1(feature, tile.extent.rectangle());
47717                             if (!fastDeepEqual(feature.geometry, featureClip.geometry)) {
47718                                 // feature = featureClip;
47719                                 isClipped = true;
47720                             }
47721                             if (!feature.geometry.coordinates.length) continue;   // not actually on this tile
47722                             if (!feature.geometry.coordinates[0].length) continue;   // not actually on this tile
47723                         }
47724
47725                         // Generate some unique IDs and add some metadata
47726                         var featurehash = utilHashcode(fastJsonStableStringify(feature));
47727                         var propertyhash = utilHashcode(fastJsonStableStringify(feature.properties || {}));
47728                         feature.__layerID__ = layerID.replace(/[^_a-zA-Z0-9\-]/g, '_');
47729                         feature.__featurehash__ = featurehash;
47730                         feature.__propertyhash__ = propertyhash;
47731                         features.push(feature);
47732
47733                         // Clipped Polygons at same zoom with identical properties can get merged
47734                         if (isClipped && geometry.type === 'MultiPolygon') {
47735                             var merged = mergeCache[propertyhash];
47736                             if (merged && merged.length) {
47737                                 var other = merged[0];
47738                                 var coords = union(
47739                                     feature.geometry.coordinates,
47740                                     other.geometry.coordinates
47741                                 );
47742
47743                                 if (!coords || !coords.length) {
47744                                     continue;  // something failed in martinez union
47745                                 }
47746
47747                                 merged.push(feature);
47748                                 for (var j = 0; j < merged.length; j++) {      // all these features get...
47749                                     merged[j].geometry.coordinates = coords;   // same coords
47750                                     merged[j].__featurehash__ = featurehash;   // same hash, so deduplication works
47751                                 }
47752                             } else {
47753                                 mergeCache[propertyhash] = [feature];
47754                             }
47755                         }
47756                     }
47757                 }
47758             });
47759
47760             return features;
47761         }
47762
47763
47764         function loadTile(source, tile) {
47765             if (source.loaded[tile.id] || source.inflight[tile.id]) return;
47766
47767             var url = source.template
47768                 .replace('{x}', tile.xyz[0])
47769                 .replace('{y}', tile.xyz[1])
47770                 // TMS-flipped y coordinate
47771                 .replace(/\{[t-]y\}/, Math.pow(2, tile.xyz[2]) - tile.xyz[1] - 1)
47772                 .replace(/\{z(oom)?\}/, tile.xyz[2])
47773                 .replace(/\{switch:([^}]+)\}/, function(s, r) {
47774                     var subdomains = r.split(',');
47775                     return subdomains[(tile.xyz[0] + tile.xyz[1]) % subdomains.length];
47776                 });
47777
47778
47779             var controller = new AbortController();
47780             source.inflight[tile.id] = controller;
47781
47782             fetch(url, { signal: controller.signal })
47783                 .then(function(response) {
47784                     if (!response.ok) {
47785                         throw new Error(response.status + ' ' + response.statusText);
47786                     }
47787                     source.loaded[tile.id] = [];
47788                     delete source.inflight[tile.id];
47789                     return response.arrayBuffer();
47790                 })
47791                 .then(function(data) {
47792                     if (!data) {
47793                         throw new Error('No Data');
47794                     }
47795
47796                     var z = tile.xyz[2];
47797                     if (!source.canMerge[z]) {
47798                         source.canMerge[z] = {};  // initialize mergeCache
47799                     }
47800
47801                     source.loaded[tile.id] = vtToGeoJSON(data, tile, source.canMerge[z]);
47802                     dispatch$8.call('loadedData');
47803                 })
47804                 .catch(function() {
47805                     source.loaded[tile.id] = [];
47806                     delete source.inflight[tile.id];
47807                 });
47808         }
47809
47810
47811         var serviceVectorTile = {
47812
47813             init: function() {
47814                 if (!_vtCache) {
47815                     this.reset();
47816                 }
47817
47818                 this.event = utilRebind(this, dispatch$8, 'on');
47819             },
47820
47821
47822             reset: function() {
47823                 for (var sourceID in _vtCache) {
47824                     var source = _vtCache[sourceID];
47825                     if (source && source.inflight) {
47826                         Object.values(source.inflight).forEach(abortRequest$7);
47827                     }
47828                 }
47829
47830                 _vtCache = {};
47831             },
47832
47833
47834             addSource: function(sourceID, template) {
47835                 _vtCache[sourceID] = { template: template, inflight: {}, loaded: {}, canMerge: {} };
47836                 return _vtCache[sourceID];
47837             },
47838
47839
47840             data: function(sourceID, projection) {
47841                 var source = _vtCache[sourceID];
47842                 if (!source) return [];
47843
47844                 var tiles = tiler$7.getTiles(projection);
47845                 var seen = {};
47846                 var results = [];
47847
47848                 for (var i = 0; i < tiles.length; i++) {
47849                     var features = source.loaded[tiles[i].id];
47850                     if (!features || !features.length) continue;
47851
47852                     for (var j = 0; j < features.length; j++) {
47853                         var feature = features[j];
47854                         var hash = feature.__featurehash__;
47855                         if (seen[hash]) continue;
47856                         seen[hash] = true;
47857
47858                         // return a shallow copy, because the hash may change
47859                         // later if this feature gets merged with another
47860                         results.push(Object.assign({}, feature));  // shallow copy
47861                     }
47862                 }
47863
47864                 return results;
47865             },
47866
47867
47868             loadTiles: function(sourceID, template, projection) {
47869                 var source = _vtCache[sourceID];
47870                 if (!source) {
47871                     source = this.addSource(sourceID, template);
47872                 }
47873
47874                 var tiles = tiler$7.getTiles(projection);
47875
47876                 // abort inflight requests that are no longer needed
47877                 Object.keys(source.inflight).forEach(function(k) {
47878                     var wanted = tiles.find(function(tile) { return k === tile.id; });
47879                     if (!wanted) {
47880                         abortRequest$7(source.inflight[k]);
47881                         delete source.inflight[k];
47882                     }
47883                 });
47884
47885                 tiles.forEach(function(tile) {
47886                     loadTile(source, tile);
47887                 });
47888             },
47889
47890
47891             cache: function() {
47892                 return _vtCache;
47893             }
47894
47895         };
47896
47897         var apibase$5 = 'https://www.wikidata.org/w/api.php?';
47898         var _wikidataCache = {};
47899
47900
47901         var serviceWikidata = {
47902
47903             init: function() {},
47904
47905             reset: function() {
47906                 _wikidataCache = {};
47907             },
47908
47909
47910             // Search for Wikidata items matching the query
47911             itemsForSearchQuery: function(query, callback) {
47912                 if (!query) {
47913                     if (callback) callback('No query', {});
47914                     return;
47915                 }
47916
47917                 var lang = this.languagesToQuery()[0];
47918
47919                 var url = apibase$5 + utilQsString({
47920                     action: 'wbsearchentities',
47921                     format: 'json',
47922                     formatversion: 2,
47923                     search: query,
47924                     type: 'item',
47925                     // the language to search
47926                     language: lang,
47927                     // the langauge for the label and description in the result
47928                     uselang: lang,
47929                     limit: 10,
47930                     origin: '*'
47931                 });
47932
47933                 d3_json(url)
47934                     .then(function(result) {
47935                         if (result && result.error) {
47936                             throw new Error(result.error);
47937                         }
47938                         if (callback) callback(null, result.search || {});
47939                     })
47940                     .catch(function(err) {
47941                         if (callback) callback(err.message, {});
47942                     });
47943             },
47944
47945
47946             // Given a Wikipedia language and article title,
47947             // return an array of corresponding Wikidata entities.
47948             itemsByTitle: function(lang, title, callback) {
47949                 if (!title) {
47950                     if (callback) callback('No title', {});
47951                     return;
47952                 }
47953
47954                 lang = lang || 'en';
47955                 var url = apibase$5 + utilQsString({
47956                     action: 'wbgetentities',
47957                     format: 'json',
47958                     formatversion: 2,
47959                     sites: lang.replace(/-/g, '_') + 'wiki',
47960                     titles: title,
47961                     languages: 'en', // shrink response by filtering to one language
47962                     origin: '*'
47963                 });
47964
47965                 d3_json(url)
47966                     .then(function(result) {
47967                         if (result && result.error) {
47968                             throw new Error(result.error);
47969                         }
47970                         if (callback) callback(null, result.entities || {});
47971                     })
47972                     .catch(function(err) {
47973                         if (callback) callback(err.message, {});
47974                     });
47975             },
47976
47977
47978             languagesToQuery: function() {
47979                 var localeCode = _mainLocalizer.localeCode().toLowerCase();
47980                 // HACK: en-us isn't a wikidata language. We should really be filtering by
47981                 // the languages known to be supported by wikidata.
47982                 if (localeCode === 'en-us') localeCode = 'en';
47983                 return utilArrayUniq([
47984                     localeCode,
47985                     _mainLocalizer.languageCode().toLowerCase(),
47986                     'en'
47987                 ]);
47988             },
47989
47990
47991             entityByQID: function(qid, callback) {
47992                 if (!qid) {
47993                     callback('No qid', {});
47994                     return;
47995                 }
47996                 if (_wikidataCache[qid]) {
47997                     if (callback) callback(null, _wikidataCache[qid]);
47998                     return;
47999                 }
48000
48001                 var langs = this.languagesToQuery();
48002                 var url = apibase$5 + utilQsString({
48003                     action: 'wbgetentities',
48004                     format: 'json',
48005                     formatversion: 2,
48006                     ids: qid,
48007                     props: 'labels|descriptions|claims|sitelinks',
48008                     sitefilter: langs.map(function(d) { return d + 'wiki'; }).join('|'),
48009                     languages: langs.join('|'),
48010                     languagefallback: 1,
48011                     origin: '*'
48012                 });
48013
48014                 d3_json(url)
48015                     .then(function(result) {
48016                         if (result && result.error) {
48017                             throw new Error(result.error);
48018                         }
48019                         if (callback) callback(null, result.entities[qid] || {});
48020                     })
48021                     .catch(function(err) {
48022                         if (callback) callback(err.message, {});
48023                     });
48024             },
48025
48026
48027             // Pass `params` object of the form:
48028             // {
48029             //   qid: 'string'      // brand wikidata  (e.g. 'Q37158')
48030             // }
48031             //
48032             // Get an result object used to display tag documentation
48033             // {
48034             //   title:        'string',
48035             //   description:  'string',
48036             //   editURL:      'string',
48037             //   imageURL:     'string',
48038             //   wiki:         { title: 'string', text: 'string', url: 'string' }
48039             // }
48040             //
48041             getDocs: function(params, callback) {
48042                 var langs = this.languagesToQuery();
48043                 this.entityByQID(params.qid, function(err, entity) {
48044                     if (err || !entity) {
48045                         callback(err || 'No entity');
48046                         return;
48047                     }
48048
48049                     var i;
48050                     var description;
48051                     if (entity.descriptions && Object.keys(entity.descriptions).length > 0) {
48052                         description = entity.descriptions[Object.keys(entity.descriptions)[0]].value;
48053                     }
48054
48055                     // prepare result
48056                     var result = {
48057                         title: entity.id,
48058                         description: description,
48059                         editURL: 'https://www.wikidata.org/wiki/' + entity.id
48060                     };
48061
48062                     // add image
48063                     if (entity.claims) {
48064                         var imageroot = 'https://commons.wikimedia.org/w/index.php';
48065                         var props = ['P154','P18'];  // logo image, image
48066                         var prop, image;
48067                         for (i = 0; i < props.length; i++) {
48068                             prop = entity.claims[props[i]];
48069                             if (prop && Object.keys(prop).length > 0) {
48070                                 image = prop[Object.keys(prop)[0]].mainsnak.datavalue.value;
48071                                 if (image) {
48072                                     result.imageURL = imageroot + '?' + utilQsString({
48073                                         title: 'Special:Redirect/file/' + image,
48074                                         width: 400
48075                                     });
48076                                     break;
48077                                 }
48078                             }
48079                         }
48080                     }
48081
48082                     if (entity.sitelinks) {
48083                         var englishLocale = _mainLocalizer.languageCode().toLowerCase() === 'en';
48084
48085                         // must be one of these that we requested..
48086                         for (i = 0; i < langs.length; i++) {   // check each, in order of preference
48087                             var w = langs[i] + 'wiki';
48088                             if (entity.sitelinks[w]) {
48089                                 var title = entity.sitelinks[w].title;
48090                                 var tKey = 'inspector.wiki_reference';
48091                                 if (!englishLocale && langs[i] === 'en') {   // user's locale isn't English but
48092                                     tKey = 'inspector.wiki_en_reference';    // we are sending them to enwiki anyway..
48093                                 }
48094
48095                                 result.wiki = {
48096                                     title: title,
48097                                     text: tKey,
48098                                     url: 'https://' + langs[i] + '.wikipedia.org/wiki/' + title.replace(/ /g, '_')
48099                                 };
48100                                 break;
48101                             }
48102                         }
48103                     }
48104
48105                     callback(null, result);
48106                 });
48107             }
48108
48109         };
48110
48111         var endpoint = 'https://en.wikipedia.org/w/api.php?';
48112
48113         var serviceWikipedia = {
48114
48115             init: function() {},
48116             reset: function() {},
48117
48118
48119             search: function(lang, query, callback) {
48120                 if (!query) {
48121                     if (callback) callback('No Query', []);
48122                     return;
48123                 }
48124
48125                 lang = lang || 'en';
48126                 var url = endpoint.replace('en', lang) +
48127                     utilQsString({
48128                         action: 'query',
48129                         list: 'search',
48130                         srlimit: '10',
48131                         srinfo: 'suggestion',
48132                         format: 'json',
48133                         origin: '*',
48134                         srsearch: query
48135                     });
48136
48137                 d3_json(url)
48138                     .then(function(result) {
48139                         if (result && result.error) {
48140                             throw new Error(result.error);
48141                         } else if (!result || !result.query || !result.query.search) {
48142                             throw new Error('No Results');
48143                         }
48144                         if (callback) {
48145                             var titles = result.query.search.map(function(d) { return d.title; });
48146                             callback(null, titles);
48147                         }
48148                     })
48149                     .catch(function(err) {
48150                         if (callback) callback(err, []);
48151                     });
48152             },
48153
48154
48155             suggestions: function(lang, query, callback) {
48156                 if (!query) {
48157                     if (callback) callback('', []);
48158                     return;
48159                 }
48160
48161                 lang = lang || 'en';
48162                 var url = endpoint.replace('en', lang) +
48163                     utilQsString({
48164                         action: 'opensearch',
48165                         namespace: 0,
48166                         suggest: '',
48167                         format: 'json',
48168                         origin: '*',
48169                         search: query
48170                     });
48171
48172                 d3_json(url)
48173                     .then(function(result) {
48174                         if (result && result.error) {
48175                             throw new Error(result.error);
48176                         } else if (!result || result.length < 2) {
48177                             throw new Error('No Results');
48178                         }
48179                         if (callback) callback(null, result[1] || []);
48180                     })
48181                     .catch(function(err) {
48182                         if (callback) callback(err.message, []);
48183                     });
48184             },
48185
48186
48187             translations: function(lang, title, callback) {
48188                 if (!title) {
48189                     if (callback) callback('No Title');
48190                     return;
48191                 }
48192
48193                 var url = endpoint.replace('en', lang) +
48194                     utilQsString({
48195                         action: 'query',
48196                         prop: 'langlinks',
48197                         format: 'json',
48198                         origin: '*',
48199                         lllimit: 500,
48200                         titles: title
48201                     });
48202
48203                 d3_json(url)
48204                     .then(function(result) {
48205                         if (result && result.error) {
48206                             throw new Error(result.error);
48207                         } else if (!result || !result.query || !result.query.pages) {
48208                             throw new Error('No Results');
48209                         }
48210                         if (callback) {
48211                             var list = result.query.pages[Object.keys(result.query.pages)[0]];
48212                             var translations = {};
48213                             if (list && list.langlinks) {
48214                                 list.langlinks.forEach(function(d) { translations[d.lang] = d['*']; });
48215                             }
48216                             callback(null, translations);
48217                         }
48218                     })
48219                     .catch(function(err) {
48220                         if (callback) callback(err.message);
48221                     });
48222             }
48223
48224         };
48225
48226         var services = {
48227             geocoder: serviceNominatim,
48228             keepRight: serviceKeepRight,
48229             improveOSM: serviceImproveOSM,
48230             osmose: serviceOsmose,
48231             mapillary: serviceMapillary,
48232             openstreetcam: serviceOpenstreetcam,
48233             osm: serviceOsm,
48234             osmWikibase: serviceOsmWikibase,
48235             maprules: serviceMapRules,
48236             streetside: serviceStreetside,
48237             taginfo: serviceTaginfo,
48238             vectorTile: serviceVectorTile,
48239             wikidata: serviceWikidata,
48240             wikipedia: serviceWikipedia
48241         };
48242
48243         function svgIcon(name, svgklass, useklass) {
48244             return function drawIcon(selection) {
48245                 selection.selectAll('svg.icon' + (svgklass ? '.' + svgklass.split(' ')[0] : ''))
48246                     .data([0])
48247                     .enter()
48248                     .append('svg')
48249                     .attr('class', 'icon ' + (svgklass || ''))
48250                     .append('use')
48251                     .attr('xlink:href', name)
48252                     .attr('class', useklass);
48253             };
48254         }
48255
48256         function uiNoteComments() {
48257             var _note;
48258
48259
48260             function noteComments(selection) {
48261                 if (_note.isNew()) return; // don't draw .comments-container
48262
48263                 var comments = selection.selectAll('.comments-container')
48264                     .data([0]);
48265
48266                 comments = comments.enter()
48267                     .append('div')
48268                     .attr('class', 'comments-container')
48269                     .merge(comments);
48270
48271                 var commentEnter = comments.selectAll('.comment')
48272                     .data(_note.comments)
48273                     .enter()
48274                     .append('div')
48275                     .attr('class', 'comment');
48276
48277                 commentEnter
48278                     .append('div')
48279                     .attr('class', function(d) { return 'comment-avatar user-' + d.uid; })
48280                     .call(svgIcon('#iD-icon-avatar', 'comment-avatar-icon'));
48281
48282                 var mainEnter = commentEnter
48283                     .append('div')
48284                     .attr('class', 'comment-main');
48285
48286                 var metadataEnter = mainEnter
48287                     .append('div')
48288                     .attr('class', 'comment-metadata');
48289
48290                 metadataEnter
48291                     .append('div')
48292                     .attr('class', 'comment-author')
48293                     .each(function(d) {
48294                         var selection = select(this);
48295                         var osm = services.osm;
48296                         if (osm && d.user) {
48297                             selection = selection
48298                                 .append('a')
48299                                 .attr('class', 'comment-author-link')
48300                                 .attr('href', osm.userURL(d.user))
48301                                 .attr('tabindex', -1)
48302                                 .attr('target', '_blank');
48303                         }
48304                         selection
48305                             .text(function(d) { return d.user || _t('note.anonymous'); });
48306                     });
48307
48308                 metadataEnter
48309                     .append('div')
48310                     .attr('class', 'comment-date')
48311                     .text(function(d) {
48312                         return _t('note.status.' + d.action, { when: localeDateString(d.date) });
48313                     });
48314
48315                 mainEnter
48316                     .append('div')
48317                     .attr('class', 'comment-text')
48318                     .html(function(d) { return d.html; });
48319
48320                 comments
48321                     .call(replaceAvatars);
48322             }
48323
48324
48325             function replaceAvatars(selection) {
48326                 var showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true';
48327                 var osm = services.osm;
48328                 if (showThirdPartyIcons !== 'true' || !osm) return;
48329
48330                 var uids = {};  // gather uids in the comment thread
48331                 _note.comments.forEach(function(d) {
48332                     if (d.uid) uids[d.uid] = true;
48333                 });
48334
48335                 Object.keys(uids).forEach(function(uid) {
48336                     osm.loadUser(uid, function(err, user) {
48337                         if (!user || !user.image_url) return;
48338
48339                         selection.selectAll('.comment-avatar.user-' + uid)
48340                             .html('')
48341                             .append('img')
48342                             .attr('class', 'icon comment-avatar-icon')
48343                             .attr('src', user.image_url)
48344                             .attr('alt', user.display_name);
48345                     });
48346                 });
48347             }
48348
48349
48350             function localeDateString(s) {
48351                 if (!s) return null;
48352                 var options = { day: 'numeric', month: 'short', year: 'numeric' };
48353                 s = s.replace(/-/g, '/'); // fix browser-specific Date() issues
48354                 var d = new Date(s);
48355                 if (isNaN(d.getTime())) return null;
48356                 return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
48357             }
48358
48359
48360             noteComments.note = function(val) {
48361                 if (!arguments.length) return _note;
48362                 _note = val;
48363                 return noteComments;
48364             };
48365
48366
48367             return noteComments;
48368         }
48369
48370         function uiNoteHeader() {
48371             var _note;
48372
48373
48374             function noteHeader(selection) {
48375                 var header = selection.selectAll('.note-header')
48376                     .data(
48377                         (_note ? [_note] : []),
48378                         function(d) { return d.status + d.id; }
48379                     );
48380
48381                 header.exit()
48382                     .remove();
48383
48384                 var headerEnter = header.enter()
48385                     .append('div')
48386                     .attr('class', 'note-header');
48387
48388                 var iconEnter = headerEnter
48389                     .append('div')
48390                     .attr('class', function(d) { return 'note-header-icon ' + d.status; })
48391                     .classed('new', function(d) { return d.id < 0; });
48392
48393                 iconEnter
48394                     .append('div')
48395                     .attr('class', 'preset-icon-28')
48396                     .call(svgIcon('#iD-icon-note', 'note-fill'));
48397
48398                 iconEnter.each(function(d) {
48399                     var statusIcon = '#iD-icon-' + (d.id < 0 ? 'plus' : (d.status === 'open' ? 'close' : 'apply'));
48400                     iconEnter
48401                         .append('div')
48402                         .attr('class', 'note-icon-annotation')
48403                         .call(svgIcon(statusIcon, 'icon-annotation'));
48404                 });
48405
48406                 headerEnter
48407                     .append('div')
48408                     .attr('class', 'note-header-label')
48409                     .text(function(d) {
48410                         if (_note.isNew()) { return _t('note.new'); }
48411                         return _t('note.note') + ' ' + d.id + ' ' +
48412                             (d.status === 'closed' ? _t('note.closed') : '');
48413                     });
48414             }
48415
48416
48417             noteHeader.note = function(val) {
48418                 if (!arguments.length) return _note;
48419                 _note = val;
48420                 return noteHeader;
48421             };
48422
48423
48424             return noteHeader;
48425         }
48426
48427         function uiNoteReport() {
48428             var _note;
48429
48430             function noteReport(selection) {
48431                 var url;
48432                 if (services.osm && (_note instanceof osmNote) && (!_note.isNew())) {
48433                     url = services.osm.noteReportURL(_note);
48434                 }
48435
48436                 var link = selection.selectAll('.note-report')
48437                     .data(url ? [url] : []);
48438
48439                 // exit
48440                 link.exit()
48441                     .remove();
48442
48443                 // enter
48444                 var linkEnter = link.enter()
48445                     .append('a')
48446                     .attr('class', 'note-report')
48447                     .attr('target', '_blank')
48448                     .attr('href', function(d) { return d; })
48449                     .call(svgIcon('#iD-icon-out-link', 'inline'));
48450
48451                 linkEnter
48452                     .append('span')
48453                     .text(_t('note.report'));
48454             }
48455
48456
48457             noteReport.note = function(val) {
48458                 if (!arguments.length) return _note;
48459                 _note = val;
48460                 return noteReport;
48461             };
48462
48463             return noteReport;
48464         }
48465
48466         function uiViewOnOSM(context) {
48467             var _what;   // an osmEntity or osmNote
48468
48469
48470             function viewOnOSM(selection) {
48471                 var url;
48472                 if (_what instanceof osmEntity) {
48473                     url = context.connection().entityURL(_what);
48474                 } else if (_what instanceof osmNote) {
48475                     url = context.connection().noteURL(_what);
48476                 }
48477
48478                 var data = ((!_what || _what.isNew()) ? [] : [_what]);
48479                 var link = selection.selectAll('.view-on-osm')
48480                     .data(data, function(d) { return d.id; });
48481
48482                 // exit
48483                 link.exit()
48484                     .remove();
48485
48486                 // enter
48487                 var linkEnter = link.enter()
48488                     .append('a')
48489                     .attr('class', 'view-on-osm')
48490                     .attr('target', '_blank')
48491                     .attr('href', url)
48492                     .call(svgIcon('#iD-icon-out-link', 'inline'));
48493
48494                 linkEnter
48495                     .append('span')
48496                     .text(_t('inspector.view_on_osm'));
48497             }
48498
48499
48500             viewOnOSM.what = function(_) {
48501                 if (!arguments.length) return _what;
48502                 _what = _;
48503                 return viewOnOSM;
48504             };
48505
48506             return viewOnOSM;
48507         }
48508
48509         function uiNoteEditor(context) {
48510             var dispatch$1 = dispatch('change');
48511             var noteComments = uiNoteComments();
48512             var noteHeader = uiNoteHeader();
48513
48514             // var formFields = uiFormFields(context);
48515
48516             var _note;
48517             var _newNote;
48518             // var _fieldsArr;
48519
48520
48521             function noteEditor(selection) {
48522
48523                 var header = selection.selectAll('.header')
48524                     .data([0]);
48525
48526                 var headerEnter = header.enter()
48527                     .append('div')
48528                     .attr('class', 'header fillL');
48529
48530                 headerEnter
48531                     .append('button')
48532                     .attr('class', 'close')
48533                     .on('click', function() {
48534                         context.enter(modeBrowse(context));
48535                     })
48536                     .call(svgIcon('#iD-icon-close'));
48537
48538                 headerEnter
48539                     .append('h3')
48540                     .text(_t('note.title'));
48541
48542
48543                 var body = selection.selectAll('.body')
48544                     .data([0]);
48545
48546                 body = body.enter()
48547                     .append('div')
48548                     .attr('class', 'body')
48549                     .merge(body);
48550
48551                 var editor = body.selectAll('.note-editor')
48552                     .data([0]);
48553
48554                 editor.enter()
48555                     .append('div')
48556                     .attr('class', 'modal-section note-editor')
48557                     .merge(editor)
48558                     .call(noteHeader.note(_note))
48559                     .call(noteComments.note(_note))
48560                     .call(noteSaveSection);
48561
48562                 var footer = selection.selectAll('.footer')
48563                     .data([0]);
48564
48565                 footer.enter()
48566                     .append('div')
48567                     .attr('class', 'footer')
48568                     .merge(footer)
48569                     .call(uiViewOnOSM(context).what(_note))
48570                     .call(uiNoteReport().note(_note));
48571
48572
48573                 // rerender the note editor on any auth change
48574                 var osm = services.osm;
48575                 if (osm) {
48576                     osm.on('change.note-save', function() {
48577                         selection.call(noteEditor);
48578                     });
48579                 }
48580             }
48581
48582
48583             function noteSaveSection(selection) {
48584                 var isSelected = (_note && _note.id === context.selectedNoteID());
48585                 var noteSave = selection.selectAll('.note-save')
48586                     .data((isSelected ? [_note] : []), function(d) { return d.status + d.id; });
48587
48588                 // exit
48589                 noteSave.exit()
48590                     .remove();
48591
48592                 // enter
48593                 var noteSaveEnter = noteSave.enter()
48594                     .append('div')
48595                     .attr('class', 'note-save save-section cf');
48596
48597                 // // if new note, show categories to pick from
48598                 // if (_note.isNew()) {
48599                 //     var presets = presetManager;
48600
48601                 //     // NOTE: this key isn't a age and therefore there is no documentation (yet)
48602                 //     _fieldsArr = [
48603                 //         uiField(context, presets.field('category'), null, { show: true, revert: false }),
48604                 //     ];
48605
48606                 //     _fieldsArr.forEach(function(field) {
48607                 //         field
48608                 //             .on('change', changeCategory);
48609                 //     });
48610
48611                 //     noteSaveEnter
48612                 //         .append('div')
48613                 //         .attr('class', 'note-category')
48614                 //         .call(formFields.fieldsArr(_fieldsArr));
48615                 // }
48616
48617                 // function changeCategory() {
48618                 //     // NOTE: perhaps there is a better way to get value
48619                 //     var val = context.container().select('input[name=\'category\']:checked').property('__data__') || undefined;
48620
48621                 //     // store the unsaved category with the note itself
48622                 //     _note = _note.update({ newCategory: val });
48623                 //     var osm = services.osm;
48624                 //     if (osm) {
48625                 //         osm.replaceNote(_note);  // update note cache
48626                 //     }
48627                 //     noteSave
48628                 //         .call(noteSaveButtons);
48629                 // }
48630
48631                 noteSaveEnter
48632                     .append('h4')
48633                     .attr('class', '.note-save-header')
48634                     .text(function() {
48635                         return _note.isNew() ? _t('note.newDescription') : _t('note.newComment');
48636                     });
48637
48638                 var commentTextarea = noteSaveEnter
48639                     .append('textarea')
48640                     .attr('class', 'new-comment-input')
48641                     .attr('placeholder', _t('note.inputPlaceholder'))
48642                     .attr('maxlength', 1000)
48643                     .property('value', function(d) { return d.newComment; })
48644                     .call(utilNoAuto)
48645                     .on('keydown.note-input', keydown)
48646                     .on('input.note-input', changeInput)
48647                     .on('blur.note-input', changeInput);
48648
48649                 if (_newNote) {
48650                     // autofocus the comment field for new notes
48651                     commentTextarea.node().focus();
48652                 }
48653
48654                 // update
48655                 noteSave = noteSaveEnter
48656                     .merge(noteSave)
48657                     .call(userDetails)
48658                     .call(noteSaveButtons);
48659
48660
48661                 // fast submit if user presses cmd+enter
48662                 function keydown() {
48663                     if (!(event.keyCode === 13 && event.metaKey)) return;
48664
48665                     var osm = services.osm;
48666                     if (!osm) return;
48667
48668                     var hasAuth = osm.authenticated();
48669                     if (!hasAuth) return;
48670
48671                     if (!_note.newComment) return;
48672
48673                     event.preventDefault();
48674
48675                     select(this)
48676                         .on('keydown.note-input', null);
48677
48678                     // focus on button and submit
48679                     window.setTimeout(function() {
48680                         if (_note.isNew()) {
48681                             noteSave.selectAll('.save-button').node().focus();
48682                             clickSave(_note);
48683                         } else  {
48684                             noteSave.selectAll('.comment-button').node().focus();
48685                             clickComment(_note);
48686                         }
48687                     }, 10);
48688                 }
48689
48690
48691                 function changeInput() {
48692                     var input = select(this);
48693                     var val = input.property('value').trim() || undefined;
48694
48695                     // store the unsaved comment with the note itself
48696                     _note = _note.update({ newComment: val });
48697
48698                     var osm = services.osm;
48699                     if (osm) {
48700                         osm.replaceNote(_note);  // update note cache
48701                     }
48702
48703                     noteSave
48704                         .call(noteSaveButtons);
48705                 }
48706             }
48707
48708
48709             function userDetails(selection) {
48710                 var detailSection = selection.selectAll('.detail-section')
48711                     .data([0]);
48712
48713                 detailSection = detailSection.enter()
48714                     .append('div')
48715                     .attr('class', 'detail-section')
48716                     .merge(detailSection);
48717
48718                 var osm = services.osm;
48719                 if (!osm) return;
48720
48721                 // Add warning if user is not logged in
48722                 var hasAuth = osm.authenticated();
48723                 var authWarning = detailSection.selectAll('.auth-warning')
48724                     .data(hasAuth ? [] : [0]);
48725
48726                 authWarning.exit()
48727                     .transition()
48728                     .duration(200)
48729                     .style('opacity', 0)
48730                     .remove();
48731
48732                 var authEnter = authWarning.enter()
48733                     .insert('div', '.tag-reference-body')
48734                     .attr('class', 'field-warning auth-warning')
48735                     .style('opacity', 0);
48736
48737                 authEnter
48738                     .call(svgIcon('#iD-icon-alert', 'inline'));
48739
48740                 authEnter
48741                     .append('span')
48742                     .text(_t('note.login'));
48743
48744                 authEnter
48745                     .append('a')
48746                     .attr('target', '_blank')
48747                     .call(svgIcon('#iD-icon-out-link', 'inline'))
48748                     .append('span')
48749                     .text(_t('login'))
48750                     .on('click.note-login', function() {
48751                         event.preventDefault();
48752                         osm.authenticate();
48753                     });
48754
48755                 authEnter
48756                     .transition()
48757                     .duration(200)
48758                     .style('opacity', 1);
48759
48760
48761                 var prose = detailSection.selectAll('.note-save-prose')
48762                     .data(hasAuth ? [0] : []);
48763
48764                 prose.exit()
48765                     .remove();
48766
48767                 prose = prose.enter()
48768                     .append('p')
48769                     .attr('class', 'note-save-prose')
48770                     .text(_t('note.upload_explanation'))
48771                     .merge(prose);
48772
48773                 osm.userDetails(function(err, user) {
48774                     if (err) return;
48775
48776                     var userLink = select(document.createElement('div'));
48777
48778                     if (user.image_url) {
48779                         userLink
48780                             .append('img')
48781                             .attr('src', user.image_url)
48782                             .attr('class', 'icon pre-text user-icon');
48783                     }
48784
48785                     userLink
48786                         .append('a')
48787                         .attr('class', 'user-info')
48788                         .text(user.display_name)
48789                         .attr('href', osm.userURL(user.display_name))
48790                         .attr('tabindex', -1)
48791                         .attr('target', '_blank');
48792
48793                     prose
48794                         .html(_t('note.upload_explanation_with_user', { user: userLink.html() }));
48795                 });
48796             }
48797
48798
48799             function noteSaveButtons(selection) {
48800                 var osm = services.osm;
48801                 var hasAuth = osm && osm.authenticated();
48802
48803                 var isSelected = (_note && _note.id === context.selectedNoteID());
48804                 var buttonSection = selection.selectAll('.buttons')
48805                     .data((isSelected ? [_note] : []), function(d) { return d.status + d.id; });
48806
48807                 // exit
48808                 buttonSection.exit()
48809                     .remove();
48810
48811                 // enter
48812                 var buttonEnter = buttonSection.enter()
48813                     .append('div')
48814                     .attr('class', 'buttons');
48815
48816                 if (_note.isNew()) {
48817                     buttonEnter
48818                         .append('button')
48819                         .attr('class', 'button cancel-button secondary-action')
48820                         .text(_t('confirm.cancel'));
48821
48822                     buttonEnter
48823                         .append('button')
48824                         .attr('class', 'button save-button action')
48825                         .text(_t('note.save'));
48826
48827                 } else {
48828                     buttonEnter
48829                         .append('button')
48830                         .attr('class', 'button status-button action');
48831
48832                     buttonEnter
48833                         .append('button')
48834                         .attr('class', 'button comment-button action')
48835                         .text(_t('note.comment'));
48836                 }
48837
48838
48839                 // update
48840                 buttonSection = buttonSection
48841                     .merge(buttonEnter);
48842
48843                 buttonSection.select('.cancel-button')   // select and propagate data
48844                     .on('click.cancel', clickCancel);
48845
48846                 buttonSection.select('.save-button')     // select and propagate data
48847                     .attr('disabled', isSaveDisabled)
48848                     .on('click.save', clickSave);
48849
48850                 buttonSection.select('.status-button')   // select and propagate data
48851                     .attr('disabled', (hasAuth ? null : true))
48852                     .text(function(d) {
48853                         var action = (d.status === 'open' ? 'close' : 'open');
48854                         var andComment = (d.newComment ? '_comment' : '');
48855                         return _t('note.' + action + andComment);
48856                     })
48857                     .on('click.status', clickStatus);
48858
48859                 buttonSection.select('.comment-button')   // select and propagate data
48860                     .attr('disabled', isSaveDisabled)
48861                     .on('click.comment', clickComment);
48862
48863
48864                 function isSaveDisabled(d) {
48865                     return (hasAuth && d.status === 'open' && d.newComment) ? null : true;
48866                 }
48867             }
48868
48869
48870
48871             function clickCancel(d) {
48872                 this.blur();    // avoid keeping focus on the button - #4641
48873                 var osm = services.osm;
48874                 if (osm) {
48875                     osm.removeNote(d);
48876                 }
48877                 context.enter(modeBrowse(context));
48878                 dispatch$1.call('change');
48879             }
48880
48881
48882             function clickSave(d) {
48883                 this.blur();    // avoid keeping focus on the button - #4641
48884                 var osm = services.osm;
48885                 if (osm) {
48886                     osm.postNoteCreate(d, function(err, note) {
48887                         dispatch$1.call('change', note);
48888                     });
48889                 }
48890             }
48891
48892
48893             function clickStatus(d) {
48894                 this.blur();    // avoid keeping focus on the button - #4641
48895                 var osm = services.osm;
48896                 if (osm) {
48897                     var setStatus = (d.status === 'open' ? 'closed' : 'open');
48898                     osm.postNoteUpdate(d, setStatus, function(err, note) {
48899                         dispatch$1.call('change', note);
48900                     });
48901                 }
48902             }
48903
48904             function clickComment(d) {
48905                 this.blur();    // avoid keeping focus on the button - #4641
48906                 var osm = services.osm;
48907                 if (osm) {
48908                     osm.postNoteUpdate(d, d.status, function(err, note) {
48909                         dispatch$1.call('change', note);
48910                     });
48911                 }
48912             }
48913
48914
48915             noteEditor.note = function(val) {
48916                 if (!arguments.length) return _note;
48917                 _note = val;
48918                 return noteEditor;
48919             };
48920
48921             noteEditor.newNote = function(val) {
48922                 if (!arguments.length) return _newNote;
48923                 _newNote = val;
48924                 return noteEditor;
48925             };
48926
48927
48928             return utilRebind(noteEditor, dispatch$1, 'on');
48929         }
48930
48931         function modeSelectNote(context, selectedNoteID) {
48932             var mode = {
48933                 id: 'select-note',
48934                 button: 'browse'
48935             };
48936
48937             var _keybinding = utilKeybinding('select-note');
48938             var _noteEditor = uiNoteEditor(context)
48939                 .on('change', function() {
48940                     context.map().pan([0,0]);  // trigger a redraw
48941                     var note = checkSelectedID();
48942                     if (!note) return;
48943                     context.ui().sidebar
48944                         .show(_noteEditor.note(note));
48945                 });
48946
48947             var _behaviors = [
48948                 behaviorBreathe(),
48949                 behaviorHover(context),
48950                 behaviorSelect(context),
48951                 behaviorLasso(context),
48952                 modeDragNode(context).behavior,
48953                 modeDragNote(context).behavior
48954             ];
48955
48956             var _newFeature = false;
48957
48958
48959             function checkSelectedID() {
48960                 if (!services.osm) return;
48961                 var note = services.osm.getNote(selectedNoteID);
48962                 if (!note) {
48963                     context.enter(modeBrowse(context));
48964                 }
48965                 return note;
48966             }
48967
48968
48969             // class the note as selected, or return to browse mode if the note is gone
48970             function selectNote(drawn) {
48971                 if (!checkSelectedID()) return;
48972
48973                 var selection = context.surface().selectAll('.layer-notes .note-' + selectedNoteID);
48974
48975                 if (selection.empty()) {
48976                     // Return to browse mode if selected DOM elements have
48977                     // disappeared because the user moved them out of view..
48978                     var source = event && event.type === 'zoom' && event.sourceEvent;
48979                     if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) {
48980                         context.enter(modeBrowse(context));
48981                     }
48982
48983                 } else {
48984                     selection
48985                         .classed('selected', true);
48986
48987                     context.selectedNoteID(selectedNoteID);
48988                 }
48989             }
48990
48991
48992             function esc() {
48993                 if (context.container().select('.combobox').size()) return;
48994                 context.enter(modeBrowse(context));
48995             }
48996
48997
48998             mode.zoomToSelected = function() {
48999                 if (!services.osm) return;
49000                 var note = services.osm.getNote(selectedNoteID);
49001                 if (note) {
49002                     context.map().centerZoomEase(note.loc, 20);
49003                 }
49004             };
49005
49006
49007             mode.newFeature = function(val) {
49008                 if (!arguments.length) return _newFeature;
49009                 _newFeature = val;
49010                 return mode;
49011             };
49012
49013
49014             mode.enter = function() {
49015                 var note = checkSelectedID();
49016                 if (!note) return;
49017
49018                 _behaviors.forEach(context.install);
49019
49020                 _keybinding
49021                     .on(_t('inspector.zoom_to.key'), mode.zoomToSelected)
49022                     .on('⎋', esc, true);
49023
49024                 select(document)
49025                     .call(_keybinding);
49026
49027                 selectNote();
49028
49029                 var sidebar = context.ui().sidebar;
49030                 sidebar.show(_noteEditor.note(note).newNote(_newFeature));
49031
49032                 // expand the sidebar, avoid obscuring the note if needed
49033                 sidebar.expand(sidebar.intersects(note.extent()));
49034
49035                 context.map()
49036                     .on('drawn.select', selectNote);
49037             };
49038
49039
49040             mode.exit = function() {
49041                 _behaviors.forEach(context.uninstall);
49042
49043                 select(document)
49044                     .call(_keybinding.unbind);
49045
49046                 context.surface()
49047                     .selectAll('.layer-notes .selected')
49048                     .classed('selected hover', false);
49049
49050                 context.map()
49051                     .on('drawn.select', null);
49052
49053                 context.ui().sidebar
49054                     .hide();
49055
49056                 context.selectedNoteID(null);
49057             };
49058
49059
49060             return mode;
49061         }
49062
49063         function modeDragNote(context) {
49064             var mode = {
49065                 id: 'drag-note',
49066                 button: 'browse'
49067             };
49068
49069             var edit = behaviorEdit(context);
49070
49071             var _nudgeInterval;
49072             var _lastLoc;
49073             var _note;    // most current note.. dragged note may have stale datum.
49074
49075
49076             function startNudge(nudge) {
49077                 if (_nudgeInterval) window.clearInterval(_nudgeInterval);
49078                 _nudgeInterval = window.setInterval(function() {
49079                     context.map().pan(nudge);
49080                     doMove(nudge);
49081                 }, 50);
49082             }
49083
49084
49085             function stopNudge() {
49086                 if (_nudgeInterval) {
49087                     window.clearInterval(_nudgeInterval);
49088                     _nudgeInterval = null;
49089                 }
49090             }
49091
49092
49093             function origin(note) {
49094                 return context.projection(note.loc);
49095             }
49096
49097
49098             function start(note) {
49099                 _note = note;
49100                 var osm = services.osm;
49101                 if (osm) {
49102                     // Get latest note from cache.. The marker may have a stale datum bound to it
49103                     // and dragging it around can sometimes delete the users note comment.
49104                     _note = osm.getNote(_note.id);
49105                 }
49106
49107                 context.surface().selectAll('.note-' + _note.id)
49108                     .classed('active', true);
49109
49110                 context.perform(actionNoop());
49111                 context.enter(mode);
49112                 context.selectedNoteID(_note.id);
49113             }
49114
49115
49116             function move() {
49117                 event.sourceEvent.stopPropagation();
49118                 _lastLoc = context.projection.invert(event.point);
49119
49120                 doMove();
49121                 var nudge = geoViewportEdge(event.point, context.map().dimensions());
49122                 if (nudge) {
49123                     startNudge(nudge);
49124                 } else {
49125                     stopNudge();
49126                 }
49127             }
49128
49129
49130             function doMove(nudge) {
49131                 nudge = nudge || [0, 0];
49132
49133                 var currPoint = (event && event.point) || context.projection(_lastLoc);
49134                 var currMouse = geoVecSubtract(currPoint, nudge);
49135                 var loc = context.projection.invert(currMouse);
49136
49137                 _note = _note.move(loc);
49138
49139                 var osm = services.osm;
49140                 if (osm) {
49141                     osm.replaceNote(_note);  // update note cache
49142                 }
49143
49144                 context.replace(actionNoop());   // trigger redraw
49145             }
49146
49147
49148             function end() {
49149                 context.replace(actionNoop());   // trigger redraw
49150
49151                 context
49152                     .selectedNoteID(_note.id)
49153                     .enter(modeSelectNote(context, _note.id));
49154             }
49155
49156
49157             var drag = behaviorDrag()
49158                 .selector('.layer-touch.markers .target.note.new')
49159                 .surface(context.container().select('.main-map').node())
49160                 .origin(origin)
49161                 .on('start', start)
49162                 .on('move', move)
49163                 .on('end', end);
49164
49165
49166             mode.enter = function() {
49167                 context.install(edit);
49168             };
49169
49170
49171             mode.exit = function() {
49172                 context.ui().sidebar.hover.cancel();
49173                 context.uninstall(edit);
49174
49175                 context.surface()
49176                     .selectAll('.active')
49177                     .classed('active', false);
49178
49179                 stopNudge();
49180             };
49181
49182             mode.behavior = drag;
49183
49184             return mode;
49185         }
49186
49187         function uiDataHeader() {
49188             var _datum;
49189
49190
49191             function dataHeader(selection) {
49192                 var header = selection.selectAll('.data-header')
49193                     .data(
49194                         (_datum ? [_datum] : []),
49195                         function(d) { return d.__featurehash__; }
49196                     );
49197
49198                 header.exit()
49199                     .remove();
49200
49201                 var headerEnter = header.enter()
49202                     .append('div')
49203                     .attr('class', 'data-header');
49204
49205                 var iconEnter = headerEnter
49206                     .append('div')
49207                     .attr('class', 'data-header-icon');
49208
49209                 iconEnter
49210                     .append('div')
49211                     .attr('class', 'preset-icon-28')
49212                     .call(svgIcon('#iD-icon-data', 'note-fill'));
49213
49214                 headerEnter
49215                     .append('div')
49216                     .attr('class', 'data-header-label')
49217                     .text(_t('map_data.layers.custom.title'));
49218             }
49219
49220
49221             dataHeader.datum = function(val) {
49222                 if (!arguments.length) return _datum;
49223                 _datum = val;
49224                 return this;
49225             };
49226
49227
49228             return dataHeader;
49229         }
49230
49231         // This code assumes that the combobox values will not have duplicate entries.
49232         // It is keyed on the `value` of the entry. Data should be an array of objects like:
49233         //   [{
49234         //       value:  'display text',  // required
49235         //       title:  'hover text'     // optional
49236         //   }, ...]
49237
49238         var _comboHideTimerID;
49239
49240         function uiCombobox(context, klass) {
49241             var dispatch$1 = dispatch('accept', 'cancel');
49242             var container = context.container();
49243
49244             var _suggestions = [];
49245             var _data = [];
49246             var _fetched = {};
49247             var _selected = null;
49248             var _canAutocomplete = true;
49249             var _caseSensitive = false;
49250             var _cancelFetch = false;
49251             var _minItems = 2;
49252             var _tDown = 0;
49253             var _mouseEnterHandler, _mouseLeaveHandler;
49254
49255             var _fetcher = function(val, cb) {
49256                 cb(_data.filter(function(d) {
49257                     var terms = d.terms || [];
49258                     terms.push(d.value);
49259                     return terms.some(function(term) {
49260                         return term
49261                             .toString()
49262                             .toLowerCase()
49263                             .indexOf(val.toLowerCase()) !== -1;
49264                     });
49265                 }));
49266             };
49267
49268             var combobox = function(input, attachTo) {
49269                 if (!input || input.empty()) return;
49270
49271                 input
49272                     .classed('combobox-input', true)
49273                     .on('focus.combo-input', focus)
49274                     .on('blur.combo-input', blur)
49275                     .on('keydown.combo-input', keydown)
49276                     .on('keyup.combo-input', keyup)
49277                     .on('input.combo-input', change)
49278                     .on('mousedown.combo-input', mousedown)
49279                     .each(function() {
49280                         var parent = this.parentNode;
49281                         var sibling = this.nextSibling;
49282
49283                         select(parent).selectAll('.combobox-caret')
49284                             .filter(function(d) { return d === input.node(); })
49285                             .data([input.node()])
49286                             .enter()
49287                             .insert('div', function() { return sibling; })
49288                             .attr('class', 'combobox-caret')
49289                             .on('mousedown.combo-caret', function() {
49290                                 event.preventDefault(); // don't steal focus from input
49291                                 input.node().focus(); // focus the input as if it was clicked
49292                                 mousedown();
49293                             })
49294                             .on('mouseup.combo-caret', function() {
49295                                 event.preventDefault(); // don't steal focus from input
49296                                 mouseup();
49297                             });
49298                     });
49299
49300
49301                 function mousedown() {
49302                     if (event.button !== 0) return;    // left click only
49303                     _tDown = +new Date();
49304
49305                     // clear selection
49306                     var start = input.property('selectionStart');
49307                     var end = input.property('selectionEnd');
49308                     if (start !== end) {
49309                         var val = utilGetSetValue(input);
49310                         input.node().setSelectionRange(val.length, val.length);
49311                         return;
49312                     }
49313
49314                     input.on('mouseup.combo-input', mouseup);
49315                 }
49316
49317
49318                 function mouseup() {
49319                     input.on('mouseup.combo-input', null);
49320                     if (event.button !== 0) return;    // left click only
49321                     if (input.node() !== document.activeElement) return;   // exit if this input is not focused
49322
49323                     var start = input.property('selectionStart');
49324                     var end = input.property('selectionEnd');
49325                     if (start !== end) return;  // exit if user is selecting
49326
49327                     // not showing or showing for a different field - try to show it.
49328                     var combo = container.selectAll('.combobox');
49329                     if (combo.empty() || combo.datum() !== input.node()) {
49330                         var tOrig = _tDown;
49331                         window.setTimeout(function() {
49332                             if (tOrig !== _tDown) return;   // exit if user double clicked
49333                             fetchComboData('', function() {
49334                                 show();
49335                                 render();
49336                             });
49337                         }, 250);
49338
49339                     } else {
49340                         hide();
49341                     }
49342                 }
49343
49344
49345                 function focus() {
49346                     fetchComboData('');   // prefetch values (may warm taginfo cache)
49347                 }
49348
49349
49350                 function blur() {
49351                     _comboHideTimerID = window.setTimeout(hide, 75);
49352                 }
49353
49354
49355                 function show() {
49356                     hide();   // remove any existing
49357
49358                     container
49359                         .insert('div', ':first-child')
49360                         .datum(input.node())
49361                         .attr('class', 'combobox' + (klass ? ' combobox-' + klass : ''))
49362                         .style('position', 'absolute')
49363                         .style('display', 'block')
49364                         .style('left', '0px')
49365                         .on('mousedown.combo-container', function () {
49366                             // prevent moving focus out of the input field
49367                             event.preventDefault();
49368                         });
49369
49370                     container
49371                         .on('scroll.combo-scroll', render, true);
49372                 }
49373
49374
49375                 function hide() {
49376                     if (_comboHideTimerID) {
49377                         window.clearTimeout(_comboHideTimerID);
49378                         _comboHideTimerID = undefined;
49379                     }
49380
49381                     container.selectAll('.combobox')
49382                         .remove();
49383
49384                     container
49385                         .on('scroll.combo-scroll', null);
49386                 }
49387
49388
49389                 function keydown() {
49390                     var shown = !container.selectAll('.combobox').empty();
49391                     var tagName = input.node() ? input.node().tagName.toLowerCase() : '';
49392
49393                     switch (event.keyCode) {
49394                         case 8:   // ⌫ Backspace
49395                         case 46:  // ⌦ Delete
49396                             event.stopPropagation();
49397                             _selected = null;
49398                             render();
49399                             input.on('input.combo-input', function() {
49400                                 var start = input.property('selectionStart');
49401                                 input.node().setSelectionRange(start, start);
49402                                 input.on('input.combo-input', change);
49403                             });
49404                             break;
49405
49406                         case 9:   // ⇥ Tab
49407                             accept();
49408                             break;
49409
49410                         case 13:  // ↩ Return
49411                             event.preventDefault();
49412                             event.stopPropagation();
49413                             break;
49414
49415                         case 38:  // ↑ Up arrow
49416                             if (tagName === 'textarea' && !shown) return;
49417                             event.preventDefault();
49418                             if (tagName === 'input' && !shown) {
49419                                 show();
49420                             }
49421                             nav(-1);
49422                             break;
49423
49424                         case 40:  // ↓ Down arrow
49425                             if (tagName === 'textarea' && !shown) return;
49426                             event.preventDefault();
49427                             if (tagName === 'input' && !shown) {
49428                                 show();
49429                             }
49430                             nav(+1);
49431                             break;
49432                     }
49433                 }
49434
49435
49436                 function keyup() {
49437                     switch (event.keyCode) {
49438                         case 27:  // ⎋ Escape
49439                             cancel();
49440                             break;
49441
49442                         case 13:  // ↩ Return
49443                             accept();
49444                             break;
49445                     }
49446                 }
49447
49448
49449                 // Called whenever the input value is changed (e.g. on typing)
49450                 function change() {
49451                     fetchComboData(value(), function() {
49452                         _selected = null;
49453                         var val = input.property('value');
49454
49455                         if (_suggestions.length) {
49456                             if (input.property('selectionEnd') === val.length) {
49457                                 _selected = tryAutocomplete();
49458                             }
49459
49460                             if (!_selected) {
49461                                 _selected = val;
49462                             }
49463                         }
49464
49465                         if (val.length) {
49466                             var combo = container.selectAll('.combobox');
49467                             if (combo.empty()) {
49468                                 show();
49469                             }
49470                         } else {
49471                             hide();
49472                         }
49473
49474                         render();
49475                     });
49476                 }
49477
49478
49479                 // Called when the user presses up/down arrows to navigate the list
49480                 function nav(dir) {
49481                     if (_suggestions.length) {
49482                         // try to determine previously selected index..
49483                         var index = -1;
49484                         for (var i = 0; i < _suggestions.length; i++) {
49485                             if (_selected && _suggestions[i].value === _selected) {
49486                                 index = i;
49487                                 break;
49488                             }
49489                         }
49490
49491                         // pick new _selected
49492                         index = Math.max(Math.min(index + dir, _suggestions.length - 1), 0);
49493                         _selected = _suggestions[index].value;
49494                         input.property('value', _selected);
49495                     }
49496
49497                     render();
49498                     ensureVisible();
49499                 }
49500
49501
49502                 function ensureVisible() {
49503                     var combo = container.selectAll('.combobox');
49504                     if (combo.empty()) return;
49505
49506                     var containerRect = container.node().getBoundingClientRect();
49507                     var comboRect = combo.node().getBoundingClientRect();
49508
49509                     if (comboRect.bottom > containerRect.bottom) {
49510                         var node = attachTo ? attachTo.node() : input.node();
49511                         node.scrollIntoView({ behavior: 'instant', block: 'center' });
49512                         render();
49513                     }
49514
49515                     // https://stackoverflow.com/questions/11039885/scrollintoview-causing-the-whole-page-to-move
49516                     var selected = combo.selectAll('.combobox-option.selected').node();
49517                     if (selected) {
49518                         selected.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
49519                     }
49520                 }
49521
49522
49523                 function value() {
49524                     var value = input.property('value');
49525                     var start = input.property('selectionStart');
49526                     var end = input.property('selectionEnd');
49527
49528                     if (start && end) {
49529                         value = value.substring(0, start);
49530                     }
49531
49532                     return value;
49533                 }
49534
49535
49536                 function fetchComboData(v, cb) {
49537                     _cancelFetch = false;
49538
49539                     _fetcher.call(input, v, function(results) {
49540                         // already chose a value, don't overwrite or autocomplete it
49541                         if (_cancelFetch) return;
49542
49543                         _suggestions = results;
49544                         results.forEach(function(d) { _fetched[d.value] = d; });
49545
49546                         if (cb) {
49547                             cb();
49548                         }
49549                     });
49550                 }
49551
49552
49553                 function tryAutocomplete() {
49554                     if (!_canAutocomplete) return;
49555
49556                     var val = _caseSensitive ? value() : value().toLowerCase();
49557                     if (!val) return;
49558
49559                     // Don't autocomplete if user is typing a number - #4935
49560                     if (!isNaN(parseFloat(val)) && isFinite(val)) return;
49561
49562                     var bestIndex = -1;
49563                     for (var i = 0; i < _suggestions.length; i++) {
49564                         var suggestion = _suggestions[i].value;
49565                         var compare = _caseSensitive ? suggestion : suggestion.toLowerCase();
49566
49567                         // if search string matches suggestion exactly, pick it..
49568                         if (compare === val) {
49569                             bestIndex = i;
49570                             break;
49571
49572                         // otherwise lock in the first result that starts with the search string..
49573                         } else if (bestIndex === -1 && compare.indexOf(val) === 0) {
49574                             bestIndex = i;
49575                         }
49576                     }
49577
49578                     if (bestIndex !== -1) {
49579                         var bestVal = _suggestions[bestIndex].value;
49580                         input.property('value', bestVal);
49581                         input.node().setSelectionRange(val.length, bestVal.length);
49582                         return bestVal;
49583                     }
49584                 }
49585
49586
49587                 function render() {
49588                     if (_suggestions.length < _minItems || document.activeElement !== input.node()) {
49589                         hide();
49590                         return;
49591                     }
49592
49593                     var shown = !container.selectAll('.combobox').empty();
49594                     if (!shown) return;
49595
49596                     var combo = container.selectAll('.combobox');
49597                     var options = combo.selectAll('.combobox-option')
49598                         .data(_suggestions, function(d) { return d.value; });
49599
49600                     options.exit()
49601                         .remove();
49602
49603                     // enter/update
49604                     options.enter()
49605                         .append('a')
49606                         .attr('class', 'combobox-option')
49607                         .attr('title', function(d) { return d.title; })
49608                         .text(function(d) { return d.display || d.value; })
49609                         .on('mouseenter', _mouseEnterHandler)
49610                         .on('mouseleave', _mouseLeaveHandler)
49611                         .merge(options)
49612                         .classed('selected', function(d) { return d.value === _selected; })
49613                         .on('click.combo-option', accept)
49614                         .order();
49615
49616                     var node = attachTo ? attachTo.node() : input.node();
49617                     var containerRect = container.node().getBoundingClientRect();
49618                     var rect = node.getBoundingClientRect();
49619
49620                     combo
49621                         .style('left', (rect.left + 5 - containerRect.left) + 'px')
49622                         .style('width', (rect.width - 10) + 'px')
49623                         .style('top', (rect.height + rect.top - containerRect.top) + 'px');
49624                 }
49625
49626
49627                 // Dispatches an 'accept' event
49628                 // Then hides the combobox.
49629                 function accept(d) {
49630                     _cancelFetch = true;
49631                     var thiz = input.node();
49632
49633                     if (d) {   // user clicked on a suggestion
49634                         utilGetSetValue(input, d.value);    // replace field contents
49635                         utilTriggerEvent(input, 'change');
49636                     }
49637
49638                     // clear (and keep) selection
49639                     var val = utilGetSetValue(input);
49640                     thiz.setSelectionRange(val.length, val.length);
49641
49642                     d = _fetched[val];
49643                     dispatch$1.call('accept', thiz, d, val);
49644                     hide();
49645                 }
49646
49647
49648                 // Dispatches an 'cancel' event
49649                 // Then hides the combobox.
49650                 function cancel() {
49651                     _cancelFetch = true;
49652                     var thiz = input.node();
49653
49654                     // clear (and remove) selection, and replace field contents
49655                     var val = utilGetSetValue(input);
49656                     var start = input.property('selectionStart');
49657                     var end = input.property('selectionEnd');
49658                     val = val.slice(0, start) + val.slice(end);
49659                     utilGetSetValue(input, val);
49660                     thiz.setSelectionRange(val.length, val.length);
49661
49662                     dispatch$1.call('cancel', thiz);
49663                     hide();
49664                 }
49665
49666             };
49667
49668
49669             combobox.canAutocomplete = function(val) {
49670                 if (!arguments.length) return _canAutocomplete;
49671                 _canAutocomplete = val;
49672                 return combobox;
49673             };
49674
49675             combobox.caseSensitive = function(val) {
49676                 if (!arguments.length) return _caseSensitive;
49677                 _caseSensitive = val;
49678                 return combobox;
49679             };
49680
49681             combobox.data = function(val) {
49682                 if (!arguments.length) return _data;
49683                 _data = val;
49684                 return combobox;
49685             };
49686
49687             combobox.fetcher = function(val) {
49688                 if (!arguments.length) return _fetcher;
49689                 _fetcher = val;
49690                 return combobox;
49691             };
49692
49693             combobox.minItems = function(val) {
49694                 if (!arguments.length) return _minItems;
49695                 _minItems = val;
49696                 return combobox;
49697             };
49698
49699             combobox.itemsMouseEnter = function(val) {
49700                 if (!arguments.length) return _mouseEnterHandler;
49701                 _mouseEnterHandler = val;
49702                 return combobox;
49703             };
49704
49705             combobox.itemsMouseLeave = function(val) {
49706                 if (!arguments.length) return _mouseLeaveHandler;
49707                 _mouseLeaveHandler = val;
49708                 return combobox;
49709             };
49710
49711             return utilRebind(combobox, dispatch$1, 'on');
49712         }
49713
49714
49715         uiCombobox.off = function(input, context) {
49716             input
49717                 .on('focus.combo-input', null)
49718                 .on('blur.combo-input', null)
49719                 .on('keydown.combo-input', null)
49720                 .on('keyup.combo-input', null)
49721                 .on('input.combo-input', null)
49722                 .on('mousedown.combo-input', null)
49723                 .on('mouseup.combo-input', null);
49724
49725
49726             context.container()
49727                 .on('scroll.combo-scroll', null);
49728         };
49729
49730         // toggles the visibility of ui elements, using a combination of the
49731         // hide class, which sets display=none, and a d3 transition for opacity.
49732         // this will cause blinking when called repeatedly, so check that the
49733         // value actually changes between calls.
49734         function uiToggle(show, callback) {
49735             return function(selection) {
49736                 selection
49737                     .style('opacity', show ? 0 : 1)
49738                     .classed('hide', false)
49739                     .transition()
49740                     .style('opacity', show ? 1 : 0)
49741                     .on('end', function() {
49742                         select(this)
49743                             .classed('hide', !show)
49744                             .style('opacity', null);
49745                         if (callback) callback.apply(this);
49746                     });
49747             };
49748         }
49749
49750         function uiDisclosure(context, key, expandedDefault) {
49751             var dispatch$1 = dispatch('toggled');
49752             var _expanded;
49753             var _title = utilFunctor('');
49754             var _updatePreference = true;
49755             var _content = function () {};
49756
49757
49758             var disclosure = function(selection) {
49759
49760                 if (_expanded === undefined || _expanded === null) {
49761                     // loading _expanded here allows it to be reset by calling `disclosure.expanded(null)`
49762
49763                     var preference = corePreferences('disclosure.' + key + '.expanded');
49764                     _expanded = preference === null ? !!expandedDefault : (preference === 'true');
49765                 }
49766
49767                 var hideToggle = selection.selectAll('.hide-toggle-' + key)
49768                     .data([0]);
49769
49770                 // enter
49771                 var hideToggleEnter = hideToggle.enter()
49772                     .append('a')
49773                     .attr('href', '#')
49774                     .attr('class', 'hide-toggle hide-toggle-' + key)
49775                     .call(svgIcon('', 'pre-text', 'hide-toggle-icon'));
49776
49777                 hideToggleEnter
49778                     .append('span')
49779                     .attr('class', 'hide-toggle-text');
49780
49781                 // update
49782                 hideToggle = hideToggleEnter
49783                     .merge(hideToggle);
49784
49785                 hideToggle
49786                     .on('click', toggle)
49787                     .classed('expanded', _expanded);
49788
49789                 hideToggle.selectAll('.hide-toggle-text')
49790                     .text(_title());
49791
49792                 hideToggle.selectAll('.hide-toggle-icon')
49793                     .attr('xlink:href', _expanded ? '#iD-icon-down'
49794                         : (_mainLocalizer.textDirection() === 'rtl') ? '#iD-icon-backward' : '#iD-icon-forward'
49795                     );
49796
49797
49798                 var wrap = selection.selectAll('.disclosure-wrap')
49799                     .data([0]);
49800
49801                 // enter/update
49802                 wrap = wrap.enter()
49803                     .append('div')
49804                     .attr('class', 'disclosure-wrap disclosure-wrap-' + key)
49805                     .merge(wrap)
49806                     .classed('hide', !_expanded);
49807
49808                 if (_expanded) {
49809                     wrap
49810                         .call(_content);
49811                 }
49812
49813
49814                 function toggle() {
49815                     event.preventDefault();
49816
49817                     _expanded = !_expanded;
49818
49819                     if (_updatePreference) {
49820                         corePreferences('disclosure.' + key + '.expanded', _expanded);
49821                     }
49822
49823                     hideToggle
49824                         .classed('expanded', _expanded);
49825
49826                     hideToggle.selectAll('.hide-toggle-icon')
49827                         .attr('xlink:href', _expanded ? '#iD-icon-down'
49828                             : (_mainLocalizer.textDirection() === 'rtl') ? '#iD-icon-backward' : '#iD-icon-forward'
49829                         );
49830
49831                     wrap
49832                         .call(uiToggle(_expanded));
49833
49834                     if (_expanded) {
49835                         wrap
49836                             .call(_content);
49837                     }
49838
49839                     dispatch$1.call('toggled', this, _expanded);
49840                 }
49841             };
49842
49843
49844             disclosure.title = function(val) {
49845                 if (!arguments.length) return _title;
49846                 _title = utilFunctor(val);
49847                 return disclosure;
49848             };
49849
49850
49851             disclosure.expanded = function(val) {
49852                 if (!arguments.length) return _expanded;
49853                 _expanded = val;
49854                 return disclosure;
49855             };
49856
49857
49858             disclosure.updatePreference = function(val) {
49859                 if (!arguments.length) return _updatePreference;
49860                 _updatePreference = val;
49861                 return disclosure;
49862             };
49863
49864
49865             disclosure.content = function(val) {
49866                 if (!arguments.length) return _content;
49867                 _content = val;
49868                 return disclosure;
49869             };
49870
49871
49872             return utilRebind(disclosure, dispatch$1, 'on');
49873         }
49874
49875         // A unit of controls or info to be used in a layout, such as within a pane.
49876         // Can be labeled and collapsible.
49877         function uiSection(id, context) {
49878
49879             var _classes = utilFunctor('');
49880             var _shouldDisplay;
49881             var _content;
49882
49883             var _disclosure;
49884             var _title;
49885             var _expandedByDefault = utilFunctor(true);
49886             var _disclosureContent;
49887             var _disclosureExpanded;
49888
49889             var _containerSelection = select(null);
49890
49891             var section = {
49892                 id: id
49893             };
49894
49895             section.classes = function(val) {
49896                 if (!arguments.length) return _classes;
49897                 _classes = utilFunctor(val);
49898                 return section;
49899             };
49900
49901             section.title = function(val) {
49902                 if (!arguments.length) return _title;
49903                 _title = utilFunctor(val);
49904                 return section;
49905             };
49906
49907             section.expandedByDefault = function(val) {
49908                 if (!arguments.length) return _expandedByDefault;
49909                 _expandedByDefault = utilFunctor(val);
49910                 return section;
49911             };
49912
49913             section.shouldDisplay = function(val) {
49914                 if (!arguments.length) return _shouldDisplay;
49915                 _shouldDisplay = utilFunctor(val);
49916                 return section;
49917             };
49918
49919             section.content = function(val) {
49920                 if (!arguments.length) return _content;
49921                 _content = val;
49922                 return section;
49923             };
49924
49925             section.disclosureContent = function(val) {
49926                 if (!arguments.length) return _disclosureContent;
49927                 _disclosureContent = val;
49928                 return section;
49929             };
49930
49931             section.disclosureExpanded = function(val) {
49932                 if (!arguments.length) return _disclosureExpanded;
49933                 _disclosureExpanded = val;
49934                 return section;
49935             };
49936
49937             // may be called multiple times
49938             section.render = function(selection) {
49939
49940                 _containerSelection = selection
49941                     .selectAll('.section-' + id)
49942                     .data([0]);
49943
49944                 var sectionEnter = _containerSelection
49945                     .enter()
49946                     .append('div')
49947                     .attr('class', 'section section-' + id + ' ' + (_classes && _classes() || ''));
49948
49949                 _containerSelection = sectionEnter
49950                     .merge(_containerSelection);
49951
49952                 _containerSelection
49953                     .call(renderContent);
49954             };
49955
49956             section.reRender = function() {
49957                 _containerSelection
49958                     .call(renderContent);
49959             };
49960
49961             section.selection = function() {
49962                 return _containerSelection;
49963             };
49964
49965             section.disclosure = function() {
49966                 return _disclosure;
49967             };
49968
49969             // may be called multiple times
49970             function renderContent(selection) {
49971                 if (_shouldDisplay) {
49972                     var shouldDisplay = _shouldDisplay();
49973                     selection.classed('hide', !shouldDisplay);
49974                     if (!shouldDisplay) {
49975                         selection.html('');
49976                         return;
49977                     }
49978                 }
49979
49980                 if (_disclosureContent) {
49981                     if (!_disclosure) {
49982                         _disclosure = uiDisclosure(context, id.replace(/-/g, '_'), _expandedByDefault())
49983                             .title(_title || '')
49984                             /*.on('toggled', function(expanded) {
49985                                 if (expanded) { selection.node().parentNode.scrollTop += 200; }
49986                             })*/
49987                             .content(_disclosureContent);
49988                     }
49989                     if (_disclosureExpanded !== undefined) {
49990                         _disclosure.expanded(_disclosureExpanded);
49991                         _disclosureExpanded = undefined;
49992                     }
49993                     selection
49994                         .call(_disclosure);
49995
49996                     return;
49997                 }
49998
49999                 if (_content) {
50000                     selection
50001                         .call(_content);
50002                 }
50003             }
50004
50005             return section;
50006         }
50007
50008         // Pass `which` object of the form:
50009         // {
50010         //   key: 'string',     // required
50011         //   value: 'string'    // optional
50012         // }
50013         //   -or-
50014         // {
50015         //   rtype: 'string'    // relation type  (e.g. 'multipolygon')
50016         // }
50017         //   -or-
50018         // {
50019         //   qid: 'string'      // brand wikidata  (e.g. 'Q37158')
50020         // }
50021         //
50022         function uiTagReference(what) {
50023             var wikibase = what.qid ? services.wikidata : services.osmWikibase;
50024             var tagReference = {};
50025
50026             var _button = select(null);
50027             var _body = select(null);
50028             var _loaded;
50029             var _showing;
50030
50031
50032             function load() {
50033                 if (!wikibase) return;
50034
50035                 _button
50036                     .classed('tag-reference-loading', true);
50037
50038                 wikibase.getDocs(what, gotDocs);
50039             }
50040
50041
50042             function gotDocs(err, docs) {
50043                 _body.html('');
50044
50045                 if (!docs || !docs.title) {
50046                     _body
50047                         .append('p')
50048                         .attr('class', 'tag-reference-description')
50049                         .text(_t('inspector.no_documentation_key'));
50050                     done();
50051                     return;
50052                 }
50053
50054                 if (docs.imageURL) {
50055                     _body
50056                         .append('img')
50057                         .attr('class', 'tag-reference-wiki-image')
50058                         .attr('src', docs.imageURL)
50059                         .on('load', function() { done(); })
50060                         .on('error', function() { select(this).remove(); done(); });
50061                 } else {
50062                     done();
50063                 }
50064
50065                 _body
50066                     .append('p')
50067                     .attr('class', 'tag-reference-description')
50068                     .text(docs.description || _t('inspector.no_documentation_key'))
50069                     .append('a')
50070                     .attr('class', 'tag-reference-edit')
50071                     .attr('target', '_blank')
50072                     .attr('tabindex', -1)
50073                     .attr('title', _t('inspector.edit_reference'))
50074                     .attr('href', docs.editURL)
50075                     .call(svgIcon('#iD-icon-edit', 'inline'));
50076
50077                 if (docs.wiki) {
50078                     _body
50079                       .append('a')
50080                       .attr('class', 'tag-reference-link')
50081                       .attr('target', '_blank')
50082                       .attr('tabindex', -1)
50083                       .attr('href', docs.wiki.url)
50084                       .call(svgIcon('#iD-icon-out-link', 'inline'))
50085                       .append('span')
50086                       .text(_t(docs.wiki.text));
50087                 }
50088
50089                 // Add link to info about "good changeset comments" - #2923
50090                 if (what.key === 'comment') {
50091                     _body
50092                         .append('a')
50093                         .attr('class', 'tag-reference-comment-link')
50094                         .attr('target', '_blank')
50095                         .attr('tabindex', -1)
50096                         .call(svgIcon('#iD-icon-out-link', 'inline'))
50097                         .attr('href', _t('commit.about_changeset_comments_link'))
50098                         .append('span')
50099                         .text(_t('commit.about_changeset_comments'));
50100                 }
50101             }
50102
50103
50104             function done() {
50105                 _loaded = true;
50106
50107                 _button
50108                     .classed('tag-reference-loading', false);
50109
50110                 _body
50111                     .classed('expanded', true)
50112                     .transition()
50113                     .duration(200)
50114                     .style('max-height', '200px')
50115                     .style('opacity', '1');
50116
50117                 _showing = true;
50118
50119                 _button.selectAll('svg.icon use').each(function() {
50120                     var iconUse = select(this);
50121                     if (iconUse.attr('href') === '#iD-icon-info') {
50122                         iconUse.attr('href', '#iD-icon-info-filled');
50123                     }
50124                 });
50125             }
50126
50127
50128             function hide() {
50129                 _body
50130                     .transition()
50131                     .duration(200)
50132                     .style('max-height', '0px')
50133                     .style('opacity', '0')
50134                     .on('end', function () {
50135                         _body.classed('expanded', false);
50136                     });
50137
50138                 _showing = false;
50139
50140                 _button.selectAll('svg.icon use').each(function() {
50141                     var iconUse = select(this);
50142                     if (iconUse.attr('href') === '#iD-icon-info-filled') {
50143                         iconUse.attr('href', '#iD-icon-info');
50144                     }
50145                 });
50146
50147             }
50148
50149
50150             tagReference.button = function(selection, klass, iconName) {
50151                 _button = selection.selectAll('.tag-reference-button')
50152                     .data([0]);
50153
50154                 _button = _button.enter()
50155                     .append('button')
50156                     .attr('class', 'tag-reference-button ' + klass)
50157                     .attr('title', _t('icons.information'))
50158                     .attr('tabindex', -1)
50159                     .call(svgIcon('#iD-icon-' + (iconName || 'inspect')))
50160                     .merge(_button);
50161
50162                 _button
50163                     .on('click', function () {
50164                         event.stopPropagation();
50165                         event.preventDefault();
50166                         this.blur();    // avoid keeping focus on the button - #4641
50167                         if (_showing) {
50168                             hide();
50169                         } else if (_loaded) {
50170                             done();
50171                         } else {
50172                             load();
50173                         }
50174                     });
50175             };
50176
50177
50178             tagReference.body = function(selection) {
50179                 var itemID = what.qid || what.rtype || (what.key + '-' + what.value);
50180                 _body = selection.selectAll('.tag-reference-body')
50181                     .data([itemID], function(d) { return d; });
50182
50183                 _body.exit()
50184                     .remove();
50185
50186                 _body = _body.enter()
50187                     .append('div')
50188                     .attr('class', 'tag-reference-body')
50189                     .style('max-height', '0')
50190                     .style('opacity', '0')
50191                     .merge(_body);
50192
50193                 if (_showing === false) {
50194                     hide();
50195                 }
50196             };
50197
50198
50199             tagReference.showing = function(val) {
50200                 if (!arguments.length) return _showing;
50201                 _showing = val;
50202                 return tagReference;
50203             };
50204
50205
50206             return tagReference;
50207         }
50208
50209         function uiSectionRawTagEditor(id, context) {
50210
50211             var section = uiSection(id, context)
50212                 .classes('raw-tag-editor')
50213                 .title(function() {
50214                     var count = Object.keys(_tags).filter(function(d) { return d; }).length;
50215                     return _t('inspector.title_count', { title: _t('inspector.tags'), count: count });
50216                 })
50217                 .expandedByDefault(false)
50218                 .disclosureContent(renderDisclosureContent);
50219
50220             var taginfo = services.taginfo;
50221             var dispatch$1 = dispatch('change');
50222             var availableViews = [
50223                 { id: 'text', icon: '#fas-i-cursor' },
50224                 { id: 'list', icon: '#fas-th-list' }
50225             ];
50226
50227             var _tagView = (corePreferences('raw-tag-editor-view') || 'list');   // 'list, 'text'
50228             var _readOnlyTags = [];
50229             // the keys in the order we want them to display
50230             var _orderedKeys = [];
50231             var _showBlank = false;
50232             var _pendingChange = null;
50233             var _state;
50234             var _presets;
50235             var _tags;
50236             var _entityIDs;
50237
50238             function renderDisclosureContent(wrap) {
50239
50240                 // remove deleted keys
50241                 _orderedKeys = _orderedKeys.filter(function(key) {
50242                     return _tags[key] !== undefined;
50243                 });
50244
50245                 // When switching to a different entity or changing the state (hover/select)
50246                 // reorder the keys alphabetically.
50247                 // We trigger this by emptying the `_orderedKeys` array, then it will be rebuilt here.
50248                 // Otherwise leave their order alone - #5857, #5927
50249                 var all = Object.keys(_tags).sort();
50250                 var missingKeys = utilArrayDifference(all, _orderedKeys);
50251                 for (var i in missingKeys) {
50252                     _orderedKeys.push(missingKeys[i]);
50253                 }
50254
50255                 // assemble row data
50256                 var rowData = _orderedKeys.map(function(key, i) {
50257                     return { index: i, key: key, value: _tags[key] };
50258                 });
50259
50260                 // append blank row last, if necessary
50261                 if (!rowData.length || _showBlank) {
50262                     _showBlank = false;
50263                     rowData.push({ index: rowData.length, key: '', value: '' });
50264                 }
50265
50266
50267                 // View Options
50268                 var options = wrap.selectAll('.raw-tag-options')
50269                     .data([0]);
50270
50271                 options.exit()
50272                     .remove();
50273
50274                 var optionsEnter = options.enter()
50275                     .insert('div', ':first-child')
50276                     .attr('class', 'raw-tag-options');
50277
50278                 var optionEnter = optionsEnter.selectAll('.raw-tag-option')
50279                     .data(availableViews, function(d) { return d.id; })
50280                     .enter();
50281
50282                 optionEnter
50283                     .append('button')
50284                     .attr('class', function(d) {
50285                         return 'raw-tag-option raw-tag-option-' + d.id + (_tagView === d.id ? ' selected' : '');
50286                     })
50287                     .attr('title', function(d) { return _t('icons.' + d.id); })
50288                     .on('click', function(d) {
50289                         _tagView = d.id;
50290                         corePreferences('raw-tag-editor-view', d.id);
50291
50292                         wrap.selectAll('.raw-tag-option')
50293                             .classed('selected', function(datum) { return datum === d; });
50294
50295                         wrap.selectAll('.tag-text')
50296                             .classed('hide', (d.id !== 'text'))
50297                             .each(setTextareaHeight);
50298
50299                         wrap.selectAll('.tag-list, .add-row')
50300                             .classed('hide', (d.id !== 'list'));
50301                     })
50302                     .each(function(d) {
50303                         select(this)
50304                             .call(svgIcon(d.icon));
50305                     });
50306
50307
50308                 // View as Text
50309                 var textData = rowsToText(rowData);
50310                 var textarea = wrap.selectAll('.tag-text')
50311                     .data([0]);
50312
50313                 textarea = textarea.enter()
50314                     .append('textarea')
50315                     .attr('class', 'tag-text' + (_tagView !== 'text' ? ' hide' : ''))
50316                     .call(utilNoAuto)
50317                     .attr('placeholder', _t('inspector.key_value'))
50318                     .attr('spellcheck', 'false')
50319                     .merge(textarea);
50320
50321                 textarea
50322                     .call(utilGetSetValue, textData)
50323                     .each(setTextareaHeight)
50324                     .on('input', setTextareaHeight)
50325                     .on('blur', textChanged)
50326                     .on('change', textChanged);
50327
50328
50329                 // View as List
50330                 var list = wrap.selectAll('.tag-list')
50331                     .data([0]);
50332
50333                 list = list.enter()
50334                     .append('ul')
50335                     .attr('class', 'tag-list' + (_tagView !== 'list' ? ' hide' : ''))
50336                     .merge(list);
50337
50338
50339                 // Container for the Add button
50340                 var addRowEnter = wrap.selectAll('.add-row')
50341                     .data([0])
50342                     .enter()
50343                     .append('div')
50344                     .attr('class', 'add-row' + (_tagView !== 'list' ? ' hide' : ''));
50345
50346                 addRowEnter
50347                     .append('button')
50348                     .attr('class', 'add-tag')
50349                     .call(svgIcon('#iD-icon-plus', 'light'))
50350                     .on('click', addTag);
50351
50352                 addRowEnter
50353                     .append('div')
50354                     .attr('class', 'space-value');   // preserve space
50355
50356                 addRowEnter
50357                     .append('div')
50358                     .attr('class', 'space-buttons');  // preserve space
50359
50360
50361                 // Tag list items
50362                 var items = list.selectAll('.tag-row')
50363                     .data(rowData, function(d) { return d.key; });
50364
50365                 items.exit()
50366                     .each(unbind)
50367                     .remove();
50368
50369
50370                 // Enter
50371                 var itemsEnter = items.enter()
50372                     .append('li')
50373                     .attr('class', 'tag-row')
50374                     .classed('readonly', isReadOnly);
50375
50376                 var innerWrap = itemsEnter.append('div')
50377                     .attr('class', 'inner-wrap');
50378
50379                 innerWrap
50380                     .append('div')
50381                     .attr('class', 'key-wrap')
50382                     .append('input')
50383                     .property('type', 'text')
50384                     .attr('class', 'key')
50385                     .call(utilNoAuto)
50386                     .on('blur', keyChange)
50387                     .on('change', keyChange);
50388
50389                 innerWrap
50390                     .append('div')
50391                     .attr('class', 'value-wrap')
50392                     .append('input')
50393                     .property('type', 'text')
50394                     .attr('class', 'value')
50395                     .call(utilNoAuto)
50396                     .on('blur', valueChange)
50397                     .on('change', valueChange)
50398                     .on('keydown.push-more', pushMore);
50399
50400                 innerWrap
50401                     .append('button')
50402                     .attr('tabindex', -1)
50403                     .attr('class', 'form-field-button remove')
50404                     .attr('title', _t('icons.remove'))
50405                     .call(svgIcon('#iD-operation-delete'));
50406
50407
50408                 // Update
50409                 items = items
50410                     .merge(itemsEnter)
50411                     .sort(function(a, b) { return a.index - b.index; });
50412
50413                 items
50414                     .each(function(d) {
50415                         var row = select(this);
50416                         var key = row.select('input.key');      // propagate bound data
50417                         var value = row.select('input.value');  // propagate bound data
50418
50419                         if (_entityIDs && taginfo && _state !== 'hover') {
50420                             bindTypeahead(key, value);
50421                         }
50422
50423                         var reference;
50424
50425                         if (typeof d.value !== 'string') {
50426                             reference = uiTagReference({ key: d.key });
50427                         } else {
50428                             var isRelation = _entityIDs && _entityIDs.some(function(entityID) {
50429                                 return context.entity(entityID).type === 'relation';
50430                             });
50431                             if (isRelation && d.key === 'type') {
50432                                 reference = uiTagReference({ rtype: d.value });
50433                             } else {
50434                                 reference = uiTagReference({ key: d.key, value: d.value });
50435                             }
50436                         }
50437
50438                         if (_state === 'hover') {
50439                             reference.showing(false);
50440                         }
50441
50442                         row.select('.inner-wrap')      // propagate bound data
50443                             .call(reference.button);
50444
50445                         row.call(reference.body);
50446
50447                         row.select('button.remove');   // propagate bound data
50448                     });
50449
50450                 items.selectAll('input.key')
50451                     .attr('title', function(d) { return d.key; })
50452                     .call(utilGetSetValue, function(d) { return d.key; })
50453                     .attr('readonly', function(d) {
50454                         return (isReadOnly(d) || (typeof d.value !== 'string')) || null;
50455                     });
50456
50457                 items.selectAll('input.value')
50458                     .attr('title', function(d) {
50459                         return Array.isArray(d.value) ? d.value.filter(Boolean).join('\n') : d.value;
50460                     })
50461                     .classed('mixed', function(d) {
50462                         return Array.isArray(d.value);
50463                     })
50464                     .attr('placeholder', function(d) {
50465                         return typeof d.value === 'string' ? null : _t('inspector.multiple_values');
50466                     })
50467                     .call(utilGetSetValue, function(d) {
50468                         return typeof d.value === 'string' ? d.value : '';
50469                     })
50470                     .attr('readonly', function(d) {
50471                         return isReadOnly(d) || null;
50472                     });
50473
50474                 items.selectAll('button.remove')
50475                     .on(('PointerEvent' in window ? 'pointer' : 'mouse') + 'down', removeTag);  // 'click' fires too late - #5878
50476
50477             }
50478
50479             function isReadOnly(d) {
50480                 for (var i = 0; i < _readOnlyTags.length; i++) {
50481                     if (d.key.match(_readOnlyTags[i]) !== null) {
50482                         return true;
50483                     }
50484                 }
50485                 return false;
50486             }
50487
50488             function setTextareaHeight() {
50489                 if (_tagView !== 'text') return;
50490
50491                 var selection = select(this);
50492                 selection.style('height', null);
50493                 selection.style('height', selection.node().scrollHeight + 5 + 'px');
50494             }
50495
50496             function stringify(s) {
50497                 return JSON.stringify(s).slice(1, -1);   // without leading/trailing "
50498             }
50499
50500             function unstringify(s) {
50501                 var leading = '';
50502                 var trailing = '';
50503                 if (s.length < 1 || s.charAt(0) !== '"') {
50504                     leading = '"';
50505                 }
50506                 if (s.length < 2 || s.charAt(s.length - 1) !== '"' ||
50507                     (s.charAt(s.length - 1) === '"' && s.charAt(s.length - 2) === '\\')
50508                 ) {
50509                     trailing = '"';
50510                 }
50511                 return JSON.parse(leading + s + trailing);
50512             }
50513
50514             function rowsToText(rows) {
50515                 var str = rows
50516                     .filter(function(row) { return row.key && row.key.trim() !== ''; })
50517                     .map(function(row) {
50518                         var rawVal = row.value;
50519                         if (typeof rawVal !== 'string') rawVal = '*';
50520                         var val = rawVal ? stringify(rawVal) : '';
50521                         return stringify(row.key) + '=' + val;
50522                     })
50523                     .join('\n');
50524
50525                 if (_state !== 'hover' && str.length) {
50526                     return str + '\n';
50527                 }
50528                 return  str;
50529             }
50530
50531             function textChanged() {
50532                 var newText = this.value.trim();
50533                 var newTags = {};
50534                 newText.split('\n').forEach(function(row) {
50535                     var m = row.match(/^\s*([^=]+)=(.*)$/);
50536                     if (m !== null) {
50537                         var k = context.cleanTagKey(unstringify(m[1].trim()));
50538                         var v = context.cleanTagValue(unstringify(m[2].trim()));
50539                         newTags[k] = v;
50540                     }
50541                 });
50542
50543                 var tagDiff = utilTagDiff(_tags, newTags);
50544                 if (!tagDiff.length) return;
50545
50546                 _pendingChange  = _pendingChange || {};
50547
50548                 tagDiff.forEach(function(change) {
50549                     if (isReadOnly({ key: change.key })) return;
50550
50551                     // skip unchanged multiselection placeholders
50552                     if (change.newVal === '*' && typeof change.oldVal !== 'string') return;
50553
50554                     if (change.type === '-') {
50555                         _pendingChange[change.key] = undefined;
50556                     } else if (change.type === '+') {
50557                         _pendingChange[change.key] = change.newVal || '';
50558                     }
50559                 });
50560
50561                 if (Object.keys(_pendingChange).length === 0) {
50562                     _pendingChange = null;
50563                     return;
50564                 }
50565
50566                 scheduleChange();
50567             }
50568
50569             function pushMore() {
50570                 // if pressing Tab on the last value field with content, add a blank row
50571                 if (event.keyCode === 9 && !event.shiftKey &&
50572                     section.selection().selectAll('.tag-list li:last-child input.value').node() === this &&
50573                     utilGetSetValue(select(this))) {
50574                     addTag();
50575                 }
50576             }
50577
50578             function bindTypeahead(key, value) {
50579                 if (isReadOnly(key.datum())) return;
50580
50581                 if (Array.isArray(value.datum().value)) {
50582                     value.call(uiCombobox(context, 'tag-value')
50583                         .minItems(1)
50584                         .fetcher(function(value, callback) {
50585                             var keyString = utilGetSetValue(key);
50586                             if (!_tags[keyString]) return;
50587                             var data = _tags[keyString].filter(Boolean).map(function(tagValue) {
50588                                 return {
50589                                     value: tagValue,
50590                                     title: tagValue
50591                                 };
50592                             });
50593                             callback(data);
50594                         }));
50595                     return;
50596                 }
50597
50598                 var geometry = context.graph().geometry(_entityIDs[0]);
50599
50600                 key.call(uiCombobox(context, 'tag-key')
50601                     .fetcher(function(value, callback) {
50602                         taginfo.keys({
50603                             debounce: true,
50604                             geometry: geometry,
50605                             query: value
50606                         }, function(err, data) {
50607                             if (!err) {
50608                                 var filtered = data.filter(function(d) { return _tags[d.value] === undefined; });
50609                                 callback(sort(value, filtered));
50610                             }
50611                         });
50612                     }));
50613
50614                 value.call(uiCombobox(context, 'tag-value')
50615                     .fetcher(function(value, callback) {
50616                         taginfo.values({
50617                             debounce: true,
50618                             key: utilGetSetValue(key),
50619                             geometry: geometry,
50620                             query: value
50621                         }, function(err, data) {
50622                             if (!err) callback(sort(value, data));
50623                         });
50624                     }));
50625
50626
50627                 function sort(value, data) {
50628                     var sameletter = [];
50629                     var other = [];
50630                     for (var i = 0; i < data.length; i++) {
50631                         if (data[i].value.substring(0, value.length) === value) {
50632                             sameletter.push(data[i]);
50633                         } else {
50634                             other.push(data[i]);
50635                         }
50636                     }
50637                     return sameletter.concat(other);
50638                 }
50639             }
50640
50641             function unbind() {
50642                 var row = select(this);
50643
50644                 row.selectAll('input.key')
50645                     .call(uiCombobox.off, context);
50646
50647                 row.selectAll('input.value')
50648                     .call(uiCombobox.off, context);
50649             }
50650
50651             function keyChange(d) {
50652                 if (select(this).attr('readonly')) return;
50653
50654                 var kOld = d.key;
50655
50656                 // exit if we are currently about to delete this row anyway - #6366
50657                 if (_pendingChange && _pendingChange.hasOwnProperty(kOld) && _pendingChange[kOld] === undefined) return;
50658
50659                 var kNew = context.cleanTagKey(this.value.trim());
50660
50661                 // allow no change if the key should be readonly
50662                 if (isReadOnly({ key: kNew })) {
50663                     this.value = kOld;
50664                     return;
50665                 }
50666
50667                 if (kNew &&
50668                     kNew !== kOld &&
50669                     _tags[kNew] !== undefined) {
50670                     // new key is already in use, switch focus to the existing row
50671
50672                     this.value = kOld;                // reset the key
50673                     section.selection().selectAll('.tag-list input.value')
50674                         .each(function(d) {
50675                             if (d.key === kNew) {     // send focus to that other value combo instead
50676                                 var input = select(this).node();
50677                                 input.focus();
50678                                 input.select();
50679                             }
50680                         });
50681                     return;
50682                 }
50683
50684
50685                 var row = this.parentNode.parentNode;
50686                 var inputVal = select(row).selectAll('input.value');
50687                 var vNew = context.cleanTagValue(utilGetSetValue(inputVal));
50688
50689                 _pendingChange = _pendingChange || {};
50690
50691                 if (kOld) {
50692                     _pendingChange[kOld] = undefined;
50693                 }
50694
50695                 _pendingChange[kNew] = vNew;
50696
50697                 // update the ordered key index so this row doesn't change position
50698                 var existingKeyIndex = _orderedKeys.indexOf(kOld);
50699                 if (existingKeyIndex !== -1) _orderedKeys[existingKeyIndex] = kNew;
50700
50701                 d.key = kNew;    // update datum to avoid exit/enter on tag update
50702                 d.value = vNew;
50703
50704                 this.value = kNew;
50705                 utilGetSetValue(inputVal, vNew);
50706                 scheduleChange();
50707             }
50708
50709             function valueChange(d) {
50710                 if (isReadOnly(d)) return;
50711
50712                 // exit if this is a multiselection and no value was entered
50713                 if (typeof d.value !== 'string' && !this.value) return;
50714
50715                 // exit if we are currently about to delete this row anyway - #6366
50716                 if (_pendingChange && _pendingChange.hasOwnProperty(d.key) && _pendingChange[d.key] === undefined) return;
50717
50718                 _pendingChange = _pendingChange || {};
50719
50720                 _pendingChange[d.key] = context.cleanTagValue(this.value);
50721                 scheduleChange();
50722             }
50723
50724             function removeTag(d) {
50725                 if (isReadOnly(d)) return;
50726
50727                 if (d.key === '') {    // removing the blank row
50728                     _showBlank = false;
50729                     section.reRender();
50730
50731                 } else {
50732                     // remove the key from the ordered key index
50733                     _orderedKeys = _orderedKeys.filter(function(key) { return key !== d.key; });
50734
50735                     _pendingChange  = _pendingChange || {};
50736                     _pendingChange[d.key] = undefined;
50737                     scheduleChange();
50738                 }
50739             }
50740
50741             function addTag() {
50742                 // Delay render in case this click is blurring an edited combo.
50743                 // Without the setTimeout, the `content` render would wipe out the pending tag change.
50744                 window.setTimeout(function() {
50745                     _showBlank = true;
50746                     section.reRender();
50747                     section.selection().selectAll('.tag-list li:last-child input.key').node().focus();
50748                 }, 20);
50749             }
50750
50751             function scheduleChange() {
50752                 // Cache IDs in case the editor is reloaded before the change event is called. - #6028
50753                 var entityIDs = _entityIDs;
50754
50755                 // Delay change in case this change is blurring an edited combo. - #5878
50756                 window.setTimeout(function() {
50757                     if (!_pendingChange) return;
50758
50759                     dispatch$1.call('change', this, entityIDs, _pendingChange);
50760                     _pendingChange = null;
50761                 }, 10);
50762             }
50763
50764
50765             section.state = function(val) {
50766                 if (!arguments.length) return _state;
50767                 if (_state !== val) {
50768                     _orderedKeys = [];
50769                     _state = val;
50770                 }
50771                 return section;
50772             };
50773
50774
50775             section.presets = function(val) {
50776                 if (!arguments.length) return _presets;
50777                 _presets = val;
50778                 if (_presets && _presets.length && _presets[0].isFallback()) {
50779                     section.disclosureExpanded(true);
50780                 } else {
50781                     section.disclosureExpanded(null);
50782                 }
50783                 return section;
50784             };
50785
50786
50787             section.tags = function(val) {
50788                 if (!arguments.length) return _tags;
50789                 _tags = val;
50790                 return section;
50791             };
50792
50793
50794             section.entityIDs = function(val) {
50795                 if (!arguments.length) return _entityIDs;
50796                 if (!_entityIDs || !val || !utilArrayIdentical(_entityIDs, val)) {
50797                     _entityIDs = val;
50798                     _orderedKeys = [];
50799                 }
50800                 return section;
50801             };
50802
50803
50804             // pass an array of regular expressions to test against the tag key
50805             section.readOnlyTags = function(val) {
50806                 if (!arguments.length) return _readOnlyTags;
50807                 _readOnlyTags = val;
50808                 return section;
50809             };
50810
50811
50812             return utilRebind(section, dispatch$1, 'on');
50813         }
50814
50815         function uiDataEditor(context) {
50816             var dataHeader = uiDataHeader();
50817             var rawTagEditor = uiSectionRawTagEditor('custom-data-tag-editor', context)
50818                 .expandedByDefault(true)
50819                 .readOnlyTags([/./]);
50820             var _datum;
50821
50822
50823             function dataEditor(selection) {
50824
50825                 var header = selection.selectAll('.header')
50826                     .data([0]);
50827
50828                 var headerEnter = header.enter()
50829                     .append('div')
50830                     .attr('class', 'header fillL');
50831
50832                 headerEnter
50833                     .append('button')
50834                     .attr('class', 'close')
50835                     .on('click', function() {
50836                         context.enter(modeBrowse(context));
50837                     })
50838                     .call(svgIcon('#iD-icon-close'));
50839
50840                 headerEnter
50841                     .append('h3')
50842                     .text(_t('map_data.title'));
50843
50844
50845                 var body = selection.selectAll('.body')
50846                     .data([0]);
50847
50848                 body = body.enter()
50849                     .append('div')
50850                     .attr('class', 'body')
50851                     .merge(body);
50852
50853                 var editor = body.selectAll('.data-editor')
50854                     .data([0]);
50855
50856                 // enter/update
50857                 editor.enter()
50858                     .append('div')
50859                     .attr('class', 'modal-section data-editor')
50860                     .merge(editor)
50861                     .call(dataHeader.datum(_datum));
50862
50863                 var rte = body.selectAll('.raw-tag-editor')
50864                     .data([0]);
50865
50866                 // enter/update
50867                 rte.enter()
50868                     .append('div')
50869                     .attr('class', 'raw-tag-editor data-editor')
50870                     .merge(rte)
50871                     .call(rawTagEditor
50872                         .tags((_datum && _datum.properties) || {})
50873                         .state('hover')
50874                         .render
50875                     )
50876                     .selectAll('textarea.tag-text')
50877                     .attr('readonly', true)
50878                     .classed('readonly', true);
50879             }
50880
50881
50882             dataEditor.datum = function(val) {
50883                 if (!arguments.length) return _datum;
50884                 _datum = val;
50885                 return this;
50886             };
50887
50888
50889             return dataEditor;
50890         }
50891
50892         function modeSelectData(context, selectedDatum) {
50893             var mode = {
50894                 id: 'select-data',
50895                 button: 'browse'
50896             };
50897
50898             var keybinding = utilKeybinding('select-data');
50899             var dataEditor = uiDataEditor(context);
50900
50901             var behaviors = [
50902                 behaviorBreathe(),
50903                 behaviorHover(context),
50904                 behaviorSelect(context),
50905                 behaviorLasso(context),
50906                 modeDragNode(context).behavior,
50907                 modeDragNote(context).behavior
50908             ];
50909
50910
50911             // class the data as selected, or return to browse mode if the data is gone
50912             function selectData(drawn) {
50913                 var selection = context.surface().selectAll('.layer-mapdata .data' + selectedDatum.__featurehash__);
50914
50915                 if (selection.empty()) {
50916                     // Return to browse mode if selected DOM elements have
50917                     // disappeared because the user moved them out of view..
50918                     var source = event && event.type === 'zoom' && event.sourceEvent;
50919                     if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) {
50920                         context.enter(modeBrowse(context));
50921                     }
50922                 } else {
50923                     selection.classed('selected', true);
50924                 }
50925             }
50926
50927
50928             function esc() {
50929                 if (context.container().select('.combobox').size()) return;
50930                 context.enter(modeBrowse(context));
50931             }
50932
50933
50934             mode.zoomToSelected = function() {
50935                 var extent = geoExtent(d3_geoBounds(selectedDatum));
50936                 context.map().centerZoomEase(extent.center(), context.map().trimmedExtentZoom(extent));
50937             };
50938
50939
50940             mode.enter = function() {
50941                 behaviors.forEach(context.install);
50942
50943                 keybinding
50944                     .on(_t('inspector.zoom_to.key'), mode.zoomToSelected)
50945                     .on('⎋', esc, true);
50946
50947                 select(document)
50948                     .call(keybinding);
50949
50950                 selectData();
50951
50952                 var sidebar = context.ui().sidebar;
50953                 sidebar.show(dataEditor.datum(selectedDatum));
50954
50955                 // expand the sidebar, avoid obscuring the data if needed
50956                 var extent = geoExtent(d3_geoBounds(selectedDatum));
50957                 sidebar.expand(sidebar.intersects(extent));
50958
50959                 context.map()
50960                     .on('drawn.select-data', selectData);
50961             };
50962
50963
50964             mode.exit = function() {
50965                 behaviors.forEach(context.uninstall);
50966
50967                 select(document)
50968                     .call(keybinding.unbind);
50969
50970                 context.surface()
50971                     .selectAll('.layer-mapdata .selected')
50972                     .classed('selected hover', false);
50973
50974                 context.map()
50975                     .on('drawn.select-data', null);
50976
50977                 context.ui().sidebar
50978                     .hide();
50979             };
50980
50981
50982             return mode;
50983         }
50984
50985         function uiImproveOsmComments() {
50986           let _qaItem;
50987
50988           function issueComments(selection) {
50989             // make the div immediately so it appears above the buttons
50990             let comments = selection.selectAll('.comments-container')
50991               .data([0]);
50992
50993             comments = comments.enter()
50994               .append('div')
50995                 .attr('class', 'comments-container')
50996               .merge(comments);
50997
50998             // must retrieve comments from API before they can be displayed
50999             services.improveOSM.getComments(_qaItem)
51000               .then(d => {
51001                 if (!d.comments) return; // nothing to do here
51002
51003                 const commentEnter = comments.selectAll('.comment')
51004                   .data(d.comments)
51005                   .enter()
51006                   .append('div')
51007                     .attr('class', 'comment');
51008
51009                 commentEnter
51010                   .append('div')
51011                     .attr('class', 'comment-avatar')
51012                     .call(svgIcon('#iD-icon-avatar', 'comment-avatar-icon'));
51013
51014                 const mainEnter = commentEnter
51015                   .append('div')
51016                   .attr('class', 'comment-main');
51017
51018                 const metadataEnter = mainEnter
51019                   .append('div')
51020                     .attr('class', 'comment-metadata');
51021
51022                 metadataEnter
51023                   .append('div')
51024                     .attr('class', 'comment-author')
51025                     .each(function(d) {
51026                       const osm = services.osm;
51027                       let selection = select(this);
51028                       if (osm && d.username) {
51029                         selection = selection
51030                           .append('a')
51031                           .attr('class', 'comment-author-link')
51032                           .attr('href', osm.userURL(d.username))
51033                           .attr('tabindex', -1)
51034                           .attr('target', '_blank');
51035                       }
51036                       selection
51037                         .text(d => d.username);
51038                     });
51039
51040                 metadataEnter
51041                   .append('div')
51042                     .attr('class', 'comment-date')
51043                     .text(d => _t('note.status.commented', { when: localeDateString(d.timestamp) }));
51044
51045                 mainEnter
51046                   .append('div')
51047                     .attr('class', 'comment-text')
51048                   .append('p')
51049                     .text(d => d.text);
51050             })
51051             .catch(err => {
51052               console.log(err); // eslint-disable-line no-console
51053             });
51054           }
51055
51056           function localeDateString(s) {
51057             if (!s) return null;
51058             const options = { day: 'numeric', month: 'short', year: 'numeric' };
51059             const d = new Date(s * 1000); // timestamp is served in seconds, date takes ms
51060             if (isNaN(d.getTime())) return null;
51061             return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
51062           }
51063
51064           issueComments.issue = function(val) {
51065             if (!arguments.length) return _qaItem;
51066             _qaItem = val;
51067             return issueComments;
51068           };
51069
51070           return issueComments;
51071         }
51072
51073         function uiImproveOsmDetails(context) {
51074           let _qaItem;
51075
51076
51077           function issueDetail(d) {
51078             if (d.desc) return d.desc;
51079             const issueKey = d.issueKey;
51080             d.replacements = d.replacements || {};
51081             d.replacements.default = _t('inspector.unknown');  // special key `default` works as a fallback string
51082             return _t(`QA.improveOSM.error_types.${issueKey}.description`, d.replacements);
51083           }
51084
51085
51086           function improveOsmDetails(selection) {
51087             const details = selection.selectAll('.error-details')
51088               .data(
51089                 (_qaItem ? [_qaItem] : []),
51090                 d => `${d.id}-${d.status || 0}`
51091               );
51092
51093             details.exit()
51094               .remove();
51095
51096             const detailsEnter = details.enter()
51097               .append('div')
51098                 .attr('class', 'error-details qa-details-container');
51099
51100
51101             // description
51102             const descriptionEnter = detailsEnter
51103               .append('div')
51104                 .attr('class', 'qa-details-subsection');
51105
51106             descriptionEnter
51107               .append('h4')
51108                 .text(() => _t('QA.keepRight.detail_description'));
51109
51110             descriptionEnter
51111               .append('div')
51112                 .attr('class', 'qa-details-description-text')
51113                 .html(issueDetail);
51114
51115             // If there are entity links in the error message..
51116             let relatedEntities = [];
51117             descriptionEnter.selectAll('.error_entity_link, .error_object_link')
51118               .each(function() {
51119                 const link = select(this);
51120                 const isObjectLink = link.classed('error_object_link');
51121                 const entityID = isObjectLink ?
51122                   (utilEntityRoot(_qaItem.objectType) + _qaItem.objectId)
51123                   : this.textContent;
51124                 const entity = context.hasEntity(entityID);
51125
51126                 relatedEntities.push(entityID);
51127
51128                 // Add click handler
51129                 link
51130                   .on('mouseenter', () => {
51131                     utilHighlightEntities([entityID], true, context);
51132                   })
51133                   .on('mouseleave', () => {
51134                     utilHighlightEntities([entityID], false, context);
51135                   })
51136                   .on('click', () => {
51137                     event.preventDefault();
51138
51139                     utilHighlightEntities([entityID], false, context);
51140
51141                     const osmlayer = context.layers().layer('osm');
51142                     if (!osmlayer.enabled()) {
51143                       osmlayer.enabled(true);
51144                     }
51145
51146                     context.map().centerZoom(_qaItem.loc, 20);
51147
51148                     if (entity) {
51149                       context.enter(modeSelect(context, [entityID]));
51150                     } else {
51151                       context.loadEntity(entityID, () => {
51152                         context.enter(modeSelect(context, [entityID]));
51153                       });
51154                     }
51155                   });
51156
51157                 // Replace with friendly name if possible
51158                 // (The entity may not yet be loaded into the graph)
51159                 if (entity) {
51160                   let name = utilDisplayName(entity);  // try to use common name
51161
51162                   if (!name && !isObjectLink) {
51163                     const preset = _mainPresetIndex.match(entity, context.graph());
51164                     name = preset && !preset.isFallback() && preset.name();  // fallback to preset name
51165                   }
51166
51167                   if (name) {
51168                     this.innerText = name;
51169                   }
51170                 }
51171               });
51172
51173             // Don't hide entities related to this error - #5880
51174             context.features().forceVisible(relatedEntities);
51175             context.map().pan([0,0]);  // trigger a redraw
51176           }
51177
51178           improveOsmDetails.issue = function(val) {
51179             if (!arguments.length) return _qaItem;
51180             _qaItem = val;
51181             return improveOsmDetails;
51182           };
51183
51184           return improveOsmDetails;
51185         }
51186
51187         function uiImproveOsmHeader() {
51188           let _qaItem;
51189
51190
51191           function issueTitle(d) {
51192             const issueKey = d.issueKey;
51193             d.replacements = d.replacements || {};
51194             d.replacements.default = _t('inspector.unknown');  // special key `default` works as a fallback string
51195             return _t(`QA.improveOSM.error_types.${issueKey}.title`, d.replacements);
51196           }
51197
51198
51199           function improveOsmHeader(selection) {
51200             const header = selection.selectAll('.qa-header')
51201               .data(
51202                 (_qaItem ? [_qaItem] : []),
51203                 d => `${d.id}-${d.status || 0}`
51204               );
51205
51206             header.exit()
51207               .remove();
51208
51209             const headerEnter = header.enter()
51210               .append('div')
51211                 .attr('class', 'qa-header');
51212
51213             const svgEnter = headerEnter
51214               .append('div')
51215                 .attr('class', 'qa-header-icon')
51216                 .classed('new', d => d.id < 0)
51217               .append('svg')
51218                 .attr('width', '20px')
51219                 .attr('height', '30px')
51220                 .attr('viewbox', '0 0 20 30')
51221                 .attr('class', d => `preset-icon-28 qaItem ${d.service} itemId-${d.id} itemType-${d.itemType}`);
51222
51223             svgEnter
51224               .append('polygon')
51225                 .attr('fill', 'currentColor')
51226                 .attr('class', 'qaItem-fill')
51227                 .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');
51228
51229             svgEnter
51230               .append('use')
51231                 .attr('class', 'icon-annotation')
51232                 .attr('width', '13px')
51233                 .attr('height', '13px')
51234                 .attr('transform', 'translate(3.5, 5)')
51235                 .attr('xlink:href', d => {
51236                   const picon = d.icon;
51237                   if (!picon) {
51238                     return '';
51239                   } else {
51240                     const isMaki = /^maki-/.test(picon);
51241                     return `#${picon}${isMaki ? '-11' : ''}`;
51242                   }
51243                 });
51244
51245             headerEnter
51246               .append('div')
51247                 .attr('class', 'qa-header-label')
51248                 .text(issueTitle);
51249           }
51250
51251           improveOsmHeader.issue = function(val) {
51252             if (!arguments.length) return _qaItem;
51253             _qaItem = val;
51254             return improveOsmHeader;
51255           };
51256
51257           return improveOsmHeader;
51258         }
51259
51260         function uiImproveOsmEditor(context) {
51261           const dispatch$1 = dispatch('change');
51262           const qaDetails = uiImproveOsmDetails(context);
51263           const qaComments = uiImproveOsmComments();
51264           const qaHeader = uiImproveOsmHeader();
51265
51266           let _qaItem;
51267
51268           function improveOsmEditor(selection) {
51269
51270             const headerEnter = selection.selectAll('.header')
51271               .data([0])
51272               .enter()
51273               .append('div')
51274                 .attr('class', 'header fillL');
51275
51276             headerEnter
51277               .append('button')
51278                 .attr('class', 'close')
51279                 .on('click', () => context.enter(modeBrowse(context)))
51280                 .call(svgIcon('#iD-icon-close'));
51281
51282             headerEnter
51283               .append('h3')
51284                 .text(_t('QA.improveOSM.title'));
51285
51286             let body = selection.selectAll('.body')
51287               .data([0]);
51288
51289             body = body.enter()
51290               .append('div')
51291                 .attr('class', 'body')
51292               .merge(body);
51293
51294             const editor = body.selectAll('.qa-editor')
51295               .data([0]);
51296
51297             editor.enter()
51298               .append('div')
51299                 .attr('class', 'modal-section qa-editor')
51300               .merge(editor)
51301                 .call(qaHeader.issue(_qaItem))
51302                 .call(qaDetails.issue(_qaItem))
51303                 .call(qaComments.issue(_qaItem))
51304                 .call(improveOsmSaveSection);
51305           }
51306
51307           function improveOsmSaveSection(selection) {
51308             const isSelected = (_qaItem && _qaItem.id === context.selectedErrorID());
51309             const isShown = (_qaItem && (isSelected || _qaItem.newComment || _qaItem.comment));
51310             let saveSection = selection.selectAll('.qa-save')
51311               .data(
51312                 (isShown ? [_qaItem] : []),
51313                 d => `${d.id}-${d.status || 0}`
51314               );
51315
51316             // exit
51317             saveSection.exit()
51318               .remove();
51319
51320             // enter
51321             const saveSectionEnter = saveSection.enter()
51322               .append('div')
51323                 .attr('class', 'qa-save save-section cf');
51324
51325             saveSectionEnter
51326               .append('h4')
51327                 .attr('class', '.qa-save-header')
51328                 .text(_t('note.newComment'));
51329
51330             saveSectionEnter
51331               .append('textarea')
51332                 .attr('class', 'new-comment-input')
51333                 .attr('placeholder', _t('QA.keepRight.comment_placeholder'))
51334                 .attr('maxlength', 1000)
51335                 .property('value', d => d.newComment)
51336                 .call(utilNoAuto)
51337                 .on('input', changeInput)
51338                 .on('blur', changeInput);
51339
51340             // update
51341             saveSection = saveSectionEnter
51342               .merge(saveSection)
51343                 .call(qaSaveButtons);
51344
51345             function changeInput() {
51346               const input = select(this);
51347               let val = input.property('value').trim();
51348
51349               if (val === '') {
51350                 val = undefined;
51351               }
51352
51353               // store the unsaved comment with the issue itself
51354               _qaItem = _qaItem.update({ newComment: val });
51355
51356               const qaService = services.improveOSM;
51357               if (qaService) {
51358                 qaService.replaceItem(_qaItem);
51359               }
51360
51361               saveSection
51362                 .call(qaSaveButtons);
51363             }
51364           }
51365
51366           function qaSaveButtons(selection) {
51367             const isSelected = (_qaItem && _qaItem.id === context.selectedErrorID());
51368             let buttonSection = selection.selectAll('.buttons')
51369               .data((isSelected ? [_qaItem] : []), d => d.status + d.id);
51370
51371             // exit
51372             buttonSection.exit()
51373               .remove();
51374
51375             // enter
51376             const buttonEnter = buttonSection.enter()
51377               .append('div')
51378                 .attr('class', 'buttons');
51379
51380             buttonEnter
51381               .append('button')
51382                 .attr('class', 'button comment-button action')
51383                 .text(_t('QA.keepRight.save_comment'));
51384
51385             buttonEnter
51386               .append('button')
51387                 .attr('class', 'button close-button action');
51388
51389             buttonEnter
51390               .append('button')
51391                 .attr('class', 'button ignore-button action');
51392
51393             // update
51394             buttonSection = buttonSection
51395               .merge(buttonEnter);
51396
51397             buttonSection.select('.comment-button')
51398               .attr('disabled', d => d.newComment ? null : true)
51399               .on('click.comment', function(d) {
51400                 this.blur();    // avoid keeping focus on the button - #4641
51401                 const qaService = services.improveOSM;
51402                 if (qaService) {
51403                   qaService.postUpdate(d, (err, item) => dispatch$1.call('change', item));
51404                 }
51405               });
51406
51407             buttonSection.select('.close-button')
51408               .text(d => {
51409                 const andComment = (d.newComment ? '_comment' : '');
51410                 return _t(`QA.keepRight.close${andComment}`);
51411               })
51412               .on('click.close', function(d) {
51413                 this.blur();    // avoid keeping focus on the button - #4641
51414                 const qaService = services.improveOSM;
51415                 if (qaService) {
51416                   d.newStatus = 'SOLVED';
51417                   qaService.postUpdate(d, (err, item) => dispatch$1.call('change', item));
51418                 }
51419               });
51420
51421             buttonSection.select('.ignore-button')
51422               .text(d => {
51423                 const andComment = (d.newComment ? '_comment' : '');
51424                 return _t(`QA.keepRight.ignore${andComment}`);
51425               })
51426               .on('click.ignore', function(d) {
51427                 this.blur();    // avoid keeping focus on the button - #4641
51428                 const qaService = services.improveOSM;
51429                 if (qaService) {
51430                   d.newStatus = 'INVALID';
51431                   qaService.postUpdate(d, (err, item) => dispatch$1.call('change', item));
51432                 }
51433               });
51434           }
51435
51436           // NOTE: Don't change method name until UI v3 is merged
51437           improveOsmEditor.error = function(val) {
51438             if (!arguments.length) return _qaItem;
51439             _qaItem = val;
51440             return improveOsmEditor;
51441           };
51442
51443           return utilRebind(improveOsmEditor, dispatch$1, 'on');
51444         }
51445
51446         function uiKeepRightDetails(context) {
51447           let _qaItem;
51448
51449
51450           function issueDetail(d) {
51451             const { itemType, parentIssueType } = d;
51452             const unknown = _t('inspector.unknown');
51453             let replacements = d.replacements || {};
51454             replacements.default = unknown;  // special key `default` works as a fallback string
51455
51456             let detail = _t(`QA.keepRight.errorTypes.${itemType}.description`, replacements);
51457             if (detail === unknown) {
51458               detail = _t(`QA.keepRight.errorTypes.${parentIssueType}.description`, replacements);
51459             }
51460             return detail;
51461           }
51462
51463
51464           function keepRightDetails(selection) {
51465             const details = selection.selectAll('.error-details')
51466               .data(
51467                 (_qaItem ? [_qaItem] : []),
51468                 d => `${d.id}-${d.status || 0}`
51469               );
51470
51471             details.exit()
51472               .remove();
51473
51474             const detailsEnter = details.enter()
51475               .append('div')
51476                 .attr('class', 'error-details qa-details-container');
51477
51478             // description
51479             const descriptionEnter = detailsEnter
51480               .append('div')
51481                 .attr('class', 'qa-details-subsection');
51482
51483             descriptionEnter
51484               .append('h4')
51485                 .text(() => _t('QA.keepRight.detail_description'));
51486
51487             descriptionEnter
51488               .append('div')
51489                 .attr('class', 'qa-details-description-text')
51490                 .html(issueDetail);
51491
51492             // If there are entity links in the error message..
51493             let relatedEntities = [];
51494             descriptionEnter.selectAll('.error_entity_link, .error_object_link')
51495               .each(function() {
51496                 const link = select(this);
51497                 const isObjectLink = link.classed('error_object_link');
51498                 const entityID = isObjectLink ?
51499                   (utilEntityRoot(_qaItem.objectType) + _qaItem.objectId)
51500                   : this.textContent;
51501                 const entity = context.hasEntity(entityID);
51502
51503                 relatedEntities.push(entityID);
51504
51505                 // Add click handler
51506                 link
51507                   .on('mouseenter', () => {
51508                     utilHighlightEntities([entityID], true, context);
51509                   })
51510                   .on('mouseleave', () => {
51511                     utilHighlightEntities([entityID], false, context);
51512                   })
51513                   .on('click', () => {
51514                     event.preventDefault();
51515
51516                     utilHighlightEntities([entityID], false, context);
51517
51518                     const osmlayer = context.layers().layer('osm');
51519                     if (!osmlayer.enabled()) {
51520                       osmlayer.enabled(true);
51521                     }
51522
51523                     context.map().centerZoomEase(_qaItem.loc, 20);
51524
51525                     if (entity) {
51526                       context.enter(modeSelect(context, [entityID]));
51527                     } else {
51528                       context.loadEntity(entityID, () => {
51529                         context.enter(modeSelect(context, [entityID]));
51530                       });
51531                     }
51532                   });
51533
51534                 // Replace with friendly name if possible
51535                 // (The entity may not yet be loaded into the graph)
51536                 if (entity) {
51537                   let name = utilDisplayName(entity);  // try to use common name
51538
51539                   if (!name && !isObjectLink) {
51540                     const preset = _mainPresetIndex.match(entity, context.graph());
51541                     name = preset && !preset.isFallback() && preset.name();  // fallback to preset name
51542                   }
51543
51544                   if (name) {
51545                     this.innerText = name;
51546                   }
51547                 }
51548               });
51549
51550             // Don't hide entities related to this issue - #5880
51551             context.features().forceVisible(relatedEntities);
51552             context.map().pan([0,0]);  // trigger a redraw
51553           }
51554
51555           keepRightDetails.issue = function(val) {
51556             if (!arguments.length) return _qaItem;
51557             _qaItem = val;
51558             return keepRightDetails;
51559           };
51560
51561           return keepRightDetails;
51562         }
51563
51564         function uiKeepRightHeader() {
51565           let _qaItem;
51566
51567
51568           function issueTitle(d) {
51569             const { itemType, parentIssueType } = d;
51570             const unknown = _t('inspector.unknown');
51571             let replacements = d.replacements || {};
51572             replacements.default = unknown;  // special key `default` works as a fallback string
51573
51574             let title = _t(`QA.keepRight.errorTypes.${itemType}.title`, replacements);
51575             if (title === unknown) {
51576               title = _t(`QA.keepRight.errorTypes.${parentIssueType}.title`, replacements);
51577             }
51578             return title;
51579           }
51580
51581
51582           function keepRightHeader(selection) {
51583             const header = selection.selectAll('.qa-header')
51584               .data(
51585                 (_qaItem ? [_qaItem] : []),
51586                 d => `${d.id}-${d.status || 0}`
51587               );
51588
51589             header.exit()
51590               .remove();
51591
51592             const headerEnter = header.enter()
51593               .append('div')
51594                 .attr('class', 'qa-header');
51595
51596             const iconEnter = headerEnter
51597               .append('div')
51598                 .attr('class', 'qa-header-icon')
51599                 .classed('new', d => d.id < 0);
51600
51601             iconEnter
51602               .append('div')
51603                 .attr('class', d => `preset-icon-28 qaItem ${d.service} itemId-${d.id} itemType-${d.parentIssueType}`)
51604                 .call(svgIcon('#iD-icon-bolt', 'qaItem-fill'));
51605
51606             headerEnter
51607               .append('div')
51608                 .attr('class', 'qa-header-label')
51609                 .text(issueTitle);
51610           }
51611
51612
51613           keepRightHeader.issue = function(val) {
51614             if (!arguments.length) return _qaItem;
51615             _qaItem = val;
51616             return keepRightHeader;
51617           };
51618
51619           return keepRightHeader;
51620         }
51621
51622         function uiViewOnKeepRight() {
51623           let _qaItem;
51624
51625           function viewOnKeepRight(selection) {
51626             let url;
51627             if (services.keepRight && (_qaItem instanceof QAItem)) {
51628               url = services.keepRight.issueURL(_qaItem);
51629             }
51630
51631             const link = selection.selectAll('.view-on-keepRight')
51632               .data(url ? [url] : []);
51633
51634             // exit
51635             link.exit()
51636               .remove();
51637
51638             // enter
51639             const linkEnter = link.enter()
51640               .append('a')
51641                 .attr('class', 'view-on-keepRight')
51642                 .attr('target', '_blank')
51643                 .attr('rel', 'noopener') // security measure
51644                 .attr('href', d => d)
51645                 .call(svgIcon('#iD-icon-out-link', 'inline'));
51646
51647             linkEnter
51648               .append('span')
51649                 .text(_t('inspector.view_on_keepRight'));
51650           }
51651
51652           viewOnKeepRight.what = function(val) {
51653             if (!arguments.length) return _qaItem;
51654             _qaItem = val;
51655             return viewOnKeepRight;
51656           };
51657
51658           return viewOnKeepRight;
51659         }
51660
51661         function uiKeepRightEditor(context) {
51662           const dispatch$1 = dispatch('change');
51663           const qaDetails = uiKeepRightDetails(context);
51664           const qaHeader = uiKeepRightHeader();
51665
51666           let _qaItem;
51667
51668           function keepRightEditor(selection) {
51669
51670             const headerEnter = selection.selectAll('.header')
51671               .data([0])
51672               .enter()
51673               .append('div')
51674                 .attr('class', 'header fillL');
51675
51676             headerEnter
51677               .append('button')
51678                 .attr('class', 'close')
51679                 .on('click', () => context.enter(modeBrowse(context)))
51680                 .call(svgIcon('#iD-icon-close'));
51681
51682             headerEnter
51683               .append('h3')
51684                 .text(_t('QA.keepRight.title'));
51685
51686
51687             let body = selection.selectAll('.body')
51688               .data([0]);
51689
51690             body = body.enter()
51691               .append('div')
51692                 .attr('class', 'body')
51693               .merge(body);
51694
51695             const editor = body.selectAll('.qa-editor')
51696               .data([0]);
51697
51698             editor.enter()
51699               .append('div')
51700                 .attr('class', 'modal-section qa-editor')
51701               .merge(editor)
51702                 .call(qaHeader.issue(_qaItem))
51703                 .call(qaDetails.issue(_qaItem))
51704                 .call(keepRightSaveSection);
51705
51706
51707             const footer = selection.selectAll('.footer')
51708               .data([0]);
51709
51710             footer.enter()
51711               .append('div')
51712               .attr('class', 'footer')
51713               .merge(footer)
51714               .call(uiViewOnKeepRight().what(_qaItem));
51715           }
51716
51717
51718           function keepRightSaveSection(selection) {
51719             const isSelected = (_qaItem && _qaItem.id === context.selectedErrorID());
51720             const isShown = (_qaItem && (isSelected || _qaItem.newComment || _qaItem.comment));
51721             let saveSection = selection.selectAll('.qa-save')
51722               .data(
51723                 (isShown ? [_qaItem] : []),
51724                 d => `${d.id}-${d.status || 0}`
51725               );
51726
51727             // exit
51728             saveSection.exit()
51729               .remove();
51730
51731             // enter
51732             const saveSectionEnter = saveSection.enter()
51733               .append('div')
51734                 .attr('class', 'qa-save save-section cf');
51735
51736             saveSectionEnter
51737               .append('h4')
51738                 .attr('class', '.qa-save-header')
51739                 .text(_t('QA.keepRight.comment'));
51740
51741             saveSectionEnter
51742               .append('textarea')
51743                 .attr('class', 'new-comment-input')
51744                 .attr('placeholder', _t('QA.keepRight.comment_placeholder'))
51745                 .attr('maxlength', 1000)
51746                 .property('value', d => d.newComment || d.comment)
51747                 .call(utilNoAuto)
51748                 .on('input', changeInput)
51749                 .on('blur', changeInput);
51750
51751             // update
51752             saveSection = saveSectionEnter
51753               .merge(saveSection)
51754                 .call(qaSaveButtons);
51755
51756             function changeInput() {
51757               const input = select(this);
51758               let val = input.property('value').trim();
51759
51760               if (val === _qaItem.comment) {
51761                 val = undefined;
51762               }
51763
51764               // store the unsaved comment with the issue itself
51765               _qaItem = _qaItem.update({ newComment: val });
51766
51767               const qaService = services.keepRight;
51768               if (qaService) {
51769                 qaService.replaceItem(_qaItem);  // update keepright cache
51770               }
51771
51772               saveSection
51773                 .call(qaSaveButtons);
51774             }
51775           }
51776
51777
51778           function qaSaveButtons(selection) {
51779             const isSelected = (_qaItem && _qaItem.id === context.selectedErrorID());
51780             let buttonSection = selection.selectAll('.buttons')
51781               .data((isSelected ? [_qaItem] : []), d => d.status + d.id);
51782
51783             // exit
51784             buttonSection.exit()
51785               .remove();
51786
51787             // enter
51788             const buttonEnter = buttonSection.enter()
51789               .append('div')
51790                 .attr('class', 'buttons');
51791
51792             buttonEnter
51793               .append('button')
51794                 .attr('class', 'button comment-button action')
51795                 .text(_t('QA.keepRight.save_comment'));
51796
51797             buttonEnter
51798               .append('button')
51799                 .attr('class', 'button close-button action');
51800
51801             buttonEnter
51802               .append('button')
51803                 .attr('class', 'button ignore-button action');
51804
51805             // update
51806             buttonSection = buttonSection
51807               .merge(buttonEnter);
51808
51809             buttonSection.select('.comment-button')   // select and propagate data
51810               .attr('disabled', d => d.newComment ? null : true)
51811               .on('click.comment', function(d) {
51812                 this.blur();    // avoid keeping focus on the button - #4641
51813                 const qaService = services.keepRight;
51814                 if (qaService) {
51815                   qaService.postUpdate(d, (err, item) => dispatch$1.call('change', item));
51816                 }
51817               });
51818
51819             buttonSection.select('.close-button')   // select and propagate data
51820               .text(d => {
51821                 const andComment = (d.newComment ? '_comment' : '');
51822                 return _t(`QA.keepRight.close${andComment}`);
51823               })
51824               .on('click.close', function(d) {
51825                 this.blur();    // avoid keeping focus on the button - #4641
51826                 const qaService = services.keepRight;
51827                 if (qaService) {
51828                   d.newStatus = 'ignore_t';   // ignore temporarily (item fixed)
51829                   qaService.postUpdate(d, (err, item) => dispatch$1.call('change', item));
51830                 }
51831               });
51832
51833             buttonSection.select('.ignore-button')   // select and propagate data
51834               .text(d => {
51835                 const andComment = (d.newComment ? '_comment' : '');
51836                 return _t(`QA.keepRight.ignore${andComment}`);
51837               })
51838               .on('click.ignore', function(d) {
51839                 this.blur();    // avoid keeping focus on the button - #4641
51840                 const qaService = services.keepRight;
51841                 if (qaService) {
51842                   d.newStatus = 'ignore';   // ignore permanently (false positive)
51843                   qaService.postUpdate(d, (err, item) => dispatch$1.call('change', item));
51844                 }
51845               });
51846           }
51847
51848           // NOTE: Don't change method name until UI v3 is merged
51849           keepRightEditor.error = function(val) {
51850             if (!arguments.length) return _qaItem;
51851             _qaItem = val;
51852             return keepRightEditor;
51853           };
51854
51855           return utilRebind(keepRightEditor, dispatch$1, 'on');
51856         }
51857
51858         function uiOsmoseDetails(context) {
51859           let _qaItem;
51860
51861           function issueString(d, type) {
51862             if (!d) return '';
51863
51864             // Issue strings are cached from Osmose API
51865             const s = services.osmose.getStrings(d.itemType);
51866             return (type in s) ? s[type] : '';
51867           }
51868
51869
51870           function osmoseDetails(selection) {
51871             const details = selection.selectAll('.error-details')
51872               .data(
51873                 _qaItem ? [_qaItem] : [],
51874                 d => `${d.id}-${d.status || 0}`
51875               );
51876
51877             details.exit()
51878               .remove();
51879
51880             const detailsEnter = details.enter()
51881               .append('div')
51882                 .attr('class', 'error-details qa-details-container');
51883
51884
51885             // Description
51886             if (issueString(_qaItem, 'detail')) {
51887               const div = detailsEnter
51888                 .append('div')
51889                   .attr('class', 'qa-details-subsection');
51890
51891               div
51892                 .append('h4')
51893                   .text(() => _t('QA.keepRight.detail_description'));
51894
51895               div
51896                 .append('p')
51897                   .attr('class', 'qa-details-description-text')
51898                   .html(d => issueString(d, 'detail'))
51899                 .selectAll('a')
51900                   .attr('rel', 'noopener')
51901                   .attr('target', '_blank');
51902             }
51903
51904             // Elements (populated later as data is requested)
51905             const detailsDiv = detailsEnter
51906               .append('div')
51907                 .attr('class', 'qa-details-subsection');
51908
51909             const elemsDiv = detailsEnter
51910               .append('div')
51911                 .attr('class', 'qa-details-subsection');
51912
51913             // Suggested Fix (musn't exist for every issue type)
51914             if (issueString(_qaItem, 'fix')) {
51915               const div = detailsEnter
51916                 .append('div')
51917                   .attr('class', 'qa-details-subsection');
51918
51919               div
51920                 .append('h4')
51921                   .text(() => _t('QA.osmose.fix_title'));
51922
51923               div
51924                 .append('p')
51925                   .html(d => issueString(d, 'fix'))
51926                 .selectAll('a')
51927                   .attr('rel', 'noopener')
51928                   .attr('target', '_blank');
51929             }
51930
51931             // Common Pitfalls (musn't exist for every issue type)
51932             if (issueString(_qaItem, 'trap')) {
51933               const div = detailsEnter
51934                 .append('div')
51935                   .attr('class', 'qa-details-subsection');
51936
51937               div
51938                 .append('h4')
51939                   .text(() => _t('QA.osmose.trap_title'));
51940
51941               div
51942                 .append('p')
51943                   .html(d => issueString(d, 'trap'))
51944                 .selectAll('a')
51945                   .attr('rel', 'noopener')
51946                   .attr('target', '_blank');
51947             }
51948
51949             // Save current item to check if UI changed by time request resolves
51950             const thisItem = _qaItem;
51951             services.osmose.loadIssueDetail(_qaItem)
51952               .then(d => {
51953                 // No details to add if there are no associated issue elements
51954                 if (!d.elems || d.elems.length === 0) return;
51955
51956                 // Do nothing if UI has moved on by the time this resolves
51957                 if (
51958                   context.selectedErrorID() !== thisItem.id
51959                   && context.container().selectAll(`.qaItem.osmose.hover.itemId-${thisItem.id}`).empty()
51960                 ) return;
51961
51962                 // Things like keys and values are dynamically added to a subtitle string
51963                 if (d.detail) {
51964                   detailsDiv
51965                     .append('h4')
51966                       .text(() => _t('QA.osmose.detail_title'));
51967
51968                   detailsDiv
51969                     .append('p')
51970                       .html(d => d.detail)
51971                     .selectAll('a')
51972                       .attr('rel', 'noopener')
51973                       .attr('target', '_blank');
51974                 }
51975
51976                 // Create list of linked issue elements
51977                 elemsDiv
51978                   .append('h4')
51979                     .text(() => _t('QA.osmose.elems_title'));
51980
51981                 elemsDiv
51982                   .append('ul').selectAll('li')
51983                   .data(d.elems)
51984                   .enter()
51985                   .append('li')
51986                   .append('a')
51987                     .attr('class', 'error_entity_link')
51988                     .text(d => d)
51989                     .each(function() {
51990                       const link = select(this);
51991                       const entityID = this.textContent;
51992                       const entity = context.hasEntity(entityID);
51993
51994                       // Add click handler
51995                       link
51996                         .on('mouseenter', () => {
51997                           utilHighlightEntities([entityID], true, context);
51998                         })
51999                         .on('mouseleave', () => {
52000                           utilHighlightEntities([entityID], false, context);
52001                         })
52002                         .on('click', () => {
52003                           event.preventDefault();
52004
52005                           utilHighlightEntities([entityID], false, context);
52006
52007                           const osmlayer = context.layers().layer('osm');
52008                           if (!osmlayer.enabled()) {
52009                             osmlayer.enabled(true);
52010                           }
52011
52012                           context.map().centerZoom(d.loc, 20);
52013
52014                           if (entity) {
52015                             context.enter(modeSelect(context, [entityID]));
52016                           } else {
52017                             context.loadEntity(entityID, () => {
52018                               context.enter(modeSelect(context, [entityID]));
52019                             });
52020                           }
52021                         });
52022
52023                       // Replace with friendly name if possible
52024                       // (The entity may not yet be loaded into the graph)
52025                       if (entity) {
52026                         let name = utilDisplayName(entity);  // try to use common name
52027
52028                         if (!name) {
52029                           const preset = _mainPresetIndex.match(entity, context.graph());
52030                           name = preset && !preset.isFallback() && preset.name();  // fallback to preset name
52031                         }
52032
52033                         if (name) {
52034                           this.innerText = name;
52035                         }
52036                       }
52037                     });
52038
52039                 // Don't hide entities related to this issue - #5880
52040                 context.features().forceVisible(d.elems);
52041                 context.map().pan([0,0]);  // trigger a redraw
52042               })
52043               .catch(err => {
52044                 console.log(err); // eslint-disable-line no-console
52045               });
52046           }
52047
52048
52049           osmoseDetails.issue = function(val) {
52050             if (!arguments.length) return _qaItem;
52051             _qaItem = val;
52052             return osmoseDetails;
52053           };
52054
52055
52056           return osmoseDetails;
52057         }
52058
52059         function uiOsmoseHeader() {
52060           let _qaItem;
52061
52062           function issueTitle(d) {
52063             const unknown = _t('inspector.unknown');
52064
52065             if (!d) return unknown;
52066
52067             // Issue titles supplied by Osmose
52068             const s = services.osmose.getStrings(d.itemType);
52069             return ('title' in s) ? s.title : unknown;
52070           }
52071
52072           function osmoseHeader(selection) {
52073             const header = selection.selectAll('.qa-header')
52074               .data(
52075                 (_qaItem ? [_qaItem] : []),
52076                 d => `${d.id}-${d.status || 0}`
52077               );
52078
52079             header.exit()
52080               .remove();
52081
52082             const headerEnter = header.enter()
52083               .append('div')
52084                 .attr('class', 'qa-header');
52085
52086             const svgEnter = headerEnter
52087               .append('div')
52088                 .attr('class', 'qa-header-icon')
52089                 .classed('new', d => d.id < 0)
52090               .append('svg')
52091                 .attr('width', '20px')
52092                 .attr('height', '30px')
52093                 .attr('viewbox', '0 0 20 30')
52094                 .attr('class', d => `preset-icon-28 qaItem ${d.service} itemId-${d.id} itemType-${d.itemType}`);
52095
52096             svgEnter
52097               .append('polygon')
52098                 .attr('fill', d => services.osmose.getColor(d.item))
52099                 .attr('class', 'qaItem-fill')
52100                 .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');
52101
52102             svgEnter
52103               .append('use')
52104                 .attr('class', 'icon-annotation')
52105                 .attr('width', '13px')
52106                 .attr('height', '13px')
52107                 .attr('transform', 'translate(3.5, 5)')
52108                 .attr('xlink:href', d => {
52109                   const picon = d.icon;
52110
52111                   if (!picon) {
52112                     return '';
52113                   } else {
52114                     const isMaki = /^maki-/.test(picon);
52115                     return `#${picon}${isMaki ? '-11' : ''}`;
52116                   }
52117                 });
52118
52119             headerEnter
52120               .append('div')
52121                 .attr('class', 'qa-header-label')
52122                 .text(issueTitle);
52123           }
52124
52125           osmoseHeader.issue = function(val) {
52126             if (!arguments.length) return _qaItem;
52127             _qaItem = val;
52128             return osmoseHeader;
52129           };
52130
52131           return osmoseHeader;
52132         }
52133
52134         function uiViewOnOsmose() {
52135           let _qaItem;
52136
52137           function viewOnOsmose(selection) {
52138             let url;
52139             if (services.osmose && (_qaItem instanceof QAItem)) {
52140               url = services.osmose.itemURL(_qaItem);
52141             }
52142
52143             const link = selection.selectAll('.view-on-osmose')
52144               .data(url ? [url] : []);
52145
52146             // exit
52147             link.exit()
52148               .remove();
52149
52150             // enter
52151             const linkEnter = link.enter()
52152               .append('a')
52153                 .attr('class', 'view-on-osmose')
52154                 .attr('target', '_blank')
52155                 .attr('rel', 'noopener') // security measure
52156                 .attr('href', d => d)
52157                 .call(svgIcon('#iD-icon-out-link', 'inline'));
52158
52159             linkEnter
52160               .append('span')
52161                 .text(_t('inspector.view_on_osmose'));
52162           }
52163
52164           viewOnOsmose.what = function(val) {
52165             if (!arguments.length) return _qaItem;
52166             _qaItem = val;
52167             return viewOnOsmose;
52168           };
52169
52170           return viewOnOsmose;
52171         }
52172
52173         function uiOsmoseEditor(context) {
52174           const dispatch$1 = dispatch('change');
52175           const qaDetails = uiOsmoseDetails(context);
52176           const qaHeader = uiOsmoseHeader();
52177
52178           let _qaItem;
52179
52180           function osmoseEditor(selection) {
52181
52182             const header = selection.selectAll('.header')
52183               .data([0]);
52184
52185             const headerEnter = header.enter()
52186               .append('div')
52187                 .attr('class', 'header fillL');
52188
52189             headerEnter
52190               .append('button')
52191                 .attr('class', 'close')
52192                 .on('click', () => context.enter(modeBrowse(context)))
52193                 .call(svgIcon('#iD-icon-close'));
52194
52195             headerEnter
52196               .append('h3')
52197                 .text(_t('QA.osmose.title'));
52198
52199             let body = selection.selectAll('.body')
52200               .data([0]);
52201
52202             body = body.enter()
52203                 .append('div')
52204                 .attr('class', 'body')
52205               .merge(body);
52206
52207             let editor = body.selectAll('.qa-editor')
52208               .data([0]);
52209
52210             editor.enter()
52211               .append('div')
52212                 .attr('class', 'modal-section qa-editor')
52213               .merge(editor)
52214                 .call(qaHeader.issue(_qaItem))
52215                 .call(qaDetails.issue(_qaItem))
52216                 .call(osmoseSaveSection);
52217
52218             const footer = selection.selectAll('.footer')
52219               .data([0]);
52220
52221             footer.enter()
52222               .append('div')
52223               .attr('class', 'footer')
52224               .merge(footer)
52225               .call(uiViewOnOsmose().what(_qaItem));
52226           }
52227
52228           function osmoseSaveSection(selection) {
52229             const isSelected = (_qaItem && _qaItem.id === context.selectedErrorID());
52230             const isShown = (_qaItem && isSelected);
52231             let saveSection = selection.selectAll('.qa-save')
52232               .data(
52233                 (isShown ? [_qaItem] : []),
52234                 d => `${d.id}-${d.status || 0}`
52235               );
52236
52237             // exit
52238             saveSection.exit()
52239               .remove();
52240
52241             // enter
52242             const saveSectionEnter = saveSection.enter()
52243               .append('div')
52244                 .attr('class', 'qa-save save-section cf');
52245
52246             // update
52247             saveSection = saveSectionEnter
52248               .merge(saveSection)
52249                 .call(qaSaveButtons);
52250           }
52251
52252           function qaSaveButtons(selection) {
52253             const isSelected = (_qaItem && _qaItem.id === context.selectedErrorID());
52254             let buttonSection = selection.selectAll('.buttons')
52255               .data((isSelected ? [_qaItem] : []), d => d.status + d.id);
52256
52257             // exit
52258             buttonSection.exit()
52259               .remove();
52260
52261             // enter
52262             const buttonEnter = buttonSection.enter()
52263               .append('div')
52264                 .attr('class', 'buttons');
52265
52266             buttonEnter
52267               .append('button')
52268                 .attr('class', 'button close-button action');
52269
52270             buttonEnter
52271               .append('button')
52272                 .attr('class', 'button ignore-button action');
52273
52274             // update
52275             buttonSection = buttonSection
52276               .merge(buttonEnter);
52277
52278             buttonSection.select('.close-button')
52279               .text(() => _t('QA.keepRight.close'))
52280               .on('click.close', function(d) {
52281                 this.blur();    // avoid keeping focus on the button - #4641
52282                 const qaService = services.osmose;
52283                 if (qaService) {
52284                   d.newStatus = 'done';
52285                   qaService.postUpdate(d, (err, item) => dispatch$1.call('change', item));
52286                 }
52287               });
52288
52289             buttonSection.select('.ignore-button')
52290               .text(() => _t('QA.keepRight.ignore'))
52291               .on('click.ignore', function(d) {
52292                 this.blur();    // avoid keeping focus on the button - #4641
52293                 const qaService = services.osmose;
52294                 if (qaService) {
52295                   d.newStatus = 'false';
52296                   qaService.postUpdate(d, (err, item) => dispatch$1.call('change', item));
52297                 }
52298               });
52299           }
52300
52301           // NOTE: Don't change method name until UI v3 is merged
52302           osmoseEditor.error = function(val) {
52303             if (!arguments.length) return _qaItem;
52304             _qaItem = val;
52305             return osmoseEditor;
52306           };
52307
52308           return utilRebind(osmoseEditor, dispatch$1, 'on');
52309         }
52310
52311         // NOTE: Don't change name of this until UI v3 is merged
52312         function modeSelectError(context, selectedErrorID, selectedErrorService) {
52313             var mode = {
52314                 id: 'select-error',
52315                 button: 'browse'
52316             };
52317
52318             var keybinding = utilKeybinding('select-error');
52319
52320             var errorService = services[selectedErrorService];
52321             var errorEditor;
52322             switch (selectedErrorService) {
52323                 case 'improveOSM':
52324                     errorEditor = uiImproveOsmEditor(context)
52325                     .on('change', function() {
52326                         context.map().pan([0,0]);  // trigger a redraw
52327                         var error = checkSelectedID();
52328                         if (!error) return;
52329                         context.ui().sidebar
52330                             .show(errorEditor.error(error));
52331                     });
52332                     break;
52333                 case 'keepRight':
52334                     errorEditor = uiKeepRightEditor(context)
52335                     .on('change', function() {
52336                         context.map().pan([0,0]);  // trigger a redraw
52337                         var error = checkSelectedID();
52338                         if (!error) return;
52339                         context.ui().sidebar
52340                             .show(errorEditor.error(error));
52341                     });
52342                     break;
52343                 case 'osmose':
52344                     errorEditor = uiOsmoseEditor(context)
52345                     .on('change', function() {
52346                         context.map().pan([0,0]);  // trigger a redraw
52347                         var error = checkSelectedID();
52348                         if (!error) return;
52349                         context.ui().sidebar
52350                             .show(errorEditor.error(error));
52351                     });
52352                     break;
52353             }
52354
52355
52356             var behaviors = [
52357                 behaviorBreathe(),
52358                 behaviorHover(context),
52359                 behaviorSelect(context),
52360                 behaviorLasso(context),
52361                 modeDragNode(context).behavior,
52362                 modeDragNote(context).behavior
52363             ];
52364
52365
52366             function checkSelectedID() {
52367                 if (!errorService) return;
52368                 var error = errorService.getError(selectedErrorID);
52369                 if (!error) {
52370                     context.enter(modeBrowse(context));
52371                 }
52372                 return error;
52373             }
52374
52375
52376             mode.zoomToSelected = function() {
52377                 if (!errorService) return;
52378                 var error = errorService.getError(selectedErrorID);
52379                 if (error) {
52380                     context.map().centerZoomEase(error.loc, 20);
52381                 }
52382             };
52383
52384
52385             mode.enter = function() {
52386                 var error = checkSelectedID();
52387                 if (!error) return;
52388
52389                 behaviors.forEach(context.install);
52390                 keybinding
52391                     .on(_t('inspector.zoom_to.key'), mode.zoomToSelected)
52392                     .on('⎋', esc, true);
52393
52394                 select(document)
52395                     .call(keybinding);
52396
52397                 selectError();
52398
52399                 var sidebar = context.ui().sidebar;
52400                 sidebar.show(errorEditor.error(error));
52401
52402                 context.map()
52403                     .on('drawn.select-error', selectError);
52404
52405
52406                 // class the error as selected, or return to browse mode if the error is gone
52407                 function selectError(drawn) {
52408                     if (!checkSelectedID()) return;
52409
52410                     var selection = context.surface()
52411                         .selectAll('.itemId-' + selectedErrorID + '.' + selectedErrorService);
52412
52413                     if (selection.empty()) {
52414                         // Return to browse mode if selected DOM elements have
52415                         // disappeared because the user moved them out of view..
52416                         var source = event && event.type === 'zoom' && event.sourceEvent;
52417                         if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) {
52418                             context.enter(modeBrowse(context));
52419                         }
52420
52421                     } else {
52422                         selection
52423                             .classed('selected', true);
52424
52425                         context.selectedErrorID(selectedErrorID);
52426                     }
52427                 }
52428
52429                 function esc() {
52430                     if (context.container().select('.combobox').size()) return;
52431                     context.enter(modeBrowse(context));
52432                 }
52433             };
52434
52435
52436             mode.exit = function() {
52437                 behaviors.forEach(context.uninstall);
52438
52439                 select(document)
52440                     .call(keybinding.unbind);
52441
52442                 context.surface()
52443                     .selectAll('.qaItem.selected')
52444                     .classed('selected hover', false);
52445
52446                 context.map()
52447                     .on('drawn.select-error', null);
52448
52449                 context.ui().sidebar
52450                     .hide();
52451
52452                 context.selectedErrorID(null);
52453                 context.features().forceVisible([]);
52454             };
52455
52456
52457             return mode;
52458         }
52459
52460         function behaviorSelect(context) {
52461             var _tolerancePx = 4; // see also behaviorDrag
52462             var _lastMouseEvent = null;
52463             var _showMenu = false;
52464             var _downPointers = {};
52465             var _longPressTimeout = null;
52466             var _lastInteractionType = null;
52467             // the id of the down pointer that's enabling multiselection while down
52468             var _multiselectionPointerId = null;
52469
52470             // use pointer events on supported platforms; fallback to mouse events
52471             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
52472
52473
52474             function keydown() {
52475
52476                 if (event.keyCode === 32) {
52477                     // don't react to spacebar events during text input
52478                     var activeNode = document.activeElement;
52479                     if (activeNode && new Set(['INPUT', 'TEXTAREA']).has(activeNode.nodeName)) return;
52480                 }
52481
52482                 if (event.keyCode === 93 ||  // context menu key
52483                     event.keyCode === 32) {  // spacebar
52484                     event.preventDefault();
52485                 }
52486
52487                 if (event.repeat) return; // ignore repeated events for held keys
52488
52489                 // if any key is pressed the user is probably doing something other than long-pressing
52490                 cancelLongPress();
52491
52492                 if (event.shiftKey) {
52493                     context.surface()
52494                         .classed('behavior-multiselect', true);
52495                 }
52496
52497                 if (event.keyCode === 32) {  // spacebar
52498                     if (!_downPointers.spacebar && _lastMouseEvent) {
52499                         cancelLongPress();
52500                         _longPressTimeout = window.setTimeout(didLongPress, 500, 'spacebar', 'spacebar');
52501
52502                         _downPointers.spacebar = {
52503                             firstEvent: _lastMouseEvent,
52504                             lastEvent: _lastMouseEvent
52505                         };
52506                     }
52507                 }
52508             }
52509
52510
52511             function keyup() {
52512                 cancelLongPress();
52513
52514                 if (!event.shiftKey) {
52515                     context.surface()
52516                         .classed('behavior-multiselect', false);
52517                 }
52518
52519                 if (event.keyCode === 93) {  // context menu key
52520                     event.preventDefault();
52521                     _lastInteractionType = 'menukey';
52522                     contextmenu();
52523                 } else if (event.keyCode === 32) {  // spacebar
52524                     var pointer = _downPointers.spacebar;
52525                     if (pointer) {
52526                         delete _downPointers.spacebar;
52527
52528                         if (pointer.done) return;
52529
52530                         event.preventDefault();
52531                         _lastInteractionType = 'spacebar';
52532                         click(pointer.firstEvent, pointer.lastEvent, 'spacebar');
52533                     }
52534                 }
52535             }
52536
52537
52538             function pointerdown() {
52539                 var id = (event.pointerId || 'mouse').toString();
52540
52541                 cancelLongPress();
52542
52543                 if (event.buttons && event.buttons !== 1) return;
52544
52545                 context.ui().closeEditMenu();
52546
52547                 _longPressTimeout = window.setTimeout(didLongPress, 500, id, 'longdown-' + (event.pointerType || 'mouse'));
52548
52549                 _downPointers[id] = {
52550                     firstEvent: event,
52551                     lastEvent: event
52552                 };
52553             }
52554
52555
52556             function didLongPress(id, interactionType) {
52557                 var pointer = _downPointers[id];
52558                 if (!pointer) return;
52559
52560                 for (var i in _downPointers) {
52561                     // don't allow this or any currently down pointer to trigger another click
52562                     _downPointers[i].done = true;
52563                 }
52564
52565                 // treat long presses like right-clicks
52566                 _longPressTimeout = null;
52567                 _lastInteractionType = interactionType;
52568                 _showMenu = true;
52569
52570                 click(pointer.firstEvent, pointer.lastEvent, id);
52571             }
52572
52573
52574             function pointermove() {
52575                 var id = (event.pointerId || 'mouse').toString();
52576                 if (_downPointers[id]) {
52577                     _downPointers[id].lastEvent = event;
52578                 }
52579                 if (!event.pointerType || event.pointerType === 'mouse') {
52580                     _lastMouseEvent = event;
52581                     if (_downPointers.spacebar) {
52582                         _downPointers.spacebar.lastEvent = event;
52583                     }
52584                 }
52585             }
52586
52587
52588             function pointerup() {
52589                 var id = (event.pointerId || 'mouse').toString();
52590                 var pointer = _downPointers[id];
52591                 if (!pointer) return;
52592
52593                 delete _downPointers[id];
52594
52595                 if (_multiselectionPointerId === id) {
52596                     _multiselectionPointerId = null;
52597                 }
52598
52599                 if (pointer.done) return;
52600
52601                 click(pointer.firstEvent, event, id);
52602             }
52603
52604
52605             function pointercancel() {
52606                 var id = (event.pointerId || 'mouse').toString();
52607                 if (!_downPointers[id]) return;
52608
52609                 delete _downPointers[id];
52610
52611                 if (_multiselectionPointerId === id) {
52612                     _multiselectionPointerId = null;
52613                 }
52614             }
52615
52616
52617             function contextmenu() {
52618                 var e = event;
52619                 e.preventDefault();
52620
52621                 if (!+e.clientX && !+e.clientY) {
52622                     if (_lastMouseEvent) {
52623                         e.sourceEvent = _lastMouseEvent;
52624                     } else {
52625                         return;
52626                     }
52627                 } else {
52628                     _lastMouseEvent = event;
52629                     _lastInteractionType = 'rightclick';
52630                 }
52631
52632                 _showMenu = true;
52633                 click(event, event);
52634             }
52635
52636
52637             function click(firstEvent, lastEvent, pointerId) {
52638                 cancelLongPress();
52639
52640                 var mapNode = context.container().select('.main-map').node();
52641
52642                 // Use the `main-map` coordinate system since the surface and supersurface
52643                 // are transformed when drag-panning.
52644                 var pointGetter = utilFastMouse(mapNode);
52645                 var p1 = pointGetter(firstEvent);
52646                 var p2 = pointGetter(lastEvent);
52647                 var dist = geoVecLength(p1, p2);
52648
52649                 if (dist > _tolerancePx ||
52650                     !mapContains(lastEvent)) {
52651
52652                     resetProperties();
52653                     return;
52654                 }
52655
52656                 var targetDatum = lastEvent.target.__data__;
52657
52658                 var multiselectEntityId;
52659
52660                 if (!_multiselectionPointerId) {
52661                     // If a different pointer than the one triggering this click is down on a
52662                     // feature, treat this and all future clicks as multiselection until that
52663                     // pointer is raised.
52664                     var selectPointerInfo = pointerDownOnSelection(pointerId);
52665                     if (selectPointerInfo) {
52666                         _multiselectionPointerId = selectPointerInfo.pointerId;
52667                         // if the other feature isn't selected yet, make sure we select it
52668                         multiselectEntityId = !selectPointerInfo.selected && selectPointerInfo.entityId;
52669                         _downPointers[selectPointerInfo.pointerId].done = true;
52670                     }
52671                 }
52672
52673                 // support multiselect if data is already selected
52674                 var isMultiselect = context.mode().id === 'select' && (
52675                     // and shift key is down
52676                     (event && event.shiftKey) ||
52677                     // or we're lasso-selecting
52678                     context.surface().select('.lasso').node() ||
52679                     // or a pointer is down over a selected feature
52680                     (_multiselectionPointerId && !multiselectEntityId)
52681                 );
52682
52683                 processClick(targetDatum, isMultiselect, p2, multiselectEntityId);
52684
52685                 function mapContains(event) {
52686                     var rect = mapNode.getBoundingClientRect();
52687                     return event.clientX >= rect.left &&
52688                         event.clientX <= rect.right &&
52689                         event.clientY >= rect.top &&
52690                         event.clientY <= rect.bottom;
52691                 }
52692
52693                 function pointerDownOnSelection(skipPointerId) {
52694                     var mode = context.mode();
52695                     var selectedIDs = mode.id === 'select' ? mode.selectedIDs() : [];
52696                     for (var pointerId in _downPointers) {
52697                         if (pointerId === 'spacebar' || pointerId === skipPointerId) continue;
52698
52699                         var pointerInfo = _downPointers[pointerId];
52700
52701                         var p1 = pointGetter(pointerInfo.firstEvent);
52702                         var p2 = pointGetter(pointerInfo.lastEvent);
52703                         if (geoVecLength(p1, p2) > _tolerancePx) continue;
52704
52705                         var datum = pointerInfo.firstEvent.target.__data__;
52706                         var entity = (datum && datum.properties && datum.properties.entity) || datum;
52707                         if (context.graph().hasEntity(entity.id)) return {
52708                             pointerId: pointerId,
52709                             entityId: entity.id,
52710                             selected: selectedIDs.indexOf(entity.id) !== -1
52711                         };
52712                     }
52713                     return null;
52714                 }
52715             }
52716
52717
52718             function processClick(datum, isMultiselect, point, alsoSelectId) {
52719                 var mode = context.mode();
52720                 var showMenu = _showMenu;
52721                 var interactionType = _lastInteractionType;
52722
52723                 var entity = datum && datum.properties && datum.properties.entity;
52724                 if (entity) datum = entity;
52725
52726                 if (datum && datum.type === 'midpoint') {
52727                     // treat targeting midpoints as if targeting the parent way
52728                     datum = datum.parents[0];
52729                 }
52730
52731                 var newMode;
52732
52733                 if (datum instanceof osmEntity) {
52734                     // targeting an entity
52735                     var selectedIDs = context.selectedIDs();
52736                     context.selectedNoteID(null);
52737                     context.selectedErrorID(null);
52738
52739                     if (!isMultiselect) {
52740                         // don't change the selection if we're toggling the menu atop a multiselection
52741                         if (!showMenu ||
52742                             selectedIDs.length <= 1 ||
52743                             selectedIDs.indexOf(datum.id) === -1) {
52744
52745                             if (alsoSelectId === datum.id) alsoSelectId = null;
52746
52747                             selectedIDs = (alsoSelectId ? [alsoSelectId] : []).concat([datum.id]);
52748                             // always enter modeSelect even if the entity is already
52749                             // selected since listeners may expect `context.enter` events,
52750                             // e.g. in the walkthrough
52751                             newMode = mode.id === 'select' ? mode.selectedIDs(selectedIDs) : modeSelect(context, selectedIDs).selectBehavior(behavior);
52752                             context.enter(newMode);
52753                         }
52754
52755                     } else {
52756                         if (selectedIDs.indexOf(datum.id) !== -1) {
52757                             // clicked entity is already in the selectedIDs list..
52758                             if (!showMenu) {
52759                                 // deselect clicked entity, then reenter select mode or return to browse mode..
52760                                 selectedIDs = selectedIDs.filter(function(id) { return id !== datum.id; });
52761                                 newMode = selectedIDs.length ? mode.selectedIDs(selectedIDs) : modeBrowse(context).selectBehavior(behavior);
52762                                 context.enter(newMode);
52763                             }
52764                         } else {
52765                             // clicked entity is not in the selected list, add it..
52766                             selectedIDs = selectedIDs.concat([datum.id]);
52767                             newMode = mode.selectedIDs(selectedIDs);
52768                             context.enter(newMode);
52769                         }
52770                     }
52771
52772                 } else if (datum && datum.__featurehash__ && !isMultiselect) {
52773                     // targeting custom data
52774                     context
52775                         .selectedNoteID(null)
52776                         .enter(modeSelectData(context, datum));
52777
52778                 } else if (datum instanceof osmNote && !isMultiselect) {
52779                     // targeting a note
52780                     context
52781                         .selectedNoteID(datum.id)
52782                         .enter(modeSelectNote(context, datum.id));
52783
52784                 } else if (datum instanceof QAItem & !isMultiselect) {
52785                     // targeting an external QA issue
52786                     context
52787                         .selectedErrorID(datum.id)
52788                         .enter(modeSelectError(context, datum.id, datum.service));
52789
52790                 } else {
52791                     // targeting nothing
52792                     context.selectedNoteID(null);
52793                     context.selectedErrorID(null);
52794                     if (!isMultiselect && mode.id !== 'browse') {
52795                         context.enter(modeBrowse(context));
52796                     }
52797                 }
52798
52799                 context.ui().closeEditMenu();
52800
52801                 // always request to show the edit menu in case the mode needs it
52802                 if (showMenu) context.ui().showEditMenu(point, interactionType);
52803
52804                 resetProperties();
52805             }
52806
52807
52808             function cancelLongPress() {
52809                 if (_longPressTimeout) window.clearTimeout(_longPressTimeout);
52810                 _longPressTimeout = null;
52811             }
52812
52813
52814             function resetProperties() {
52815                 cancelLongPress();
52816                 _showMenu = false;
52817                 _lastInteractionType = null;
52818                 // don't reset _lastMouseEvent since it might still be useful
52819             }
52820
52821
52822             function behavior(selection) {
52823                 resetProperties();
52824                 _lastMouseEvent = context.map().lastPointerEvent();
52825
52826                 select(window)
52827                     .on('keydown.select', keydown)
52828                     .on('keyup.select', keyup)
52829                     .on(_pointerPrefix + 'move.select', pointermove, true)
52830                     .on(_pointerPrefix + 'up.select', pointerup, true)
52831                     .on('pointercancel.select', pointercancel, true)
52832                     .on('contextmenu.select-window', function() {
52833                         // Edge and IE really like to show the contextmenu on the
52834                         // menubar when user presses a keyboard menu button
52835                         // even after we've already preventdefaulted the key event.
52836                         var e = event;
52837                         if (+e.clientX === 0 && +e.clientY === 0) {
52838                             event.preventDefault();
52839                         }
52840                     });
52841
52842                 selection
52843                     .on(_pointerPrefix + 'down.select', pointerdown)
52844                     .on('contextmenu.select', contextmenu);
52845
52846                 if (event && event.shiftKey) {
52847                     context.surface()
52848                         .classed('behavior-multiselect', true);
52849                 }
52850             }
52851
52852
52853             behavior.off = function(selection) {
52854                 cancelLongPress();
52855
52856                 select(window)
52857                     .on('keydown.select', null)
52858                     .on('keyup.select', null)
52859                     .on('contextmenu.select-window', null)
52860                     .on(_pointerPrefix + 'move.select', null, true)
52861                     .on(_pointerPrefix + 'up.select', null, true)
52862                     .on('pointercancel.select', null, true);
52863
52864                 selection
52865                     .on(_pointerPrefix + 'down.select', null)
52866                     .on('contextmenu.select', null);
52867
52868                 context.surface()
52869                     .classed('behavior-multiselect', false);
52870             };
52871
52872
52873             return behavior;
52874         }
52875
52876         function behaviorDrawWay(context, wayID, mode, startGraph) {
52877
52878             var dispatch$1 = dispatch('rejectedSelfIntersection');
52879
52880             var behavior = behaviorDraw(context);
52881
52882             // Must be set by `drawWay.nodeIndex` before each install of this behavior.
52883             var _nodeIndex;
52884
52885             var _origWay;
52886             var _wayGeometry;
52887             var _headNodeID;
52888             var _annotation;
52889
52890             var _pointerHasMoved = false;
52891
52892             // The osmNode to be placed.
52893             // This is temporary and just follows the mouse cursor until an "add" event occurs.
52894             var _drawNode;
52895
52896             var _didResolveTempEdit = false;
52897
52898             function createDrawNode(loc) {
52899                 // don't make the draw node until we actually need it
52900                 _drawNode = osmNode({ loc: loc });
52901
52902                 context.pauseChangeDispatch();
52903                 context.replace(function actionAddDrawNode(graph) {
52904                     // add the draw node to the graph and insert it into the way
52905                     var way = graph.entity(wayID);
52906                     return graph
52907                         .replace(_drawNode)
52908                         .replace(way.addNode(_drawNode.id, _nodeIndex));
52909                 }, _annotation);
52910                 context.resumeChangeDispatch();
52911
52912                 setActiveElements();
52913             }
52914
52915             function removeDrawNode() {
52916
52917                 context.pauseChangeDispatch();
52918                 context.replace(
52919                     function actionDeleteDrawNode(graph) {
52920                        var way = graph.entity(wayID);
52921                        return graph
52922                            .replace(way.removeNode(_drawNode.id))
52923                            .remove(_drawNode);
52924                    },
52925                     _annotation
52926                 );
52927                 _drawNode = undefined;
52928                 context.resumeChangeDispatch();
52929             }
52930
52931
52932             function keydown() {
52933                 if (event.keyCode === utilKeybinding.modifierCodes.alt) {
52934                     if (context.surface().classed('nope')) {
52935                         context.surface()
52936                             .classed('nope-suppressed', true);
52937                     }
52938                     context.surface()
52939                         .classed('nope', false)
52940                         .classed('nope-disabled', true);
52941                 }
52942             }
52943
52944
52945             function keyup() {
52946                 if (event.keyCode === utilKeybinding.modifierCodes.alt) {
52947                     if (context.surface().classed('nope-suppressed')) {
52948                         context.surface()
52949                             .classed('nope', true);
52950                     }
52951                     context.surface()
52952                         .classed('nope-suppressed', false)
52953                         .classed('nope-disabled', false);
52954                 }
52955             }
52956
52957
52958             function allowsVertex(d) {
52959                 return d.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(d, context.graph());
52960             }
52961
52962
52963             // related code
52964             // - `mode/drag_node.js`     `doMove()`
52965             // - `behavior/draw.js`      `click()`
52966             // - `behavior/draw_way.js`  `move()`
52967             function move(datum) {
52968
52969                 var loc = context.map().mouseCoordinates();
52970
52971                 if (!_drawNode) createDrawNode(loc);
52972
52973                 context.surface().classed('nope-disabled', event.altKey);
52974
52975                 var targetLoc = datum && datum.properties && datum.properties.entity &&
52976                     allowsVertex(datum.properties.entity) && datum.properties.entity.loc;
52977                 var targetNodes = datum && datum.properties && datum.properties.nodes;
52978
52979                 if (targetLoc) {   // snap to node/vertex - a point target with `.loc`
52980                     loc = targetLoc;
52981
52982                 } else if (targetNodes) {   // snap to way - a line target with `.nodes`
52983                     var choice = geoChooseEdge(targetNodes, context.map().mouse(), context.projection, _drawNode.id);
52984                     if (choice) {
52985                         loc = choice.loc;
52986                     }
52987                 }
52988
52989                 context.replace(actionMoveNode(_drawNode.id, loc), _annotation);
52990                 _drawNode = context.entity(_drawNode.id);
52991                 checkGeometry(true /* includeDrawNode */);
52992             }
52993
52994
52995             // Check whether this edit causes the geometry to break.
52996             // If so, class the surface with a nope cursor.
52997             // `includeDrawNode` - Only check the relevant line segments if finishing drawing
52998             function checkGeometry(includeDrawNode) {
52999                 var nopeDisabled = context.surface().classed('nope-disabled');
53000                 var isInvalid = isInvalidGeometry(includeDrawNode);
53001
53002                 if (nopeDisabled) {
53003                     context.surface()
53004                         .classed('nope', false)
53005                         .classed('nope-suppressed', isInvalid);
53006                 } else {
53007                     context.surface()
53008                         .classed('nope', isInvalid)
53009                         .classed('nope-suppressed', false);
53010                 }
53011             }
53012
53013
53014             function isInvalidGeometry(includeDrawNode) {
53015
53016                 var testNode = _drawNode;
53017
53018                 // we only need to test the single way we're drawing
53019                 var parentWay = context.graph().entity(wayID);
53020                 var nodes = context.graph().childNodes(parentWay).slice();  // shallow copy
53021
53022                 if (includeDrawNode) {
53023                     if (parentWay.isClosed()) {
53024                         // don't test the last segment for closed ways - #4655
53025                         // (still test the first segement)
53026                         nodes.pop();
53027                     }
53028                 } else { // discount the draw node
53029
53030                     if (parentWay.isClosed()) {
53031                         if (nodes.length < 3) return false;
53032                         if (_drawNode) nodes.splice(-2, 1);
53033                         testNode = nodes[nodes.length - 2];
53034                     } else {
53035                         // there's nothing we need to test if we ignore the draw node on open ways
53036                         return false;
53037                     }
53038                 }
53039
53040                 return testNode && geoHasSelfIntersections(nodes, testNode.id);
53041             }
53042
53043
53044             function undone() {
53045
53046                 // undoing removed the temp edit
53047                 _didResolveTempEdit = true;
53048
53049                 context.pauseChangeDispatch();
53050
53051                 var nextMode;
53052
53053                 if (context.graph() === startGraph) { // we've undone back to the beginning
53054                     nextMode = modeSelect(context, [wayID]);
53055                 } else {
53056                     context.history()
53057                         .on('undone.draw', null);
53058                     // remove whatever segment was drawn previously
53059                     context.undo();
53060
53061                     if (context.graph() === startGraph) { // we've undone back to the beginning
53062                         nextMode = modeSelect(context, [wayID]);
53063                     } else {
53064                         // continue drawing
53065                         nextMode = mode;
53066                     }
53067                 }
53068
53069                 // clear the redo stack by adding and removing an edit
53070                 context.perform(actionNoop());
53071                 context.pop(1);
53072
53073                 context.resumeChangeDispatch();
53074                 context.enter(nextMode);
53075             }
53076
53077
53078             function setActiveElements() {
53079                 if (!_drawNode) return;
53080
53081                 context.surface().selectAll('.' + _drawNode.id)
53082                     .classed('active', true);
53083             }
53084
53085
53086             function resetToStartGraph() {
53087                 while (context.graph() !== startGraph) {
53088                     context.pop();
53089                 }
53090             }
53091
53092
53093             var drawWay = function(surface) {
53094                 _drawNode = undefined;
53095                 _didResolveTempEdit = false;
53096                 _origWay = context.entity(wayID);
53097                 _headNodeID = typeof _nodeIndex === 'number' ? _origWay.nodes[_nodeIndex] :
53098                     (_origWay.isClosed() ? _origWay.nodes[_origWay.nodes.length - 2] : _origWay.nodes[_origWay.nodes.length - 1]);
53099                 _wayGeometry = _origWay.geometry(context.graph());
53100                 _annotation = _t((_origWay.isDegenerate() ?
53101                     'operations.start.annotation.' :
53102                     'operations.continue.annotation.') + _wayGeometry
53103                 );
53104                 _pointerHasMoved = false;
53105
53106                 // Push an annotated state for undo to return back to.
53107                 // We must make sure to replace or remove it later.
53108                 context.pauseChangeDispatch();
53109                 context.perform(actionNoop(), _annotation);
53110                 context.resumeChangeDispatch();
53111
53112                 behavior.hover()
53113                     .initialNodeID(_headNodeID);
53114
53115                 behavior
53116                     .on('move', function() {
53117                         _pointerHasMoved = true;
53118                         move.apply(this, arguments);
53119                     })
53120                     .on('down', function() {
53121                         move.apply(this, arguments);
53122                     })
53123                     .on('downcancel', function() {
53124                         if (_drawNode) removeDrawNode();
53125                     })
53126                     .on('click', drawWay.add)
53127                     .on('clickWay', drawWay.addWay)
53128                     .on('clickNode', drawWay.addNode)
53129                     .on('undo', context.undo)
53130                     .on('cancel', drawWay.cancel)
53131                     .on('finish', drawWay.finish);
53132
53133                 select(window)
53134                     .on('keydown.drawWay', keydown)
53135                     .on('keyup.drawWay', keyup);
53136
53137                 context.map()
53138                     .dblclickZoomEnable(false)
53139                     .on('drawn.draw', setActiveElements);
53140
53141                 setActiveElements();
53142
53143                 surface.call(behavior);
53144
53145                 context.history()
53146                     .on('undone.draw', undone);
53147             };
53148
53149
53150             drawWay.off = function(surface) {
53151
53152                 if (!_didResolveTempEdit) {
53153                     // Drawing was interrupted unexpectedly.
53154                     // This can happen if the user changes modes,
53155                     // clicks geolocate button, a hashchange event occurs, etc.
53156
53157                     context.pauseChangeDispatch();
53158                     resetToStartGraph();
53159                     context.resumeChangeDispatch();
53160                 }
53161
53162                 _drawNode = undefined;
53163                 _nodeIndex = undefined;
53164
53165                 context.map()
53166                     .on('drawn.draw', null);
53167
53168                 surface.call(behavior.off)
53169                     .selectAll('.active')
53170                     .classed('active', false);
53171
53172                 surface
53173                     .classed('nope', false)
53174                     .classed('nope-suppressed', false)
53175                     .classed('nope-disabled', false);
53176
53177                 select(window)
53178                     .on('keydown.drawWay', null)
53179                     .on('keyup.drawWay', null);
53180
53181                 context.history()
53182                     .on('undone.draw', null);
53183             };
53184
53185
53186             function attemptAdd(d, loc, doAdd) {
53187
53188                 if (_drawNode) {
53189                     // move the node to the final loc in case move wasn't called
53190                     // consistently (e.g. on touch devices)
53191                     context.replace(actionMoveNode(_drawNode.id, loc), _annotation);
53192                     _drawNode = context.entity(_drawNode.id);
53193                 } else {
53194                     createDrawNode(loc);
53195                 }
53196
53197                 checkGeometry(true /* includeDrawNode */);
53198                 if ((d && d.properties && d.properties.nope) || context.surface().classed('nope')) {
53199                     if (!_pointerHasMoved) {
53200                         // prevent the temporary draw node from appearing on touch devices
53201                         removeDrawNode();
53202                     }
53203                     dispatch$1.call('rejectedSelfIntersection', this);
53204                     return;   // can't click here
53205                 }
53206
53207                 context.pauseChangeDispatch();
53208                 doAdd();
53209                 // we just replaced the temporary edit with the real one
53210                 _didResolveTempEdit = true;
53211                 context.resumeChangeDispatch();
53212
53213                 context.enter(mode);
53214             }
53215
53216
53217             // Accept the current position of the drawing node
53218             drawWay.add = function(loc, d) {
53219                 attemptAdd(d, loc, function() {
53220                     // don't need to do anything extra
53221                 });
53222             };
53223
53224
53225             // Connect the way to an existing way
53226             drawWay.addWay = function(loc, edge, d) {
53227                 attemptAdd(d, loc, function() {
53228                     context.replace(
53229                         actionAddMidpoint({ loc: loc, edge: edge }, _drawNode),
53230                         _annotation
53231                     );
53232                 });
53233             };
53234
53235
53236             // Connect the way to an existing node
53237             drawWay.addNode = function(node, d) {
53238
53239                 // finish drawing if the mapper targets the prior node
53240                 if (node.id === _headNodeID ||
53241                     // or the first node when drawing an area
53242                     (_origWay.isClosed() && node.id === _origWay.first())) {
53243                     drawWay.finish();
53244                     return;
53245                 }
53246
53247                 attemptAdd(d, node.loc, function() {
53248                     context.replace(
53249                         function actionReplaceDrawNode(graph) {
53250                             // remove the temporary draw node and insert the existing node
53251                             // at the same index
53252
53253                             graph = graph
53254                                 .replace(graph.entity(wayID).removeNode(_drawNode.id))
53255                                 .remove(_drawNode);
53256                             return graph
53257                                 .replace(graph.entity(wayID).addNode(node.id, _nodeIndex));
53258                         },
53259                         _annotation
53260                     );
53261                 });
53262             };
53263
53264
53265             // Finish the draw operation, removing the temporary edit.
53266             // If the way has enough nodes to be valid, it's selected.
53267             // Otherwise, delete everything and return to browse mode.
53268             drawWay.finish = function() {
53269                 checkGeometry(false /* includeDrawNode */);
53270                 if (context.surface().classed('nope')) {
53271                     dispatch$1.call('rejectedSelfIntersection', this);
53272                     return;   // can't click here
53273                 }
53274
53275                 context.pauseChangeDispatch();
53276                 // remove the temporary edit
53277                 context.pop(1);
53278                 _didResolveTempEdit = true;
53279                 context.resumeChangeDispatch();
53280
53281                 var way = context.hasEntity(wayID);
53282                 if (!way || way.isDegenerate()) {
53283                     drawWay.cancel();
53284                     return;
53285                 }
53286
53287                 window.setTimeout(function() {
53288                     context.map().dblclickZoomEnable(true);
53289                 }, 1000);
53290
53291                 var isNewFeature = !mode.isContinuing;
53292                 context.enter(modeSelect(context, [wayID]).newFeature(isNewFeature));
53293             };
53294
53295
53296             // Cancel the draw operation, delete everything, and return to browse mode.
53297             drawWay.cancel = function() {
53298                 context.pauseChangeDispatch();
53299                 resetToStartGraph();
53300                 context.resumeChangeDispatch();
53301
53302                 window.setTimeout(function() {
53303                     context.map().dblclickZoomEnable(true);
53304                 }, 1000);
53305
53306                 context.surface()
53307                     .classed('nope', false)
53308                     .classed('nope-disabled', false)
53309                     .classed('nope-suppressed', false);
53310
53311                 context.enter(modeBrowse(context));
53312             };
53313
53314
53315             drawWay.nodeIndex = function(val) {
53316                 if (!arguments.length) return _nodeIndex;
53317                 _nodeIndex = val;
53318                 return drawWay;
53319             };
53320
53321
53322             drawWay.activeID = function() {
53323                 if (!arguments.length) return _drawNode && _drawNode.id;
53324                 // no assign
53325                 return drawWay;
53326             };
53327
53328
53329             return utilRebind(drawWay, dispatch$1, 'on');
53330         }
53331
53332         function modeDrawLine(context, wayID, startGraph, button, affix, continuing) {
53333             var mode = {
53334                 button: button,
53335                 id: 'draw-line'
53336             };
53337
53338             var behavior = behaviorDrawWay(context, wayID, mode, startGraph)
53339                 .on('rejectedSelfIntersection.modeDrawLine', function() {
53340                     context.ui().flash
53341                         .text(_t('self_intersection.error.lines'))();
53342                 });
53343
53344             mode.wayID = wayID;
53345
53346             mode.isContinuing = continuing;
53347
53348             mode.enter = function() {
53349                 behavior
53350                     .nodeIndex(affix === 'prefix' ? 0 : undefined);
53351
53352                 context.install(behavior);
53353             };
53354
53355             mode.exit = function() {
53356                 context.uninstall(behavior);
53357             };
53358
53359             mode.selectedIDs = function() {
53360                 return [wayID];
53361             };
53362
53363             mode.activeID = function() {
53364                 return (behavior && behavior.activeID()) || [];
53365             };
53366
53367             return mode;
53368         }
53369
53370         function operationContinue(context, selectedIDs) {
53371             var graph = context.graph();
53372             var entities = selectedIDs.map(function(id) { return graph.entity(id); });
53373             var geometries = Object.assign(
53374                 { line: [], vertex: [] },
53375                 utilArrayGroupBy(entities, function(entity) { return entity.geometry(graph); })
53376             );
53377             var vertex = geometries.vertex[0];
53378
53379
53380             function candidateWays() {
53381                 return graph.parentWays(vertex).filter(function(parent) {
53382                     return parent.geometry(graph) === 'line' &&
53383                         !parent.isClosed() &&
53384                         parent.affix(vertex.id) &&
53385                         (geometries.line.length === 0 || geometries.line[0] === parent);
53386                 });
53387             }
53388
53389
53390             var operation = function() {
53391                 var candidate = candidateWays()[0];
53392                 context.enter(
53393                     modeDrawLine(context, candidate.id, context.graph(), 'line', candidate.affix(vertex.id), true)
53394                 );
53395             };
53396
53397
53398             operation.available = function() {
53399                 return geometries.vertex.length === 1 &&
53400                     geometries.line.length <= 1 &&
53401                     !context.features().hasHiddenConnections(vertex, context.graph());
53402             };
53403
53404
53405             operation.disabled = function() {
53406                 var candidates = candidateWays();
53407                 if (candidates.length === 0) {
53408                     return 'not_eligible';
53409                 } else if (candidates.length > 1) {
53410                     return 'multiple';
53411                 }
53412
53413                 return false;
53414             };
53415
53416
53417             operation.tooltip = function() {
53418                 var disable = operation.disabled();
53419                 return disable ?
53420                     _t('operations.continue.' + disable) :
53421                     _t('operations.continue.description');
53422             };
53423
53424
53425             operation.annotation = function() {
53426                 return _t('operations.continue.annotation.line');
53427             };
53428
53429
53430             operation.id = 'continue';
53431             operation.keys = [_t('operations.continue.key')];
53432             operation.title = _t('operations.continue.title');
53433             operation.behavior = behaviorOperation(context).which(operation);
53434
53435             return operation;
53436         }
53437
53438         function operationCopy(context, selectedIDs) {
53439
53440             var _multi = selectedIDs.length === 1 ? 'single' : 'multiple';
53441
53442             function getFilteredIdsToCopy() {
53443                 return selectedIDs.filter(function(selectedID) {
53444                     var entity = context.graph().hasEntity(selectedID);
53445                     // don't copy untagged vertices separately from ways
53446                     return entity.hasInterestingTags() || entity.geometry(context.graph()) !== 'vertex';
53447                 });
53448             }
53449
53450             var operation = function() {
53451
53452                 if (!getSelectionText()) {
53453                     event.preventDefault();
53454                 }
53455
53456                 var graph = context.graph();
53457                 var selected = groupEntities(getFilteredIdsToCopy(), graph);
53458                 var canCopy = [];
53459                 var skip = {};
53460                 var entity;
53461                 var i;
53462
53463                 for (i = 0; i < selected.relation.length; i++) {
53464                     entity = selected.relation[i];
53465                     if (!skip[entity.id] && entity.isComplete(graph)) {
53466                         canCopy.push(entity.id);
53467                         skip = getDescendants(entity.id, graph, skip);
53468                     }
53469                 }
53470                 for (i = 0; i < selected.way.length; i++) {
53471                     entity = selected.way[i];
53472                     if (!skip[entity.id]) {
53473                         canCopy.push(entity.id);
53474                         skip = getDescendants(entity.id, graph, skip);
53475                     }
53476                 }
53477                 for (i = 0; i < selected.node.length; i++) {
53478                     entity = selected.node[i];
53479                     if (!skip[entity.id]) {
53480                         canCopy.push(entity.id);
53481                     }
53482                 }
53483
53484                 context.copyIDs(canCopy);
53485                 if (_point &&
53486                     (canCopy.length !== 1 || graph.entity(canCopy[0]).type !== 'node')) {
53487                     // store the anchor coordinates if copying more than a single node
53488                     context.copyLonLat(context.projection.invert(_point));
53489                 } else {
53490                     context.copyLonLat(null);
53491                 }
53492
53493             };
53494
53495
53496             function groupEntities(ids, graph) {
53497                 var entities = ids.map(function (id) { return graph.entity(id); });
53498                 return Object.assign(
53499                     { relation: [], way: [], node: [] },
53500                     utilArrayGroupBy(entities, 'type')
53501                 );
53502             }
53503
53504
53505             function getDescendants(id, graph, descendants) {
53506                 var entity = graph.entity(id);
53507                 var children;
53508
53509                 descendants = descendants || {};
53510
53511                 if (entity.type === 'relation') {
53512                     children = entity.members.map(function(m) { return m.id; });
53513                 } else if (entity.type === 'way') {
53514                     children = entity.nodes;
53515                 } else {
53516                     children = [];
53517                 }
53518
53519                 for (var i = 0; i < children.length; i++) {
53520                     if (!descendants[children[i]]) {
53521                         descendants[children[i]] = true;
53522                         descendants = getDescendants(children[i], graph, descendants);
53523                     }
53524                 }
53525
53526                 return descendants;
53527             }
53528
53529
53530             function getSelectionText() {
53531                 return window.getSelection().toString();
53532             }
53533
53534
53535             operation.available = function() {
53536                 return getFilteredIdsToCopy().length > 0;
53537             };
53538
53539
53540             operation.disabled = function() {
53541                 var extent = utilTotalExtent(getFilteredIdsToCopy(), context.graph());
53542                 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
53543                     return 'too_large';
53544                 }
53545                 return false;
53546             };
53547
53548
53549             operation.tooltip = function() {
53550                 var disable = operation.disabled();
53551                 return disable ?
53552                     _t('operations.copy.' + disable + '.' + _multi) :
53553                     _t('operations.copy.description' + '.' + _multi);
53554             };
53555
53556
53557             operation.annotation = function() {
53558                 return selectedIDs.length === 1 ?
53559                     _t('operations.copy.annotation.single') :
53560                     _t('operations.copy.annotation.multiple', { n: selectedIDs.length.toString() });
53561             };
53562
53563
53564             var _point;
53565             operation.point = function(val) {
53566                 _point = val;
53567                 return operation;
53568             };
53569
53570
53571             operation.id = 'copy';
53572             operation.keys = [uiCmd('⌘C')];
53573             operation.title = _t('operations.copy.title');
53574             operation.behavior = behaviorOperation(context).which(operation);
53575
53576             return operation;
53577         }
53578
53579         function operationDisconnect(context, selectedIDs) {
53580             var _vertexIDs = [];
53581             var _wayIDs = [];
53582             var _otherIDs = [];
53583             var _actions = [];
53584
53585             selectedIDs.forEach(function(id) {
53586                 var entity = context.entity(id);
53587                 if (entity.type === 'way'){
53588                     _wayIDs.push(id);
53589                 } else if (entity.geometry(context.graph()) === 'vertex') {
53590                     _vertexIDs.push(id);
53591                 } else {
53592                     _otherIDs.push(id);
53593                 }
53594             });
53595
53596             var _extent, _nodes, _coords, _descriptionID = '', _annotationID = 'features';
53597
53598             if (_vertexIDs.length > 0) {
53599                 // At the selected vertices, disconnect the selected ways, if any, else
53600                 // disconnect all connected ways
53601
53602                 _extent = utilTotalExtent(_vertexIDs, context.graph());
53603
53604                 _vertexIDs.forEach(function(vertexID) {
53605                     var action = actionDisconnect(vertexID);
53606
53607                     if (_wayIDs.length > 0) {
53608                         var waysIDsForVertex = _wayIDs.filter(function(wayID) {
53609                             var way = context.entity(wayID);
53610                             return way.nodes.indexOf(vertexID) !== -1;
53611                         });
53612                         action.limitWays(waysIDsForVertex);
53613                     }
53614                     _actions.push(action);
53615                 });
53616
53617                 _descriptionID += _actions.length === 1 ? 'single_point.' : 'multiple_points.';
53618                 if (_wayIDs.length === 1) {
53619                     _descriptionID += 'single_way.' + context.graph().geometry(_wayIDs[0]);
53620                 } else {
53621                     _descriptionID += _wayIDs.length === 0 ? 'no_ways' : 'multiple_ways';
53622                 }
53623
53624             } else if (_wayIDs.length > 0) {
53625                 // Disconnect the selected ways from each other, if they're connected,
53626                 // else disconnect them from all connected ways
53627
53628                 var ways = _wayIDs.map(function(id) {
53629                     return context.entity(id);
53630                 });
53631                 _nodes = utilGetAllNodes(_wayIDs, context.graph());
53632                 _coords = _nodes.map(function(n) { return n.loc; });
53633                 _extent = utilTotalExtent(ways, context.graph());
53634
53635                 // actions for connected nodes shared by at least two selected ways
53636                 var sharedActions = [];
53637                 // actions for connected nodes
53638                 var unsharedActions = [];
53639
53640                 _nodes.forEach(function(node) {
53641                     var action = actionDisconnect(node.id).limitWays(_wayIDs);
53642                     if (action.disabled(context.graph()) !== 'not_connected') {
53643
53644                         var count = 0;
53645                         for (var i in ways) {
53646                             var way = ways[i];
53647                             if (way.nodes.indexOf(node.id) !== -1) {
53648                                 count += 1;
53649                             }
53650                             if (count > 1) break;
53651                         }
53652
53653                         if (count > 1) {
53654                             sharedActions.push(action);
53655                         } else {
53656                             unsharedActions.push(action);
53657                         }
53658                     }
53659                 });
53660
53661                 _descriptionID += 'no_points.';
53662                 _descriptionID += _wayIDs.length === 1 ? 'single_way.' : 'multiple_ways.';
53663
53664                 if (sharedActions.length) {
53665                     // if any nodes are shared, only disconnect the selected ways from each other
53666                     _actions = sharedActions;
53667                     _descriptionID += 'conjoined';
53668                     _annotationID = 'from_each_other';
53669                 } else {
53670                     // if no nodes are shared, disconnect the selected ways from all connected ways
53671                     _actions = unsharedActions;
53672                     if (_wayIDs.length === 1) {
53673                         _descriptionID += context.graph().geometry(_wayIDs[0]);
53674                     } else {
53675                         _descriptionID += 'separate';
53676                     }
53677                 }
53678             }
53679
53680
53681             var operation = function() {
53682                 context.perform(function(graph) {
53683                     return _actions.reduce(function(graph, action) { return action(graph); }, graph);
53684                 }, operation.annotation());
53685
53686                 context.validator().validate();
53687             };
53688
53689
53690             operation.available = function() {
53691                 if (_actions.length === 0) return false;
53692                 if (_otherIDs.length !== 0) return false;
53693
53694                 if (_vertexIDs.length !== 0 && _wayIDs.length !== 0 && !_wayIDs.every(function(wayID) {
53695                     return _vertexIDs.some(function(vertexID) {
53696                         var way = context.entity(wayID);
53697                         return way.nodes.indexOf(vertexID) !== -1;
53698                     });
53699                 })) return false;
53700
53701                 return true;
53702             };
53703
53704
53705             operation.disabled = function() {
53706                 var reason;
53707                 for (var actionIndex in _actions) {
53708                     reason = _actions[actionIndex].disabled(context.graph());
53709                     if (reason) return reason;
53710                 }
53711
53712                 if (_extent && _extent.percentContainedIn(context.map().extent()) < 0.8) {
53713                     return 'too_large.' + ((_vertexIDs.length ? _vertexIDs : _wayIDs).length === 1 ? 'single' : 'multiple');
53714                 } else if (_coords && someMissing()) {
53715                     return 'not_downloaded';
53716                 } else if (selectedIDs.some(context.hasHiddenConnections)) {
53717                     return 'connected_to_hidden';
53718                 }
53719
53720                 return false;
53721
53722
53723                 function someMissing() {
53724                     if (context.inIntro()) return false;
53725                     var osm = context.connection();
53726                     if (osm) {
53727                         var missing = _coords.filter(function(loc) { return !osm.isDataLoaded(loc); });
53728                         if (missing.length) {
53729                             missing.forEach(function(loc) { context.loadTileAtLoc(loc); });
53730                             return true;
53731                         }
53732                     }
53733                     return false;
53734                 }
53735             };
53736
53737
53738             operation.tooltip = function() {
53739                 var disable = operation.disabled();
53740                 if (disable) {
53741                     return _t('operations.disconnect.' + disable);
53742                 }
53743                 return _t('operations.disconnect.description.' + _descriptionID);
53744             };
53745
53746
53747             operation.annotation = function() {
53748                 return _t('operations.disconnect.annotation.' + _annotationID);
53749             };
53750
53751
53752             operation.id = 'disconnect';
53753             operation.keys = [_t('operations.disconnect.key')];
53754             operation.title = _t('operations.disconnect.title');
53755             operation.behavior = behaviorOperation(context).which(operation);
53756
53757             return operation;
53758         }
53759
53760         function operationDowngrade(context, selectedIDs) {
53761             var affectedFeatureCount = 0;
53762             var downgradeType;
53763
53764             setDowngradeTypeForEntityIDs();
53765
53766             var multi = affectedFeatureCount === 1 ? 'single' : 'multiple';
53767
53768             function setDowngradeTypeForEntityIDs() {
53769                 for (var i in selectedIDs) {
53770                     var entityID = selectedIDs[i];
53771                     var type = downgradeTypeForEntityID(entityID);
53772                     if (type) {
53773                         affectedFeatureCount += 1;
53774                         if (downgradeType && type !== downgradeType) {
53775                             downgradeType = 'building_address';
53776                         } else {
53777                             downgradeType = type;
53778                         }
53779                     }
53780                 }
53781             }
53782
53783             function downgradeTypeForEntityID(entityID) {
53784                 var graph = context.graph();
53785                 var entity = graph.entity(entityID);
53786                 var preset = _mainPresetIndex.match(entity, graph);
53787
53788                 if (!preset || preset.isFallback()) return null;
53789
53790                 if (entity.type === 'node' &&
53791                     preset.id !== 'address' &&
53792                     Object.keys(entity.tags).some(function(key) {
53793                         return key.match(/^addr:.{1,}/);
53794                     })) {
53795
53796                     return 'address';
53797                 }
53798                 if (entity.geometry(graph) === 'area' &&
53799                     entity.tags.building &&
53800                     !preset.tags.building) {
53801
53802                     return 'building';
53803                 }
53804
53805                 return null;
53806             }
53807
53808             var buildingKeysToKeep = ['architect', 'building', 'height', 'layer', 'source', 'type', 'wheelchair'];
53809             var addressKeysToKeep = ['source'];
53810
53811             var operation = function () {
53812                 context.perform(function(graph) {
53813
53814                     for (var i in selectedIDs) {
53815                         var entityID = selectedIDs[i];
53816                         var type = downgradeTypeForEntityID(entityID);
53817                         if (!type) continue;
53818
53819                         var tags = Object.assign({}, graph.entity(entityID).tags);  // shallow copy
53820                         for (var key in tags) {
53821                             if (type === 'address' && addressKeysToKeep.indexOf(key) !== -1) continue;
53822                             if (type === 'building') {
53823                                 if (buildingKeysToKeep.indexOf(key) !== -1 ||
53824                                     key.match(/^building:.{1,}/) ||
53825                                     key.match(/^roof:.{1,}/)) continue;
53826                             }
53827                             // keep address tags for buildings too
53828                             if (key.match(/^addr:.{1,}/)) continue;
53829
53830                             delete tags[key];
53831                         }
53832                         graph = actionChangeTags(entityID, tags)(graph);
53833                     }
53834                     return graph;
53835                 }, operation.annotation());
53836
53837                 context.validator().validate();
53838
53839                 // refresh the select mode to enable the delete operation
53840                 context.enter(modeSelect(context, selectedIDs));
53841             };
53842
53843
53844             operation.available = function () {
53845                 return downgradeType;
53846             };
53847
53848
53849             operation.disabled = function () {
53850                 if (selectedIDs.some(hasWikidataTag)) {
53851                     return 'has_wikidata_tag';
53852                 }
53853                 return false;
53854
53855                 function hasWikidataTag(id) {
53856                     var entity = context.entity(id);
53857                     return entity.tags.wikidata && entity.tags.wikidata.trim().length > 0;
53858                 }
53859             };
53860
53861
53862             operation.tooltip = function () {
53863                 var disable = operation.disabled();
53864                 return disable ?
53865                     _t('operations.downgrade.' + disable + '.' + multi) :
53866                     _t('operations.downgrade.description.' + downgradeType);
53867             };
53868
53869
53870             operation.annotation = function () {
53871                 var suffix;
53872                 if (downgradeType === 'building_address') {
53873                     suffix = 'multiple';
53874                 } else {
53875                     suffix = downgradeType + '.' + multi;
53876                 }
53877                 return _t('operations.downgrade.annotation.' + suffix, { n: affectedFeatureCount});
53878             };
53879
53880
53881             operation.id = 'downgrade';
53882             operation.keys = [uiCmd('⌘⌫'), uiCmd('⌘⌦'), uiCmd('⌦')];
53883             operation.title = _t('operations.downgrade.title');
53884             operation.behavior = behaviorOperation(context).which(operation);
53885
53886
53887             return operation;
53888         }
53889
53890         function operationExtract(context, selectedIDs) {
53891
53892             var _amount = selectedIDs.length === 1 ? 'single' : 'multiple';
53893             var _geometries = utilArrayUniq(selectedIDs.map(function(entityID) {
53894                 return context.graph().hasEntity(entityID) && context.graph().geometry(entityID);
53895             }).filter(Boolean));
53896             var _geometryID = _geometries.length === 1 ? _geometries[0] : 'feature';
53897
53898             var _extent;
53899             var _actions = selectedIDs.map(function(entityID) {
53900                 var graph = context.graph();
53901                 var entity = graph.hasEntity(entityID);
53902                 if (!entity || !entity.hasInterestingTags()) return;
53903
53904                 if (entity.type === 'node' && graph.parentWays(entity).length === 0) return;
53905
53906                 if (entity.type !== 'node') {
53907                     var preset = _mainPresetIndex.match(entity, graph);
53908                     // only allow extraction from ways/relations if the preset supports points
53909                     if (preset.geometry.indexOf('point') === -1) return;
53910                 }
53911
53912                 _extent = _extent ? _extent.extend(entity.extent(graph)) : entity.extent(graph);
53913
53914                 return actionExtract(entityID);
53915             }).filter(Boolean);
53916
53917
53918             var operation = function () {
53919                 var combinedAction = function(graph) {
53920                     _actions.forEach(function(action) {
53921                         graph = action(graph);
53922                     });
53923                     return graph;
53924                 };
53925                 context.perform(combinedAction, operation.annotation());  // do the extract
53926
53927                 var extractedNodeIDs = _actions.map(function(action) {
53928                     return action.getExtractedNodeID();
53929                 });
53930                 context.enter(modeSelect(context, extractedNodeIDs));
53931             };
53932
53933
53934             operation.available = function () {
53935                 return _actions.length && selectedIDs.length === _actions.length;
53936             };
53937
53938
53939             operation.disabled = function () {
53940
53941                 if (_extent && _extent.percentContainedIn(context.map().extent()) < 0.8) {
53942                     return 'too_large';
53943                 } else if (selectedIDs.some(function(entityID) {
53944                     return context.graph().geometry(entityID) === 'vertex' && context.hasHiddenConnections(entityID);
53945                 })) {
53946                     return 'connected_to_hidden';
53947                 }
53948
53949                 return false;
53950             };
53951
53952
53953             operation.tooltip = function () {
53954                 var disableReason = operation.disabled();
53955                 if (disableReason) {
53956                     return _t('operations.extract.' + disableReason + '.' + _amount);
53957                 } else {
53958                     return _t('operations.extract.description.' + _geometryID + '.' + _amount);
53959                 }
53960             };
53961
53962
53963             operation.annotation = function () {
53964                 return _t('operations.extract.annotation.' + _amount, { n: selectedIDs.length });
53965             };
53966
53967
53968             operation.id = 'extract';
53969             operation.keys = [_t('operations.extract.key')];
53970             operation.title = _t('operations.extract.title');
53971             operation.behavior = behaviorOperation(context).which(operation);
53972
53973
53974             return operation;
53975         }
53976
53977         function operationMerge(context, selectedIDs) {
53978
53979             var _action = getAction();
53980
53981             function getAction() {
53982                 // prefer a non-disabled action first
53983                 var join = actionJoin(selectedIDs);
53984                 if (!join.disabled(context.graph())) return join;
53985
53986                 var merge = actionMerge(selectedIDs);
53987                 if (!merge.disabled(context.graph())) return merge;
53988
53989                 var mergePolygon = actionMergePolygon(selectedIDs);
53990                 if (!mergePolygon.disabled(context.graph())) return mergePolygon;
53991
53992                 var mergeNodes = actionMergeNodes(selectedIDs);
53993                 if (!mergeNodes.disabled(context.graph())) return mergeNodes;
53994
53995                 // otherwise prefer an action with an interesting disabled reason
53996                 if (join.disabled(context.graph()) !== 'not_eligible') return join;
53997                 if (merge.disabled(context.graph()) !== 'not_eligible') return merge;
53998                 if (mergePolygon.disabled(context.graph()) !== 'not_eligible') return mergePolygon;
53999
54000                 return mergeNodes;
54001             }
54002
54003             var operation = function() {
54004
54005                 if (operation.disabled()) return;
54006
54007                 context.perform(_action, operation.annotation());
54008
54009                 context.validator().validate();
54010
54011                 var resultIDs = selectedIDs.filter(context.hasEntity);
54012                 if (resultIDs.length > 1) {
54013                     var interestingIDs = resultIDs.filter(function(id) {
54014                         return context.entity(id).hasInterestingTags();
54015                     });
54016                     if (interestingIDs.length) resultIDs = interestingIDs;
54017                 }
54018                 context.enter(modeSelect(context, resultIDs));
54019             };
54020
54021             operation.available = function() {
54022                 return selectedIDs.length >= 2;
54023             };
54024
54025             operation.disabled = function() {
54026                 var actionDisabled = _action.disabled(context.graph());
54027                 if (actionDisabled) return actionDisabled;
54028
54029                 var osm = context.connection();
54030                 if (osm &&
54031                     _action.resultingWayNodesLength &&
54032                     _action.resultingWayNodesLength(context.graph()) > osm.maxWayNodes()) {
54033                     return 'too_many_vertices';
54034                 }
54035
54036                 return false;
54037             };
54038
54039             operation.tooltip = function() {
54040                 var disabled = operation.disabled();
54041                 if (disabled) {
54042                     if (disabled === 'restriction') {
54043                         return _t('operations.merge.restriction',
54044                             { relation: _mainPresetIndex.item('type/restriction').name() });
54045                     }
54046                     return _t('operations.merge.' + disabled);
54047                 }
54048                 return _t('operations.merge.description');
54049             };
54050
54051             operation.annotation = function() {
54052                 return _t('operations.merge.annotation', { n: selectedIDs.length });
54053             };
54054
54055             operation.id = 'merge';
54056             operation.keys = [_t('operations.merge.key')];
54057             operation.title = _t('operations.merge.title');
54058             operation.behavior = behaviorOperation(context).which(operation);
54059
54060             return operation;
54061         }
54062
54063         // see also `behaviorPaste`
54064         function operationPaste(context) {
54065
54066             var _pastePoint;
54067
54068             var operation = function() {
54069
54070                 if (!_pastePoint) return;
54071
54072                 var oldIDs = context.copyIDs();
54073                 if (!oldIDs.length) return;
54074
54075                 var projection = context.projection;
54076                 var extent = geoExtent();
54077                 var oldGraph = context.copyGraph();
54078                 var newIDs = [];
54079
54080                 var action = actionCopyEntities(oldIDs, oldGraph);
54081                 context.perform(action);
54082
54083                 var copies = action.copies();
54084                 var originals = new Set();
54085                 Object.values(copies).forEach(function(entity) { originals.add(entity.id); });
54086
54087                 for (var id in copies) {
54088                     var oldEntity = oldGraph.entity(id);
54089                     var newEntity = copies[id];
54090
54091                     extent._extend(oldEntity.extent(oldGraph));
54092
54093                     // Exclude child nodes from newIDs if their parent way was also copied.
54094                     var parents = context.graph().parentWays(newEntity);
54095                     var parentCopied = parents.some(function(parent) {
54096                         return originals.has(parent.id);
54097                     });
54098
54099                     if (!parentCopied) {
54100                         newIDs.push(newEntity.id);
54101                     }
54102                 }
54103
54104                 // Use the location of the copy operation to offset the paste location,
54105                 // or else use the center of the pasted extent
54106                 var copyPoint = (context.copyLonLat() && projection(context.copyLonLat())) ||
54107                     projection(extent.center());
54108                 var delta = geoVecSubtract(_pastePoint, copyPoint);
54109
54110                 // Move the pasted objects to be anchored at the paste location
54111                 context.replace(actionMove(newIDs, delta, projection), operation.annotation());
54112                 context.enter(modeSelect(context, newIDs));
54113             };
54114
54115             operation.point = function(val) {
54116                 _pastePoint = val;
54117                 return operation;
54118             };
54119
54120             operation.available = function() {
54121                 return context.mode().id === 'browse';
54122             };
54123
54124             operation.disabled = function() {
54125                 return !context.copyIDs().length;
54126             };
54127
54128             operation.tooltip = function() {
54129                 var oldGraph = context.copyGraph();
54130                 var ids = context.copyIDs();
54131                 if (!ids.length) {
54132                     return _t('operations.paste.nothing_copied');
54133                 }
54134                 return ids.length === 1 ?
54135                     _t('operations.paste.description.single', { feature: utilDisplayLabel(oldGraph.entity(ids[0]), oldGraph) }) :
54136                     _t('operations.paste.description.multiple', { n: ids.length.toString() });
54137             };
54138
54139             operation.annotation = function() {
54140                 var ids = context.copyIDs();
54141                 return ids.length === 1 ?
54142                     _t('operations.paste.annotation.single') :
54143                     _t('operations.paste.annotation.multiple', { n: ids.length.toString() });
54144             };
54145
54146             operation.id = 'paste';
54147             operation.keys = [uiCmd('⌘V')];
54148             operation.title = _t('operations.paste.title');
54149
54150             return operation;
54151         }
54152
54153         function operationReverse(context, selectedIDs) {
54154
54155             var operation = function() {
54156                 context.perform(function combinedReverseAction(graph) {
54157                     actions().forEach(function(action) {
54158                         graph = action(graph);
54159                     });
54160                     return graph;
54161                 }, operation.annotation());
54162                 context.validator().validate();
54163             };
54164
54165             function actions(situation) {
54166                 return selectedIDs.map(function(entityID) {
54167                     var entity = context.hasEntity(entityID);
54168                     if (!entity) return;
54169
54170                     if (situation === 'toolbar') {
54171                         if (entity.type === 'way' &&
54172                             (!entity.isOneWay() && !entity.isSided())) return;
54173                     }
54174
54175                     var geometry = entity.geometry(context.graph());
54176                     if (entity.type !== 'node' && geometry !== 'line') return;
54177
54178                     var action = actionReverse(entityID);
54179                     if (action.disabled(context.graph())) return;
54180
54181                     return action;
54182                 }).filter(Boolean);
54183             }
54184
54185             function reverseTypeID() {
54186                 var acts = actions();
54187                 var nodeActionCount = acts.filter(function(act) {
54188                     var entity = context.hasEntity(act.entityID());
54189                     return entity && entity.type === 'node';
54190                 }).length;
54191                 var typeID = nodeActionCount === 0 ? 'line' : (nodeActionCount === acts.length ? 'point' : 'features');
54192                 if (typeID !== 'features' && acts.length > 1) typeID += 's';
54193                 return typeID;
54194             }
54195
54196
54197             operation.available = function(situation) {
54198                 return actions(situation).length > 0;
54199             };
54200
54201
54202             operation.disabled = function() {
54203                 return false;
54204             };
54205
54206
54207             operation.tooltip = function() {
54208                 return _t('operations.reverse.description.' + reverseTypeID());
54209             };
54210
54211
54212             operation.annotation = function() {
54213                 return _t('operations.reverse.annotation.' + reverseTypeID());
54214             };
54215
54216
54217             operation.id = 'reverse';
54218             operation.keys = [_t('operations.reverse.key')];
54219             operation.title = _t('operations.reverse.title');
54220             operation.behavior = behaviorOperation(context).which(operation);
54221
54222             return operation;
54223         }
54224
54225         function operationSplit(context, selectedIDs) {
54226             var vertices = selectedIDs
54227                 .filter(function(id) { return context.graph().geometry(id) === 'vertex'; });
54228             var entityID = vertices[0];
54229             var action = actionSplit(entityID);
54230             var ways = [];
54231
54232             if (vertices.length === 1) {
54233                 if (entityID && selectedIDs.length > 1) {
54234                     var ids = selectedIDs.filter(function(id) { return id !== entityID; });
54235                     action.limitWays(ids);
54236                 }
54237                 ways = action.ways(context.graph());
54238             }
54239
54240
54241             var operation = function() {
54242                 var difference = context.perform(action, operation.annotation());
54243                 context.enter(modeSelect(context, difference.extantIDs()));
54244             };
54245
54246
54247             operation.available = function() {
54248                 return vertices.length === 1;
54249             };
54250
54251
54252             operation.disabled = function() {
54253                 var reason = action.disabled(context.graph());
54254                 if (reason) {
54255                     return reason;
54256                 } else if (selectedIDs.some(context.hasHiddenConnections)) {
54257                     return 'connected_to_hidden';
54258                 }
54259
54260                 return false;
54261             };
54262
54263
54264             operation.tooltip = function() {
54265                 var disable = operation.disabled();
54266                 if (disable) {
54267                     return _t('operations.split.' + disable);
54268                 } else if (ways.length === 1) {
54269                     return _t('operations.split.description.' + context.graph().geometry(ways[0].id));
54270                 } else {
54271                     return _t('operations.split.description.multiple');
54272                 }
54273             };
54274
54275
54276             operation.annotation = function() {
54277                 return ways.length === 1 ?
54278                     _t('operations.split.annotation.' + context.graph().geometry(ways[0].id)) :
54279                     _t('operations.split.annotation.multiple', { n: ways.length });
54280             };
54281
54282
54283             operation.id = 'split';
54284             operation.keys = [_t('operations.split.key')];
54285             operation.title = _t('operations.split.title');
54286             operation.behavior = behaviorOperation(context).which(operation);
54287
54288             return operation;
54289         }
54290
54291         function operationStraighten(context, selectedIDs) {
54292             var _wayIDs = selectedIDs.filter(function(id) { return id.charAt(0) === 'w'; });
54293             var _nodeIDs = selectedIDs.filter(function(id) { return id.charAt(0) === 'n'; });
54294             var _amount = ((_wayIDs.length ? _wayIDs : _nodeIDs).length === 1 ? 'single' : 'multiple');
54295
54296             var _nodes = utilGetAllNodes(selectedIDs, context.graph());
54297             var _coords = _nodes.map(function(n) { return n.loc; });
54298             var _extent = utilTotalExtent(selectedIDs, context.graph());
54299             var _action = chooseAction();
54300             var _geometry;
54301
54302
54303             function chooseAction() {
54304                 // straighten selected nodes
54305                 if (_wayIDs.length === 0 && _nodeIDs.length > 2) {
54306                     _geometry = 'points';
54307                     return actionStraightenNodes(_nodeIDs, context.projection);
54308
54309                 // straighten selected ways (possibly between range of 2 selected nodes)
54310                 } else if (_wayIDs.length > 0 && (_nodeIDs.length === 0 || _nodeIDs.length === 2)) {
54311                     var startNodeIDs = [];
54312                     var endNodeIDs = [];
54313
54314                     for (var i = 0; i < selectedIDs.length; i++) {
54315                         var entity = context.entity(selectedIDs[i]);
54316                         if (entity.type === 'node') {
54317                             continue;
54318                         } else if (entity.type !== 'way' || entity.isClosed()) {
54319                             return null;  // exit early, can't straighten these
54320                         }
54321
54322                         startNodeIDs.push(entity.first());
54323                         endNodeIDs.push(entity.last());
54324                     }
54325
54326                     // Remove duplicate end/startNodeIDs (duplicate nodes cannot be at the line end)
54327                     startNodeIDs = startNodeIDs.filter(function(n) {
54328                         return startNodeIDs.indexOf(n) === startNodeIDs.lastIndexOf(n);
54329                     });
54330                     endNodeIDs = endNodeIDs.filter(function(n) {
54331                         return endNodeIDs.indexOf(n) === endNodeIDs.lastIndexOf(n);
54332                     });
54333
54334                     // Ensure all ways are connected (i.e. only 2 unique endpoints/startpoints)
54335                     if (utilArrayDifference(startNodeIDs, endNodeIDs).length +
54336                         utilArrayDifference(endNodeIDs, startNodeIDs).length !== 2) return null;
54337
54338                     // Ensure path contains at least 3 unique nodes
54339                     var wayNodeIDs = utilGetAllNodes(_wayIDs, context.graph())
54340                         .map(function(node) { return node.id; });
54341                     if (wayNodeIDs.length <= 2) return null;
54342
54343                     // If range of 2 selected nodes is supplied, ensure nodes lie on the selected path
54344                     if (_nodeIDs.length === 2 && (
54345                         wayNodeIDs.indexOf(_nodeIDs[0]) === -1 || wayNodeIDs.indexOf(_nodeIDs[1]) === -1
54346                     )) return null;
54347
54348                     if (_nodeIDs.length) {
54349                         // If we're only straightenting between two points, we only need that extent visible
54350                         _extent = utilTotalExtent(_nodeIDs, context.graph());
54351                     }
54352
54353                     _geometry = _wayIDs.length === 1 ? 'line' : 'lines';
54354                     return actionStraightenWay(selectedIDs, context.projection);
54355                 }
54356
54357                 return null;
54358             }
54359
54360
54361             function operation() {
54362                 if (!_action) return;
54363
54364                 context.perform(_action, operation.annotation());
54365
54366                 window.setTimeout(function() {
54367                     context.validator().validate();
54368                 }, 300);  // after any transition
54369             }
54370
54371
54372             operation.available = function() {
54373                 return Boolean(_action);
54374             };
54375
54376
54377             operation.disabled = function() {
54378                 var reason = _action.disabled(context.graph());
54379                 if (reason) {
54380                     return reason;
54381                 } else if (_extent.percentContainedIn(context.map().extent()) < 0.8) {
54382                     return 'too_large';
54383                 } else if (someMissing()) {
54384                     return 'not_downloaded';
54385                 } else if (selectedIDs.some(context.hasHiddenConnections)) {
54386                     return 'connected_to_hidden';
54387                 }
54388
54389                 return false;
54390
54391
54392                 function someMissing() {
54393                     if (context.inIntro()) return false;
54394                     var osm = context.connection();
54395                     if (osm) {
54396                         var missing = _coords.filter(function(loc) { return !osm.isDataLoaded(loc); });
54397                         if (missing.length) {
54398                             missing.forEach(function(loc) { context.loadTileAtLoc(loc); });
54399                             return true;
54400                         }
54401                     }
54402                     return false;
54403                 }
54404             };
54405
54406
54407             operation.tooltip = function() {
54408                 var disable = operation.disabled();
54409                 return disable ?
54410                     _t('operations.straighten.' + disable + '.' + _amount) :
54411                     _t('operations.straighten.description.' + _geometry);
54412             };
54413
54414
54415             operation.annotation = function() {
54416                 return _t('operations.straighten.annotation.' + _geometry);
54417             };
54418
54419
54420             operation.id = 'straighten';
54421             operation.keys = [_t('operations.straighten.key')];
54422             operation.title = _t('operations.straighten.title');
54423             operation.behavior = behaviorOperation(context).which(operation);
54424
54425             return operation;
54426         }
54427
54428         var Operations = /*#__PURE__*/Object.freeze({
54429                 __proto__: null,
54430                 operationCircularize: operationCircularize,
54431                 operationContinue: operationContinue,
54432                 operationCopy: operationCopy,
54433                 operationDelete: operationDelete,
54434                 operationDisconnect: operationDisconnect,
54435                 operationDowngrade: operationDowngrade,
54436                 operationExtract: operationExtract,
54437                 operationMerge: operationMerge,
54438                 operationMove: operationMove,
54439                 operationOrthogonalize: operationOrthogonalize,
54440                 operationPaste: operationPaste,
54441                 operationReflectShort: operationReflectShort,
54442                 operationReflectLong: operationReflectLong,
54443                 operationReverse: operationReverse,
54444                 operationRotate: operationRotate,
54445                 operationSplit: operationSplit,
54446                 operationStraighten: operationStraighten
54447         });
54448
54449         var _relatedParent;
54450
54451
54452         function modeSelect(context, selectedIDs) {
54453             var mode = {
54454                 id: 'select',
54455                 button: 'browse'
54456             };
54457
54458             var keybinding = utilKeybinding('select');
54459
54460             var _breatheBehavior = behaviorBreathe();
54461             var _modeDragNode = modeDragNode(context);
54462             var _selectBehavior;
54463             var _behaviors = [];
54464
54465             var _operations = [];
54466             var _newFeature = false;
54467             var _follow = false;
54468
54469
54470             function singular() {
54471                 if (selectedIDs && selectedIDs.length === 1) {
54472                     return context.hasEntity(selectedIDs[0]);
54473                 }
54474             }
54475
54476             function selectedEntities() {
54477                 return selectedIDs.map(function(id) {
54478                     return context.hasEntity(id);
54479                 }).filter(Boolean);
54480             }
54481
54482
54483             function checkSelectedIDs() {
54484                 var ids = [];
54485                 if (Array.isArray(selectedIDs)) {
54486                     ids = selectedIDs.filter(function(id) {
54487                         return context.hasEntity(id);
54488                     });
54489                 }
54490
54491                 if (!ids.length) {
54492                     context.enter(modeBrowse(context));
54493                     return false;
54494                 } else if ((selectedIDs.length > 1 && ids.length === 1) ||
54495                     (selectedIDs.length === 1 && ids.length > 1)) {
54496                     // switch between single- and multi-select UI
54497                     context.enter(modeSelect(context, ids));
54498                     return false;
54499                 }
54500
54501                 selectedIDs = ids;
54502                 return true;
54503             }
54504
54505
54506             // find the common parent ways for nextVertex, previousVertex
54507             function commonParents() {
54508                 var graph = context.graph();
54509                 var commonParents = [];
54510
54511                 for (var i = 0; i < selectedIDs.length; i++) {
54512                     var entity = context.hasEntity(selectedIDs[i]);
54513                     if (!entity || entity.geometry(graph) !== 'vertex') {
54514                         return [];  // selection includes some not vertexes
54515                     }
54516
54517                     var currParents = graph.parentWays(entity).map(function(w) { return w.id; });
54518                     if (!commonParents.length) {
54519                         commonParents = currParents;
54520                         continue;
54521                     }
54522
54523                     commonParents = utilArrayIntersection(commonParents, currParents);
54524                     if (!commonParents.length) {
54525                         return [];
54526                     }
54527                 }
54528
54529                 return commonParents;
54530             }
54531
54532
54533             function singularParent() {
54534                 var parents = commonParents();
54535                 if (!parents || parents.length === 0) {
54536                     _relatedParent = null;
54537                     return null;
54538                 }
54539
54540                 // relatedParent is used when we visit a vertex with multiple
54541                 // parents, and we want to remember which parent line we started on.
54542
54543                 if (parents.length === 1) {
54544                     _relatedParent = parents[0];  // remember this parent for later
54545                     return _relatedParent;
54546                 }
54547
54548                 if (parents.indexOf(_relatedParent) !== -1) {
54549                     return _relatedParent;   // prefer the previously seen parent
54550                 }
54551
54552                 return parents[0];
54553             }
54554
54555
54556             mode.selectedIDs = function(val) {
54557                 if (!arguments.length) return selectedIDs;
54558                 selectedIDs = val;
54559                 return mode;
54560             };
54561
54562
54563             mode.zoomToSelected = function() {
54564                 context.map().zoomToEase(selectedEntities());
54565             };
54566
54567
54568             mode.newFeature = function(val) {
54569                 if (!arguments.length) return _newFeature;
54570                 _newFeature = val;
54571                 return mode;
54572             };
54573
54574
54575             mode.selectBehavior = function(val) {
54576                 if (!arguments.length) return _selectBehavior;
54577                 _selectBehavior = val;
54578                 return mode;
54579             };
54580
54581
54582             mode.follow = function(val) {
54583                 if (!arguments.length) return _follow;
54584                 _follow = val;
54585                 return mode;
54586             };
54587
54588             function loadOperations() {
54589
54590                 _operations.forEach(function(operation) {
54591                     if (operation.behavior) {
54592                         context.uninstall(operation.behavior);
54593                     }
54594                 });
54595
54596                 _operations = Object.values(Operations)
54597                     .map(function(o) { return o(context, selectedIDs); })
54598                     .filter(function(o) { return o.available() && o.id !== 'delete' && o.id !== 'downgrade' && o.id !== 'copy'; });
54599
54600                 var copyOperation = operationCopy(context, selectedIDs);
54601                 if (copyOperation.available()) {
54602                     // group copy operation with delete/downgrade
54603                     _operations.push(copyOperation);
54604                 }
54605
54606                 var downgradeOperation = operationDowngrade(context, selectedIDs);
54607                 // don't allow delete if downgrade is available
54608                 var lastOperation = !context.inIntro() && downgradeOperation.available() ? downgradeOperation : operationDelete(context, selectedIDs);
54609
54610                 _operations.push(lastOperation);
54611
54612                 _operations.forEach(function(operation) {
54613                     if (operation.behavior) {
54614                         context.install(operation.behavior);
54615                     }
54616                 });
54617
54618                 // remove any displayed menu
54619                 context.ui().closeEditMenu();
54620             }
54621
54622             mode.operations = function() {
54623                 return _operations;
54624             };
54625
54626
54627             mode.enter = function() {
54628                 if (!checkSelectedIDs()) return;
54629
54630                 context.features().forceVisible(selectedIDs);
54631
54632                 _modeDragNode.restoreSelectedIDs(selectedIDs);
54633
54634                 loadOperations();
54635
54636                 if (!_behaviors.length) {
54637                     if (!_selectBehavior) _selectBehavior = behaviorSelect(context);
54638
54639                     _behaviors = [
54640                         behaviorPaste(context),
54641                         _breatheBehavior,
54642                         behaviorHover(context).on('hover', context.ui().sidebar.hoverModeSelect),
54643                         _selectBehavior,
54644                         behaviorLasso(context),
54645                         _modeDragNode.behavior,
54646                         modeDragNote(context).behavior
54647                     ];
54648                 }
54649                 _behaviors.forEach(context.install);
54650
54651                 keybinding
54652                     .on(_t('inspector.zoom_to.key'), mode.zoomToSelected)
54653                     .on(['[', 'pgup'], previousVertex)
54654                     .on([']', 'pgdown'], nextVertex)
54655                     .on(['{', uiCmd('⌘['), 'home'], firstVertex)
54656                     .on(['}', uiCmd('⌘]'), 'end'], lastVertex)
54657                     .on(uiCmd('⇧←'), nudgeSelection([-10, 0]))
54658                     .on(uiCmd('⇧↑'), nudgeSelection([0, -10]))
54659                     .on(uiCmd('⇧→'), nudgeSelection([10, 0]))
54660                     .on(uiCmd('⇧↓'), nudgeSelection([0, 10]))
54661                     .on(uiCmd('⇧⌘←'), nudgeSelection([-100, 0]))
54662                     .on(uiCmd('⇧⌘↑'), nudgeSelection([0, -100]))
54663                     .on(uiCmd('⇧⌘→'), nudgeSelection([100, 0]))
54664                     .on(uiCmd('⇧⌘↓'), nudgeSelection([0, 100]))
54665                     .on(['\\', 'pause'], nextParent)
54666                     .on('⎋', esc, true);
54667
54668                 select(document)
54669                     .call(keybinding);
54670
54671                 context.ui().sidebar
54672                     .select(selectedIDs, _newFeature);
54673
54674                 context.history()
54675                     .on('change.select', function() {
54676                         loadOperations();
54677                         // reselect after change in case relation members were removed or added
54678                         selectElements();
54679                     })
54680                     .on('undone.select', checkSelectedIDs)
54681                     .on('redone.select', checkSelectedIDs);
54682
54683                 context.map()
54684                     .on('drawn.select', selectElements)
54685                     .on('crossEditableZoom.select', function() {
54686                         selectElements();
54687                         _breatheBehavior.restartIfNeeded(context.surface());
54688                     });
54689
54690                 context.map().doubleUpHandler()
54691                     .on('doubleUp.modeSelect', didDoubleUp);
54692
54693
54694                 selectElements();
54695
54696                 if (_follow) {
54697                     var extent = geoExtent();
54698                     var graph = context.graph();
54699                     selectedIDs.forEach(function(id) {
54700                         var entity = context.entity(id);
54701                         extent._extend(entity.extent(graph));
54702                     });
54703
54704                     var loc = extent.center();
54705                     context.map().centerEase(loc);
54706                 }
54707
54708
54709                 function nudgeSelection(delta) {
54710                     return function() {
54711                         // prevent nudging during low zoom selection
54712                         if (!context.map().withinEditableZoom()) return;
54713
54714                         var moveOp = operationMove(context, selectedIDs);
54715                         if (moveOp.disabled()) {
54716                             context.ui().flash
54717                                 .duration(4000)
54718                                 .iconName('#iD-operation-' + moveOp.id)
54719                                 .iconClass('operation disabled')
54720                                 .text(moveOp.tooltip)();
54721                         } else {
54722                             context.perform(actionMove(selectedIDs, delta, context.projection), moveOp.annotation());
54723                         }
54724                     };
54725                 }
54726
54727
54728                 function didDoubleUp(loc) {
54729                     if (!context.map().withinEditableZoom()) return;
54730
54731                     var target = select(event.target);
54732
54733                     var datum = target.datum();
54734                     var entity = datum && datum.properties && datum.properties.entity;
54735                     if (!entity) return;
54736
54737                     if (entity instanceof osmWay && target.classed('target')) {
54738                         var choice = geoChooseEdge(context.graph().childNodes(entity), loc, context.projection);
54739                         var prev = entity.nodes[choice.index - 1];
54740                         var next = entity.nodes[choice.index];
54741
54742                         context.perform(
54743                             actionAddMidpoint({ loc: choice.loc, edge: [prev, next] }, osmNode()),
54744                             _t('operations.add.annotation.vertex')
54745                         );
54746
54747                     } else if (entity.type === 'midpoint') {
54748                         context.perform(
54749                             actionAddMidpoint({ loc: entity.loc, edge: entity.edge }, osmNode()),
54750                             _t('operations.add.annotation.vertex'));
54751                     }
54752                 }
54753
54754
54755                 function selectElements() {
54756                     if (!checkSelectedIDs()) return;
54757
54758                     var surface = context.surface();
54759
54760                     surface.selectAll('.selected-member')
54761                         .classed('selected-member', false);
54762
54763                     surface.selectAll('.selected')
54764                         .classed('selected', false);
54765
54766                     surface.selectAll('.related')
54767                         .classed('related', false);
54768
54769                     singularParent();
54770                     if (_relatedParent) {
54771                         surface.selectAll(utilEntitySelector([_relatedParent]))
54772                             .classed('related', true);
54773                     }
54774
54775                     if (context.map().withinEditableZoom()) {
54776                         // Apply selection styling if not in wide selection
54777
54778                         surface
54779                             .selectAll(utilDeepMemberSelector(selectedIDs, context.graph(), true /* skipMultipolgonMembers */))
54780                             .classed('selected-member', true);
54781                         surface
54782                             .selectAll(utilEntityOrDeepMemberSelector(selectedIDs, context.graph()))
54783                             .classed('selected', true);
54784                     }
54785
54786                 }
54787
54788
54789                 function esc() {
54790                     if (context.container().select('.combobox').size()) return;
54791                     context.enter(modeBrowse(context));
54792                 }
54793
54794
54795                 function firstVertex() {
54796                     event.preventDefault();
54797                     var entity = singular();
54798                     var parent = singularParent();
54799                     var way;
54800
54801                     if (entity && entity.type === 'way') {
54802                         way = entity;
54803                     } else if (parent) {
54804                         way = context.entity(parent);
54805                     }
54806
54807                     if (way) {
54808                         context.enter(
54809                             modeSelect(context, [way.first()]).follow(true)
54810                         );
54811                     }
54812                 }
54813
54814
54815                 function lastVertex() {
54816                     event.preventDefault();
54817                     var entity = singular();
54818                     var parent = singularParent();
54819                     var way;
54820
54821                     if (entity && entity.type === 'way') {
54822                         way = entity;
54823                     } else if (parent) {
54824                         way = context.entity(parent);
54825                     }
54826
54827                     if (way) {
54828                         context.enter(
54829                             modeSelect(context, [way.last()]).follow(true)
54830                         );
54831                     }
54832                 }
54833
54834
54835                 function previousVertex() {
54836                     event.preventDefault();
54837                     var parent = singularParent();
54838                     if (!parent) return;
54839
54840                     var way = context.entity(parent);
54841                     var length = way.nodes.length;
54842                     var curr = way.nodes.indexOf(selectedIDs[0]);
54843                     var index = -1;
54844
54845                     if (curr > 0) {
54846                         index = curr - 1;
54847                     } else if (way.isClosed()) {
54848                         index = length - 2;
54849                     }
54850
54851                     if (index !== -1) {
54852                         context.enter(
54853                             modeSelect(context, [way.nodes[index]]).follow(true)
54854                         );
54855                     }
54856                 }
54857
54858
54859                 function nextVertex() {
54860                     event.preventDefault();
54861                     var parent = singularParent();
54862                     if (!parent) return;
54863
54864                     var way = context.entity(parent);
54865                     var length = way.nodes.length;
54866                     var curr = way.nodes.indexOf(selectedIDs[0]);
54867                     var index = -1;
54868
54869                     if (curr < length - 1) {
54870                         index = curr + 1;
54871                     } else if (way.isClosed()) {
54872                         index = 0;
54873                     }
54874
54875                     if (index !== -1) {
54876                         context.enter(
54877                             modeSelect(context, [way.nodes[index]]).follow(true)
54878                         );
54879                     }
54880                 }
54881
54882
54883                 function nextParent() {
54884                     event.preventDefault();
54885                     var parents = commonParents();
54886                     if (!parents || parents.length < 2) return;
54887
54888                     var index = parents.indexOf(_relatedParent);
54889                     if (index < 0 || index > parents.length - 2) {
54890                         _relatedParent = parents[0];
54891                     } else {
54892                         _relatedParent = parents[index + 1];
54893                     }
54894
54895                     var surface = context.surface();
54896                     surface.selectAll('.related')
54897                         .classed('related', false);
54898
54899                     if (_relatedParent) {
54900                         surface.selectAll(utilEntitySelector([_relatedParent]))
54901                             .classed('related', true);
54902                     }
54903                 }
54904             };
54905
54906
54907             mode.exit = function() {
54908
54909                 _newFeature = false;
54910
54911                 _operations.forEach(function(operation) {
54912                     if (operation.behavior) {
54913                         context.uninstall(operation.behavior);
54914                     }
54915                 });
54916                 _operations = [];
54917
54918                 _behaviors.forEach(context.uninstall);
54919
54920                 select(document)
54921                     .call(keybinding.unbind);
54922
54923                 context.ui().closeEditMenu();
54924
54925                 context.history()
54926                     .on('change.select', null)
54927                     .on('undone.select', null)
54928                     .on('redone.select', null);
54929
54930                 var surface = context.surface();
54931
54932                 surface
54933                     .selectAll('.selected-member')
54934                     .classed('selected-member', false);
54935
54936                 surface
54937                     .selectAll('.selected')
54938                     .classed('selected', false);
54939
54940                 surface
54941                     .selectAll('.highlighted')
54942                     .classed('highlighted', false);
54943
54944                 surface
54945                     .selectAll('.related')
54946                     .classed('related', false);
54947
54948                 context.map().on('drawn.select', null);
54949                 context.ui().sidebar.hide();
54950                 context.features().forceVisible([]);
54951
54952                 var entity = singular();
54953                 if (_newFeature && entity && entity.type === 'relation' &&
54954                     // no tags
54955                     Object.keys(entity.tags).length === 0 &&
54956                     // no parent relations
54957                     context.graph().parentRelations(entity).length === 0 &&
54958                     // no members or one member with no role
54959                     (entity.members.length === 0 || (entity.members.length === 1 && !entity.members[0].role))
54960                 ) {
54961                     // the user added this relation but didn't edit it at all, so just delete it
54962                     var deleteAction = actionDeleteRelation(entity.id, true /* don't delete untagged members */);
54963                     context.perform(deleteAction, _t('operations.delete.annotation.relation'));
54964                 }
54965             };
54966
54967
54968             return mode;
54969         }
54970
54971         function uiLasso(context) {
54972             var group, polygon;
54973
54974             lasso.coordinates = [];
54975
54976             function lasso(selection) {
54977                 context.container()
54978                     .classed('lasso', true);
54979
54980                 group = selection
54981                     .append('g')
54982                     .attr('class', 'lasso hide');
54983
54984                 polygon = group
54985                     .append('path')
54986                     .attr('class', 'lasso-path');
54987
54988                 group
54989                     .call(uiToggle(true));
54990             }
54991
54992
54993             function draw() {
54994                 if (polygon) {
54995                     polygon.data([lasso.coordinates])
54996                         .attr('d', function(d) { return 'M' + d.join(' L') + ' Z'; });
54997                 }
54998             }
54999
55000
55001             lasso.extent = function () {
55002                 return lasso.coordinates.reduce(function(extent, point) {
55003                     return extent.extend(geoExtent(point));
55004                 }, geoExtent());
55005             };
55006
55007
55008             lasso.p = function(_) {
55009                 if (!arguments.length) return lasso;
55010                 lasso.coordinates.push(_);
55011                 draw();
55012                 return lasso;
55013             };
55014
55015
55016             lasso.close = function() {
55017                 if (group) {
55018                     group.call(uiToggle(false, function() {
55019                         select(this).remove();
55020                     }));
55021                 }
55022                 context.container().classed('lasso', false);
55023             };
55024
55025
55026             return lasso;
55027         }
55028
55029         function behaviorLasso(context) {
55030
55031             // use pointer events on supported platforms; fallback to mouse events
55032             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
55033
55034             var behavior = function(selection) {
55035                 var lasso;
55036
55037
55038                 function pointerdown() {
55039                     var button = 0;  // left
55040                     if (event.button === button && event.shiftKey === true) {
55041                         lasso = null;
55042
55043                         select(window)
55044                             .on(_pointerPrefix + 'move.lasso', pointermove)
55045                             .on(_pointerPrefix + 'up.lasso', pointerup);
55046
55047                         event.stopPropagation();
55048                     }
55049                 }
55050
55051
55052                 function pointermove() {
55053                     if (!lasso) {
55054                         lasso = uiLasso(context);
55055                         context.surface().call(lasso);
55056                     }
55057
55058                     lasso.p(context.map().mouse());
55059                 }
55060
55061
55062                 function normalize(a, b) {
55063                     return [
55064                         [Math.min(a[0], b[0]), Math.min(a[1], b[1])],
55065                         [Math.max(a[0], b[0]), Math.max(a[1], b[1])]
55066                     ];
55067                 }
55068
55069
55070                 function lassoed() {
55071                     if (!lasso) return [];
55072
55073                     var graph = context.graph();
55074                     var limitToNodes;
55075
55076                     if (context.map().editableDataEnabled(true /* skipZoomCheck */) && context.map().isInWideSelection()) {
55077                         // only select from the visible nodes
55078                         limitToNodes = new Set(utilGetAllNodes(context.selectedIDs(), graph));
55079                     } else if (!context.map().editableDataEnabled()) {
55080                         return [];
55081                     }
55082
55083                     var bounds = lasso.extent().map(context.projection.invert);
55084                     var extent = geoExtent(normalize(bounds[0], bounds[1]));
55085
55086                     var intersects = context.history().intersects(extent).filter(function(entity) {
55087                         return entity.type === 'node' &&
55088                             (!limitToNodes || limitToNodes.has(entity)) &&
55089                             geoPointInPolygon(context.projection(entity.loc), lasso.coordinates) &&
55090                             !context.features().isHidden(entity, graph, entity.geometry(graph));
55091                     });
55092
55093                     // sort the lassoed nodes as best we can
55094                     intersects.sort(function(node1, node2) {
55095                         var parents1 = graph.parentWays(node1);
55096                         var parents2 = graph.parentWays(node2);
55097                         if (parents1.length && parents2.length) {
55098                             // both nodes are vertices
55099
55100                             var sharedParents = utilArrayIntersection(parents1, parents2);
55101                             if (sharedParents.length) {
55102                                 var sharedParentNodes = sharedParents[0].nodes;
55103                                 // vertices are members of the same way; sort them in their listed order
55104                                 return sharedParentNodes.indexOf(node1.id) -
55105                                     sharedParentNodes.indexOf(node2.id);
55106                             } else {
55107                                 // vertices do not share a way; group them by their respective parent ways
55108                                 return parseFloat(parents1[0].id.slice(1)) -
55109                                     parseFloat(parents2[0].id.slice(1));
55110                             }
55111
55112                         } else if (parents1.length || parents2.length) {
55113                             // only one node is a vertex; sort standalone points before vertices
55114                             return parents1.length - parents2.length;
55115                         }
55116                         // both nodes are standalone points; sort left to right
55117                         return node1.loc[0] - node2.loc[0];
55118                     });
55119
55120                     return intersects.map(function(entity) { return entity.id; });
55121                 }
55122
55123
55124                 function pointerup() {
55125                     select(window)
55126                         .on(_pointerPrefix + 'move.lasso', null)
55127                         .on(_pointerPrefix + 'up.lasso', null);
55128
55129                     if (!lasso) return;
55130
55131                     var ids = lassoed();
55132                     lasso.close();
55133
55134                     if (ids.length) {
55135                         context.enter(modeSelect(context, ids));
55136                     }
55137                 }
55138
55139                 selection
55140                     .on(_pointerPrefix + 'down.lasso', pointerdown);
55141             };
55142
55143
55144             behavior.off = function(selection) {
55145                 selection.on(_pointerPrefix + 'down.lasso', null);
55146             };
55147
55148
55149             return behavior;
55150         }
55151
55152         function modeBrowse(context) {
55153             var mode = {
55154                 button: 'browse',
55155                 id: 'browse',
55156                 title: _t('modes.browse.title'),
55157                 description: _t('modes.browse.description')
55158             };
55159             var sidebar;
55160
55161             var _selectBehavior;
55162             var _behaviors = [];
55163
55164
55165             mode.selectBehavior = function(val) {
55166                 if (!arguments.length) return _selectBehavior;
55167                 _selectBehavior = val;
55168                 return mode;
55169             };
55170
55171
55172             mode.enter = function() {
55173                 if (!_behaviors.length) {
55174                     if (!_selectBehavior) _selectBehavior = behaviorSelect(context);
55175                     _behaviors = [
55176                         behaviorPaste(context),
55177                         behaviorHover(context).on('hover', context.ui().sidebar.hover),
55178                         _selectBehavior,
55179                         behaviorLasso(context),
55180                         modeDragNode(context).behavior,
55181                         modeDragNote(context).behavior
55182                     ];
55183                 }
55184                 _behaviors.forEach(context.install);
55185
55186                 // Get focus on the body.
55187                 if (document.activeElement && document.activeElement.blur) {
55188                     document.activeElement.blur();
55189                 }
55190
55191                 if (sidebar) {
55192                     context.ui().sidebar.show(sidebar);
55193                 } else {
55194                     context.ui().sidebar.select(null);
55195                 }
55196             };
55197
55198
55199             mode.exit = function() {
55200                 context.ui().sidebar.hover.cancel();
55201                 _behaviors.forEach(context.uninstall);
55202
55203                 if (sidebar) {
55204                     context.ui().sidebar.hide();
55205                 }
55206             };
55207
55208
55209             mode.sidebar = function(_) {
55210                 if (!arguments.length) return sidebar;
55211                 sidebar = _;
55212                 return mode;
55213             };
55214
55215
55216             mode.operations = function() {
55217                 return [operationPaste(context)];
55218             };
55219
55220
55221             return mode;
55222         }
55223
55224         function behaviorAddWay(context) {
55225             var dispatch$1 = dispatch('start', 'startFromWay', 'startFromNode');
55226             var draw = behaviorDraw(context);
55227
55228             function behavior(surface) {
55229                 draw.on('click', function() { dispatch$1.apply('start', this, arguments); })
55230                     .on('clickWay', function() { dispatch$1.apply('startFromWay', this, arguments); })
55231                     .on('clickNode', function() { dispatch$1.apply('startFromNode', this, arguments); })
55232                     .on('cancel', behavior.cancel)
55233                     .on('finish', behavior.cancel);
55234
55235                 context.map()
55236                     .dblclickZoomEnable(false);
55237
55238                 surface.call(draw);
55239             }
55240
55241
55242             behavior.off = function(surface) {
55243                 surface.call(draw.off);
55244             };
55245
55246
55247             behavior.cancel = function() {
55248                 window.setTimeout(function() {
55249                     context.map().dblclickZoomEnable(true);
55250                 }, 1000);
55251
55252                 context.enter(modeBrowse(context));
55253             };
55254
55255
55256             return utilRebind(behavior, dispatch$1, 'on');
55257         }
55258
55259         function behaviorHash(context) {
55260
55261             // cached window.location.hash
55262             var _cachedHash = null;
55263             // allowable latitude range
55264             var _latitudeLimit = 90 - 1e-8;
55265
55266             function computedHashParameters() {
55267                 var map = context.map();
55268                 var center = map.center();
55269                 var zoom = map.zoom();
55270                 var precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
55271                 var oldParams = utilObjectOmit(utilStringQs(window.location.hash),
55272                     ['comment', 'source', 'hashtags', 'walkthrough']
55273                 );
55274                 var newParams = {};
55275
55276                 delete oldParams.id;
55277                 var selected = context.selectedIDs().filter(function(id) {
55278                     return context.hasEntity(id);
55279                 });
55280                 if (selected.length) {
55281                     newParams.id = selected.join(',');
55282                 }
55283
55284                 newParams.map = zoom.toFixed(2) +
55285                     '/' + center[1].toFixed(precision) +
55286                     '/' + center[0].toFixed(precision);
55287
55288                 return Object.assign(oldParams, newParams);
55289             }
55290
55291             function computedHash() {
55292                 return '#' + utilQsString(computedHashParameters(), true);
55293             }
55294
55295             function computedTitle(includeChangeCount) {
55296
55297                 var baseTitle = context.documentTitleBase() || 'iD';
55298                 var contextual;
55299                 var changeCount;
55300                 var titleID;
55301
55302                 var selected = context.selectedIDs().filter(function(id) {
55303                     return context.hasEntity(id);
55304                 });
55305                 if (selected.length) {
55306                     var firstLabel = utilDisplayLabel(context.entity(selected[0]), context.graph());
55307                     if (selected.length > 1 ) {
55308                         contextual = _t('title.labeled_and_more', {
55309                             labeled: firstLabel,
55310                             count: (selected.length - 1).toString()
55311                         });
55312                     } else {
55313                         contextual = firstLabel;
55314                     }
55315                     titleID = 'context';
55316                 }
55317
55318                 if (includeChangeCount) {
55319                     changeCount = context.history().difference().summary().length;
55320                     if (changeCount > 0) {
55321                         titleID = contextual ? 'changes_context' : 'changes';
55322                     }
55323                 }
55324
55325                 if (titleID) {
55326                     return _t('title.format.' + titleID, {
55327                         changes: changeCount,
55328                         base: baseTitle,
55329                         context: contextual
55330                     });
55331                 }
55332
55333                 return baseTitle;
55334             }
55335
55336             function updateTitle(includeChangeCount) {
55337                 if (!context.setsDocumentTitle()) return;
55338
55339                 var newTitle = computedTitle(includeChangeCount);
55340                 if (document.title !== newTitle) {
55341                     document.title = newTitle;
55342                 }
55343             }
55344
55345             function updateHashIfNeeded() {
55346                 if (context.inIntro()) return;
55347
55348                 var latestHash = computedHash();
55349                 if (_cachedHash !== latestHash) {
55350                     _cachedHash = latestHash;
55351
55352                     // Update the URL hash without affecting the browser navigation stack,
55353                     // though unavoidably creating a browser history entry
55354                     window.history.replaceState(null, computedTitle(false /* includeChangeCount */), latestHash);
55355
55356                     // set the title we want displayed for the browser tab/window
55357                     updateTitle(true /* includeChangeCount */);
55358                 }
55359             }
55360
55361             var _throttledUpdate = throttle(updateHashIfNeeded, 500);
55362             var _throttledUpdateTitle = throttle(function() {
55363                 updateTitle(true /* includeChangeCount */);
55364             }, 500);
55365
55366             function hashchange() {
55367
55368                 // ignore spurious hashchange events
55369                 if (window.location.hash === _cachedHash) return;
55370
55371                 _cachedHash = window.location.hash;
55372
55373                 var q = utilStringQs(_cachedHash);
55374                 var mapArgs = (q.map || '').split('/').map(Number);
55375
55376                 if (mapArgs.length < 3 || mapArgs.some(isNaN)) {
55377                     // replace bogus hash
55378                     updateHashIfNeeded();
55379
55380                 } else {
55381                     // don't update if the new hash already reflects the state of iD
55382                     if (_cachedHash === computedHash()) return;
55383
55384                     var mode = context.mode();
55385
55386                     context.map().centerZoom([mapArgs[2], Math.min(_latitudeLimit, Math.max(-_latitudeLimit, mapArgs[1]))], mapArgs[0]);
55387
55388                     if (q.id) {
55389                         var ids = q.id.split(',').filter(function(id) {
55390                             return context.hasEntity(id);
55391                         });
55392                         var skip = mode && mode.id === 'select' && utilArrayIdentical(mode.selectedIDs(), ids);
55393                         if (ids.length && !skip) {
55394                             context.enter(modeSelect(context, ids));
55395                             return;
55396                         }
55397                     }
55398
55399                     var center = context.map().center();
55400                     var dist = geoSphericalDistance(center, [mapArgs[2], mapArgs[1]]);
55401                     var maxdist = 500;
55402
55403                     // Don't allow the hash location to change too much while drawing
55404                     // This can happen if the user accidently hit the back button.  #3996
55405                     if (mode && mode.id.match(/^draw/) !== null && dist > maxdist) {
55406                         context.enter(modeBrowse(context));
55407                         return;
55408                     }
55409                 }
55410             }
55411
55412             function behavior() {
55413                 context.map()
55414                     .on('move.behaviorHash', _throttledUpdate);
55415
55416                 context.history()
55417                     .on('change.behaviorHash', _throttledUpdateTitle);
55418
55419                 context
55420                     .on('enter.behaviorHash', _throttledUpdate);
55421
55422                 select(window)
55423                     .on('hashchange.behaviorHash', hashchange);
55424
55425                 if (window.location.hash) {
55426                     var q = utilStringQs(window.location.hash);
55427
55428                     if (q.id) {
55429                         //if (!context.history().hasRestorableChanges()) {
55430                             // targeting specific features: download, select, and zoom to them
55431                             context.zoomToEntity(q.id.split(',')[0], !q.map);
55432                         //}
55433                     }
55434
55435                     if (q.walkthrough === 'true') {
55436                         behavior.startWalkthrough = true;
55437                     }
55438
55439                     if (q.map) {
55440                         behavior.hadHash = true;
55441                     }
55442
55443                     hashchange();
55444
55445                     updateTitle(false);
55446                 }
55447             }
55448
55449             behavior.off = function() {
55450                 _throttledUpdate.cancel();
55451                 _throttledUpdateTitle.cancel();
55452
55453                 context.map()
55454                     .on('move.behaviorHash', null);
55455
55456                 context
55457                     .on('enter.behaviorHash', null);
55458
55459                 select(window)
55460                     .on('hashchange.behaviorHash', null);
55461
55462                 window.location.hash = '';
55463             };
55464
55465             return behavior;
55466         }
55467
55468         /*
55469             iD.coreDifference represents the difference between two graphs.
55470             It knows how to calculate the set of entities that were
55471             created, modified, or deleted, and also contains the logic
55472             for recursively extending a difference to the complete set
55473             of entities that will require a redraw, taking into account
55474             child and parent relationships.
55475          */
55476         function coreDifference(base, head) {
55477             var _changes = {};
55478             var _didChange = {};  // 'addition', 'deletion', 'geometry', 'properties'
55479             var _diff = {};
55480
55481             function checkEntityID(id) {
55482                 var h = head.entities[id];
55483                 var b = base.entities[id];
55484
55485                 if (h === b) return;
55486                 if (_changes[id]) return;
55487
55488                 if (!h && b) {
55489                     _changes[id] = { base: b, head: h };
55490                     _didChange.deletion = true;
55491                     return;
55492                 }
55493                 if (h && !b) {
55494                     _changes[id] = { base: b, head: h };
55495                     _didChange.addition = true;
55496                     return;
55497                 }
55498
55499                 if (h && b) {
55500                     if (h.members && b.members && !fastDeepEqual(h.members, b.members)) {
55501                         _changes[id] = { base: b, head: h };
55502                         _didChange.geometry = true;
55503                         _didChange.properties = true;
55504                         return;
55505                     }
55506                     if (h.loc && b.loc && !geoVecEqual(h.loc, b.loc)) {
55507                         _changes[id] = { base: b, head: h };
55508                         _didChange.geometry = true;
55509                     }
55510                     if (h.nodes && b.nodes && !fastDeepEqual(h.nodes, b.nodes)) {
55511                         _changes[id] = { base: b, head: h };
55512                         _didChange.geometry = true;
55513                     }
55514                     if (h.tags && b.tags && !fastDeepEqual(h.tags, b.tags)) {
55515                         _changes[id] = { base: b, head: h };
55516                         _didChange.properties = true;
55517                     }
55518                 }
55519             }
55520
55521             function load() {
55522                 // HOT CODE: there can be many thousands of downloaded entities, so looping
55523                 // through them all can become a performance bottleneck. Optimize by
55524                 // resolving duplicates and using a basic `for` loop
55525                 var ids = utilArrayUniq(Object.keys(head.entities).concat(Object.keys(base.entities)));
55526                 for (var i = 0; i < ids.length; i++) {
55527                     checkEntityID(ids[i]);
55528                 }
55529             }
55530             load();
55531
55532
55533             _diff.length = function length() {
55534                 return Object.keys(_changes).length;
55535             };
55536
55537
55538             _diff.changes = function changes() {
55539                 return _changes;
55540             };
55541
55542             _diff.didChange = _didChange;
55543
55544
55545             // pass true to include affected relation members
55546             _diff.extantIDs = function extantIDs(includeRelMembers) {
55547                 var result = new Set();
55548                 Object.keys(_changes).forEach(function(id) {
55549                     if (_changes[id].head) {
55550                         result.add(id);
55551                     }
55552
55553                     var h = _changes[id].head;
55554                     var b = _changes[id].base;
55555                     var entity = h || b;
55556
55557                     if (includeRelMembers && entity.type === 'relation') {
55558                         var mh = h ? h.members.map(function(m) { return m.id; }) : [];
55559                         var mb = b ? b.members.map(function(m) { return m.id; }) : [];
55560                         utilArrayUnion(mh, mb).forEach(function(memberID) {
55561                             if (head.hasEntity(memberID)) {
55562                                 result.add(memberID);
55563                             }
55564                         });
55565                     }
55566                 });
55567
55568                 return Array.from(result);
55569             };
55570
55571
55572             _diff.modified = function modified() {
55573                 var result = [];
55574                 Object.values(_changes).forEach(function(change) {
55575                     if (change.base && change.head) {
55576                         result.push(change.head);
55577                     }
55578                 });
55579                 return result;
55580             };
55581
55582
55583             _diff.created = function created() {
55584                 var result = [];
55585                 Object.values(_changes).forEach(function(change) {
55586                     if (!change.base && change.head) {
55587                         result.push(change.head);
55588                     }
55589                 });
55590                 return result;
55591             };
55592
55593
55594             _diff.deleted = function deleted() {
55595                 var result = [];
55596                 Object.values(_changes).forEach(function(change) {
55597                     if (change.base && !change.head) {
55598                         result.push(change.base);
55599                     }
55600                 });
55601                 return result;
55602             };
55603
55604
55605             _diff.summary = function summary() {
55606                 var relevant = {};
55607
55608                 var keys = Object.keys(_changes);
55609                 for (var i = 0; i < keys.length; i++) {
55610                     var change = _changes[keys[i]];
55611
55612                     if (change.head && change.head.geometry(head) !== 'vertex') {
55613                         addEntity(change.head, head, change.base ? 'modified' : 'created');
55614
55615                     } else if (change.base && change.base.geometry(base) !== 'vertex') {
55616                         addEntity(change.base, base, 'deleted');
55617
55618                     } else if (change.base && change.head) { // modified vertex
55619                         var moved    = !fastDeepEqual(change.base.loc,  change.head.loc);
55620                         var retagged = !fastDeepEqual(change.base.tags, change.head.tags);
55621
55622                         if (moved) {
55623                             addParents(change.head);
55624                         }
55625
55626                         if (retagged || (moved && change.head.hasInterestingTags())) {
55627                             addEntity(change.head, head, 'modified');
55628                         }
55629
55630                     } else if (change.head && change.head.hasInterestingTags()) { // created vertex
55631                         addEntity(change.head, head, 'created');
55632
55633                     } else if (change.base && change.base.hasInterestingTags()) { // deleted vertex
55634                         addEntity(change.base, base, 'deleted');
55635                     }
55636                 }
55637
55638                 return Object.values(relevant);
55639
55640
55641                 function addEntity(entity, graph, changeType) {
55642                     relevant[entity.id] = {
55643                         entity: entity,
55644                         graph: graph,
55645                         changeType: changeType
55646                     };
55647                 }
55648
55649                 function addParents(entity) {
55650                     var parents = head.parentWays(entity);
55651                     for (var j = parents.length - 1; j >= 0; j--) {
55652                         var parent = parents[j];
55653                         if (!(parent.id in relevant)) {
55654                             addEntity(parent, head, 'modified');
55655                         }
55656                     }
55657                 }
55658             };
55659
55660
55661             // returns complete set of entities that require a redraw
55662             //  (optionally within given `extent`)
55663             _diff.complete = function complete(extent) {
55664                 var result = {};
55665                 var id, change;
55666
55667                 for (id in _changes) {
55668                     change = _changes[id];
55669
55670                     var h = change.head;
55671                     var b = change.base;
55672                     var entity = h || b;
55673                     var i;
55674
55675                     if (extent &&
55676                         (!h || !h.intersects(extent, head)) &&
55677                         (!b || !b.intersects(extent, base)))
55678                         continue;
55679
55680                     result[id] = h;
55681
55682                     if (entity.type === 'way') {
55683                         var nh = h ? h.nodes : [];
55684                         var nb = b ? b.nodes : [];
55685                         var diff;
55686
55687                         diff = utilArrayDifference(nh, nb);
55688                         for (i = 0; i < diff.length; i++) {
55689                             result[diff[i]] = head.hasEntity(diff[i]);
55690                         }
55691
55692                         diff = utilArrayDifference(nb, nh);
55693                         for (i = 0; i < diff.length; i++) {
55694                             result[diff[i]] = head.hasEntity(diff[i]);
55695                         }
55696                     }
55697
55698                     if (entity.type === 'relation' && entity.isMultipolygon()) {
55699                         var mh = h ? h.members.map(function(m) { return m.id; }) : [];
55700                         var mb = b ? b.members.map(function(m) { return m.id; }) : [];
55701                         var ids = utilArrayUnion(mh, mb);
55702                         for (i = 0; i < ids.length; i++) {
55703                             var member = head.hasEntity(ids[i]);
55704                             if (!member) continue;   // not downloaded
55705                             if (extent && !member.intersects(extent, head)) continue;   // not visible
55706                             result[ids[i]] = member;
55707                         }
55708                     }
55709
55710                     addParents(head.parentWays(entity), result);
55711                     addParents(head.parentRelations(entity), result);
55712                 }
55713
55714                 return result;
55715
55716
55717                 function addParents(parents, result) {
55718                     for (var i = 0; i < parents.length; i++) {
55719                         var parent = parents[i];
55720                         if (parent.id in result) continue;
55721
55722                         result[parent.id] = parent;
55723                         addParents(head.parentRelations(parent), result);
55724                     }
55725                 }
55726             };
55727
55728
55729             return _diff;
55730         }
55731
55732         function coreTree(head) {
55733             // tree for entities
55734             var _rtree = new RBush();
55735             var _bboxes = {};
55736
55737             // maintain a separate tree for granular way segments
55738             var _segmentsRTree = new RBush();
55739             var _segmentsBBoxes = {};
55740             var _segmentsByWayId = {};
55741
55742             var tree = {};
55743
55744
55745             function entityBBox(entity) {
55746                 var bbox = entity.extent(head).bbox();
55747                 bbox.id = entity.id;
55748                 _bboxes[entity.id] = bbox;
55749                 return bbox;
55750             }
55751
55752
55753             function segmentBBox(segment) {
55754                 var extent = segment.extent(head);
55755                 // extent can be null if the node entites aren't in the graph for some reason
55756                 if (!extent) return null;
55757
55758                 var bbox = extent.bbox();
55759                 bbox.segment = segment;
55760                 _segmentsBBoxes[segment.id] = bbox;
55761                 return bbox;
55762             }
55763
55764
55765             function removeEntity(entity) {
55766                 _rtree.remove(_bboxes[entity.id]);
55767                 delete _bboxes[entity.id];
55768
55769                 if (_segmentsByWayId[entity.id]) {
55770                     _segmentsByWayId[entity.id].forEach(function(segment) {
55771                         _segmentsRTree.remove(_segmentsBBoxes[segment.id]);
55772                         delete _segmentsBBoxes[segment.id];
55773                     });
55774                     delete _segmentsByWayId[entity.id];
55775                 }
55776             }
55777
55778
55779             function loadEntities(entities) {
55780                 _rtree.load(entities.map(entityBBox));
55781
55782                 var segments = [];
55783                 entities.forEach(function(entity) {
55784                     if (entity.segments) {
55785                         var entitySegments = entity.segments(head);
55786                         // cache these to make them easy to remove later
55787                         _segmentsByWayId[entity.id] = entitySegments;
55788                         segments = segments.concat(entitySegments);
55789                     }
55790                 });
55791                 if (segments.length) _segmentsRTree.load(segments.map(segmentBBox).filter(Boolean));
55792             }
55793
55794
55795             function updateParents(entity, insertions, memo) {
55796                 head.parentWays(entity).forEach(function(way) {
55797                     if (_bboxes[way.id]) {
55798                         removeEntity(way);
55799                         insertions[way.id] = way;
55800                     }
55801                     updateParents(way, insertions, memo);
55802                 });
55803
55804                 head.parentRelations(entity).forEach(function(relation) {
55805                     if (memo[entity.id]) return;
55806                     memo[entity.id] = true;
55807                     if (_bboxes[relation.id]) {
55808                         removeEntity(relation);
55809                         insertions[relation.id] = relation;
55810                     }
55811                     updateParents(relation, insertions, memo);
55812                 });
55813             }
55814
55815
55816             tree.rebase = function(entities, force) {
55817                 var insertions = {};
55818
55819                 for (var i = 0; i < entities.length; i++) {
55820                     var entity = entities[i];
55821                     if (!entity.visible) continue;
55822
55823                     if (head.entities.hasOwnProperty(entity.id) || _bboxes[entity.id]) {
55824                         if (!force) {
55825                             continue;
55826                         } else if (_bboxes[entity.id]) {
55827                             removeEntity(entity);
55828                         }
55829                     }
55830
55831                     insertions[entity.id] = entity;
55832                     updateParents(entity, insertions, {});
55833                 }
55834
55835                 loadEntities(Object.values(insertions));
55836
55837                 return tree;
55838             };
55839
55840
55841             function updateToGraph(graph) {
55842                 if (graph === head) return;
55843
55844                 var diff = coreDifference(head, graph);
55845
55846                 head = graph;
55847
55848                 var changed = diff.didChange;
55849                 if (!changed.addition && !changed.deletion && !changed.geometry) return;
55850
55851                 var insertions = {};
55852
55853                 if (changed.deletion) {
55854                     diff.deleted().forEach(function(entity) {
55855                         removeEntity(entity);
55856                     });
55857                 }
55858
55859                 if (changed.geometry) {
55860                     diff.modified().forEach(function(entity) {
55861                         removeEntity(entity);
55862                         insertions[entity.id] = entity;
55863                         updateParents(entity, insertions, {});
55864                     });
55865                 }
55866
55867                 if (changed.addition) {
55868                     diff.created().forEach(function(entity) {
55869                         insertions[entity.id] = entity;
55870                     });
55871                 }
55872
55873                 loadEntities(Object.values(insertions));
55874             }
55875
55876             // returns an array of entities with bounding boxes overlapping `extent` for the given `graph`
55877             tree.intersects = function(extent, graph) {
55878                 updateToGraph(graph);
55879                 return _rtree.search(extent.bbox())
55880                     .map(function(bbox) { return graph.entity(bbox.id); });
55881             };
55882
55883             // returns an array of segment objects with bounding boxes overlapping `extent` for the given `graph`
55884             tree.waySegments = function(extent, graph) {
55885                 updateToGraph(graph);
55886                 return _segmentsRTree.search(extent.bbox())
55887                     .map(function(bbox) { return bbox.segment; });
55888             };
55889
55890
55891             return tree;
55892         }
55893
55894         function uiModal(selection, blocking) {
55895           let keybinding = utilKeybinding('modal');
55896           let previous = selection.select('div.modal');
55897           let animate = previous.empty();
55898
55899           previous.transition()
55900             .duration(200)
55901             .style('opacity', 0)
55902             .remove();
55903
55904           let shaded = selection
55905             .append('div')
55906             .attr('class', 'shaded')
55907             .style('opacity', 0);
55908
55909           shaded.close = () => {
55910             shaded
55911               .transition()
55912               .duration(200)
55913               .style('opacity',0)
55914               .remove();
55915
55916             modal
55917               .transition()
55918               .duration(200)
55919               .style('top','0px');
55920
55921             select(document)
55922               .call(keybinding.unbind);
55923           };
55924
55925
55926           let modal = shaded
55927             .append('div')
55928             .attr('class', 'modal fillL');
55929
55930           if (!blocking) {
55931             shaded.on('click.remove-modal', () => {
55932               if (event.target === this) {
55933                 shaded.close();
55934               }
55935             });
55936
55937             modal
55938               .append('button')
55939               .attr('class', 'close')
55940               .on('click', shaded.close)
55941               .call(svgIcon('#iD-icon-close'));
55942
55943             keybinding
55944               .on('⌫', shaded.close)
55945               .on('⎋', shaded.close);
55946
55947             select(document)
55948               .call(keybinding);
55949           }
55950
55951           modal
55952             .append('div')
55953             .attr('class', 'content');
55954
55955           if (animate) {
55956             shaded.transition().style('opacity', 1);
55957           } else {
55958             shaded.style('opacity', 1);
55959           }
55960
55961           return shaded;
55962         }
55963
55964         function uiLoading(context) {
55965           let _modalSelection = select(null);
55966           let _message = '';
55967           let _blocking = false;
55968
55969
55970           let loading = (selection) => {
55971             _modalSelection = uiModal(selection, _blocking);
55972
55973             let loadertext = _modalSelection.select('.content')
55974               .classed('loading-modal', true)
55975               .append('div')
55976               .attr('class', 'modal-section fillL');
55977
55978             loadertext
55979               .append('img')
55980               .attr('class', 'loader')
55981               .attr('src', context.imagePath('loader-white.gif'));
55982
55983             loadertext
55984               .append('h3')
55985               .text(_message);
55986
55987             _modalSelection.select('button.close')
55988               .attr('class', 'hide');
55989
55990             return loading;
55991           };
55992
55993
55994           loading.message = function(val) {
55995             if (!arguments.length) return _message;
55996             _message = val;
55997             return loading;
55998           };
55999
56000
56001           loading.blocking = function(val) {
56002             if (!arguments.length) return _blocking;
56003             _blocking = val;
56004             return loading;
56005           };
56006
56007
56008           loading.close = () => {
56009             _modalSelection.remove();
56010           };
56011
56012
56013           loading.isShown = () => {
56014             return _modalSelection && !_modalSelection.empty() && _modalSelection.node().parentNode;
56015           };
56016
56017
56018           return loading;
56019         }
56020
56021         function coreHistory(context) {
56022             var dispatch$1 = dispatch('change', 'merge', 'restore', 'undone', 'redone');
56023             var lock = utilSessionMutex('lock');
56024
56025             // restorable if iD not open in another window/tab and a saved history exists in localStorage
56026             var _hasUnresolvedRestorableChanges = lock.lock() && !!corePreferences(getKey('saved_history'));
56027
56028             var duration = 150;
56029             var _imageryUsed = [];
56030             var _photoOverlaysUsed = [];
56031             var _checkpoints = {};
56032             var _pausedGraph;
56033             var _stack;
56034             var _index;
56035             var _tree;
56036
56037
56038             // internal _act, accepts list of actions and eased time
56039             function _act(actions, t) {
56040                 actions = Array.prototype.slice.call(actions);
56041
56042                 var annotation;
56043                 if (typeof actions[actions.length - 1] !== 'function') {
56044                     annotation = actions.pop();
56045                 }
56046
56047                 var graph = _stack[_index].graph;
56048                 for (var i = 0; i < actions.length; i++) {
56049                     graph = actions[i](graph, t);
56050                 }
56051
56052                 return {
56053                     graph: graph,
56054                     annotation: annotation,
56055                     imageryUsed: _imageryUsed,
56056                     photoOverlaysUsed: _photoOverlaysUsed,
56057                     transform: context.projection.transform(),
56058                     selectedIDs: context.selectedIDs()
56059                 };
56060             }
56061
56062
56063             // internal _perform with eased time
56064             function _perform(args, t) {
56065                 var previous = _stack[_index].graph;
56066                 _stack = _stack.slice(0, _index + 1);
56067                 var actionResult = _act(args, t);
56068                 _stack.push(actionResult);
56069                 _index++;
56070                 return change(previous);
56071             }
56072
56073
56074             // internal _replace with eased time
56075             function _replace(args, t) {
56076                 var previous = _stack[_index].graph;
56077                 // assert(_index == _stack.length - 1)
56078                 var actionResult = _act(args, t);
56079                 _stack[_index] = actionResult;
56080                 return change(previous);
56081             }
56082
56083
56084             // internal _overwrite with eased time
56085             function _overwrite(args, t) {
56086                 var previous = _stack[_index].graph;
56087                 if (_index > 0) {
56088                     _index--;
56089                     _stack.pop();
56090                 }
56091                 _stack = _stack.slice(0, _index + 1);
56092                 var actionResult = _act(args, t);
56093                 _stack.push(actionResult);
56094                 _index++;
56095                 return change(previous);
56096             }
56097
56098
56099             // determine difference and dispatch a change event
56100             function change(previous) {
56101                 var difference = coreDifference(previous, history.graph());
56102                 if (!_pausedGraph) {
56103                     dispatch$1.call('change', this, difference);
56104                 }
56105                 return difference;
56106             }
56107
56108
56109             // iD uses namespaced keys so multiple installations do not conflict
56110             function getKey(n) {
56111                 return 'iD_' + window.location.origin + '_' + n;
56112             }
56113
56114
56115             var history = {
56116
56117                 graph: function() {
56118                     return _stack[_index].graph;
56119                 },
56120
56121
56122                 tree: function() {
56123                     return _tree;
56124                 },
56125
56126
56127                 base: function() {
56128                     return _stack[0].graph;
56129                 },
56130
56131
56132                 merge: function(entities/*, extent*/) {
56133                     var stack = _stack.map(function(state) { return state.graph; });
56134                     _stack[0].graph.rebase(entities, stack, false);
56135                     _tree.rebase(entities, false);
56136
56137                     dispatch$1.call('merge', this, entities);
56138                 },
56139
56140
56141                 perform: function() {
56142                     // complete any transition already in progress
56143                     select(document).interrupt('history.perform');
56144
56145                     var transitionable = false;
56146                     var action0 = arguments[0];
56147
56148                     if (arguments.length === 1 ||
56149                         (arguments.length === 2 && (typeof arguments[1] !== 'function'))) {
56150                         transitionable = !!action0.transitionable;
56151                     }
56152
56153                     if (transitionable) {
56154                         var origArguments = arguments;
56155                         select(document)
56156                             .transition('history.perform')
56157                             .duration(duration)
56158                             .ease(linear$1)
56159                             .tween('history.tween', function() {
56160                                 return function(t) {
56161                                     if (t < 1) _overwrite([action0], t);
56162                                 };
56163                             })
56164                             .on('start', function() {
56165                                 _perform([action0], 0);
56166                             })
56167                             .on('end interrupt', function() {
56168                                 _overwrite(origArguments, 1);
56169                             });
56170
56171                     } else {
56172                         return _perform(arguments);
56173                     }
56174                 },
56175
56176
56177                 replace: function() {
56178                     select(document).interrupt('history.perform');
56179                     return _replace(arguments, 1);
56180                 },
56181
56182
56183                 // Same as calling pop and then perform
56184                 overwrite: function() {
56185                     select(document).interrupt('history.perform');
56186                     return _overwrite(arguments, 1);
56187                 },
56188
56189
56190                 pop: function(n) {
56191                     select(document).interrupt('history.perform');
56192
56193                     var previous = _stack[_index].graph;
56194                     if (isNaN(+n) || +n < 0) {
56195                         n = 1;
56196                     }
56197                     while (n-- > 0 && _index > 0) {
56198                         _index--;
56199                         _stack.pop();
56200                     }
56201                     return change(previous);
56202                 },
56203
56204
56205                 // Back to the previous annotated state or _index = 0.
56206                 undo: function() {
56207                     select(document).interrupt('history.perform');
56208
56209                     var previousStack = _stack[_index];
56210                     var previous = previousStack.graph;
56211                     while (_index > 0) {
56212                         _index--;
56213                         if (_stack[_index].annotation) break;
56214                     }
56215
56216                     dispatch$1.call('undone', this, _stack[_index], previousStack);
56217                     return change(previous);
56218                 },
56219
56220
56221                 // Forward to the next annotated state.
56222                 redo: function() {
56223                     select(document).interrupt('history.perform');
56224
56225                     var previousStack = _stack[_index];
56226                     var previous = previousStack.graph;
56227                     var tryIndex = _index;
56228                     while (tryIndex < _stack.length - 1) {
56229                         tryIndex++;
56230                         if (_stack[tryIndex].annotation) {
56231                             _index = tryIndex;
56232                             dispatch$1.call('redone', this, _stack[_index], previousStack);
56233                             break;
56234                         }
56235                     }
56236
56237                     return change(previous);
56238                 },
56239
56240
56241                 pauseChangeDispatch: function() {
56242                     if (!_pausedGraph) {
56243                         _pausedGraph = _stack[_index].graph;
56244                     }
56245                 },
56246
56247
56248                 resumeChangeDispatch: function() {
56249                     if (_pausedGraph) {
56250                         var previous = _pausedGraph;
56251                         _pausedGraph = null;
56252                         return change(previous);
56253                     }
56254                 },
56255
56256
56257                 undoAnnotation: function() {
56258                     var i = _index;
56259                     while (i >= 0) {
56260                         if (_stack[i].annotation) return _stack[i].annotation;
56261                         i--;
56262                     }
56263                 },
56264
56265
56266                 redoAnnotation: function() {
56267                     var i = _index + 1;
56268                     while (i <= _stack.length - 1) {
56269                         if (_stack[i].annotation) return _stack[i].annotation;
56270                         i++;
56271                     }
56272                 },
56273
56274
56275                 // Returns the entities from the active graph with bounding boxes
56276                 // overlapping the given `extent`.
56277                 intersects: function(extent) {
56278                     return _tree.intersects(extent, _stack[_index].graph);
56279                 },
56280
56281
56282                 difference: function() {
56283                     var base = _stack[0].graph;
56284                     var head = _stack[_index].graph;
56285                     return coreDifference(base, head);
56286                 },
56287
56288
56289                 changes: function(action) {
56290                     var base = _stack[0].graph;
56291                     var head = _stack[_index].graph;
56292
56293                     if (action) {
56294                         head = action(head);
56295                     }
56296
56297                     var difference = coreDifference(base, head);
56298
56299                     return {
56300                         modified: difference.modified(),
56301                         created: difference.created(),
56302                         deleted: difference.deleted()
56303                     };
56304                 },
56305
56306
56307                 hasChanges: function() {
56308                     return this.difference().length() > 0;
56309                 },
56310
56311
56312                 imageryUsed: function(sources) {
56313                     if (sources) {
56314                         _imageryUsed = sources;
56315                         return history;
56316                     } else {
56317                         var s = new Set();
56318                         _stack.slice(1, _index + 1).forEach(function(state) {
56319                             state.imageryUsed.forEach(function(source) {
56320                                 if (source !== 'Custom') {
56321                                     s.add(source);
56322                                 }
56323                             });
56324                         });
56325                         return Array.from(s);
56326                     }
56327                 },
56328
56329
56330                 photoOverlaysUsed: function(sources) {
56331                     if (sources) {
56332                         _photoOverlaysUsed = sources;
56333                         return history;
56334                     } else {
56335                         var s = new Set();
56336                         _stack.slice(1, _index + 1).forEach(function(state) {
56337                             if (state.photoOverlaysUsed && Array.isArray(state.photoOverlaysUsed)) {
56338                                 state.photoOverlaysUsed.forEach(function(photoOverlay) {
56339                                     s.add(photoOverlay);
56340                                 });
56341                             }
56342                         });
56343                         return Array.from(s);
56344                     }
56345                 },
56346
56347
56348                 // save the current history state
56349                 checkpoint: function(key) {
56350                     _checkpoints[key] = {
56351                         stack: _stack,
56352                         index: _index
56353                     };
56354                     return history;
56355                 },
56356
56357
56358                 // restore history state to a given checkpoint or reset completely
56359                 reset: function(key) {
56360                     if (key !== undefined && _checkpoints.hasOwnProperty(key)) {
56361                         _stack = _checkpoints[key].stack;
56362                         _index = _checkpoints[key].index;
56363                     } else {
56364                         _stack = [{graph: coreGraph()}];
56365                         _index = 0;
56366                         _tree = coreTree(_stack[0].graph);
56367                         _checkpoints = {};
56368                     }
56369                     dispatch$1.call('change');
56370                     return history;
56371                 },
56372
56373
56374                 // `toIntroGraph()` is used to export the intro graph used by the walkthrough.
56375                 //
56376                 // To use it:
56377                 //  1. Start the walkthrough.
56378                 //  2. Get to a "free editing" tutorial step
56379                 //  3. Make your edits to the walkthrough map
56380                 //  4. In your browser dev console run:
56381                 //        `id.history().toIntroGraph()`
56382                 //  5. This outputs stringified JSON to the browser console
56383                 //  6. Copy it to `data/intro_graph.json` and prettify it in your code editor
56384                 toIntroGraph: function() {
56385                     var nextID = { n: 0, r: 0, w: 0 };
56386                     var permIDs = {};
56387                     var graph = this.graph();
56388                     var baseEntities = {};
56389
56390                     // clone base entities..
56391                     Object.values(graph.base().entities).forEach(function(entity) {
56392                         var copy = copyIntroEntity(entity);
56393                         baseEntities[copy.id] = copy;
56394                     });
56395
56396                     // replace base entities with head entities..
56397                     Object.keys(graph.entities).forEach(function(id) {
56398                         var entity = graph.entities[id];
56399                         if (entity) {
56400                             var copy = copyIntroEntity(entity);
56401                             baseEntities[copy.id] = copy;
56402                         } else {
56403                             delete baseEntities[id];
56404                         }
56405                     });
56406
56407                     // swap temporary for permanent ids..
56408                     Object.values(baseEntities).forEach(function(entity) {
56409                         if (Array.isArray(entity.nodes)) {
56410                             entity.nodes = entity.nodes.map(function(node) {
56411                                 return permIDs[node] || node;
56412                             });
56413                         }
56414                         if (Array.isArray(entity.members)) {
56415                             entity.members = entity.members.map(function(member) {
56416                                 member.id = permIDs[member.id] || member.id;
56417                                 return member;
56418                             });
56419                         }
56420                     });
56421
56422                     return JSON.stringify({ dataIntroGraph: baseEntities });
56423
56424
56425                     function copyIntroEntity(source) {
56426                         var copy = utilObjectOmit(source, ['type', 'user', 'v', 'version', 'visible']);
56427
56428                         // Note: the copy is no longer an osmEntity, so it might not have `tags`
56429                         if (copy.tags && !Object.keys(copy.tags)) {
56430                             delete copy.tags;
56431                         }
56432
56433                         if (Array.isArray(copy.loc)) {
56434                             copy.loc[0] = +copy.loc[0].toFixed(6);
56435                             copy.loc[1] = +copy.loc[1].toFixed(6);
56436                         }
56437
56438                         var match = source.id.match(/([nrw])-\d*/);  // temporary id
56439                         if (match !== null) {
56440                             var nrw = match[1];
56441                             var permID;
56442                             do { permID = nrw + (++nextID[nrw]); }
56443                             while (baseEntities.hasOwnProperty(permID));
56444
56445                             copy.id = permIDs[source.id] = permID;
56446                         }
56447                         return copy;
56448                     }
56449                 },
56450
56451
56452                 toJSON: function() {
56453                     if (!this.hasChanges()) return;
56454
56455                     var allEntities = {};
56456                     var baseEntities = {};
56457                     var base = _stack[0];
56458
56459                     var s = _stack.map(function(i) {
56460                         var modified = [];
56461                         var deleted = [];
56462
56463                         Object.keys(i.graph.entities).forEach(function(id) {
56464                             var entity = i.graph.entities[id];
56465                             if (entity) {
56466                                 var key = osmEntity.key(entity);
56467                                 allEntities[key] = entity;
56468                                 modified.push(key);
56469                             } else {
56470                                 deleted.push(id);
56471                             }
56472
56473                             // make sure that the originals of changed or deleted entities get merged
56474                             // into the base of the _stack after restoring the data from JSON.
56475                             if (id in base.graph.entities) {
56476                                 baseEntities[id] = base.graph.entities[id];
56477                             }
56478                             if (entity && entity.nodes) {
56479                                 // get originals of pre-existing child nodes
56480                                 entity.nodes.forEach(function(nodeID) {
56481                                     if (nodeID in base.graph.entities) {
56482                                         baseEntities[nodeID] = base.graph.entities[nodeID];
56483                                     }
56484                                 });
56485                             }
56486                             // get originals of parent entities too
56487                             var baseParents = base.graph._parentWays[id];
56488                             if (baseParents) {
56489                                 baseParents.forEach(function(parentID) {
56490                                     if (parentID in base.graph.entities) {
56491                                         baseEntities[parentID] = base.graph.entities[parentID];
56492                                     }
56493                                 });
56494                             }
56495                         });
56496
56497                         var x = {};
56498
56499                         if (modified.length) x.modified = modified;
56500                         if (deleted.length) x.deleted = deleted;
56501                         if (i.imageryUsed) x.imageryUsed = i.imageryUsed;
56502                         if (i.photoOverlaysUsed) x.photoOverlaysUsed = i.photoOverlaysUsed;
56503                         if (i.annotation) x.annotation = i.annotation;
56504                         if (i.transform) x.transform = i.transform;
56505                         if (i.selectedIDs) x.selectedIDs = i.selectedIDs;
56506
56507                         return x;
56508                     });
56509
56510                     return JSON.stringify({
56511                         version: 3,
56512                         entities: Object.values(allEntities),
56513                         baseEntities: Object.values(baseEntities),
56514                         stack: s,
56515                         nextIDs: osmEntity.id.next,
56516                         index: _index,
56517                         // note the time the changes were saved
56518                         timestamp: (new Date()).getTime()
56519                     });
56520                 },
56521
56522
56523                 fromJSON: function(json, loadChildNodes) {
56524                     var h = JSON.parse(json);
56525                     var loadComplete = true;
56526
56527                     osmEntity.id.next = h.nextIDs;
56528                     _index = h.index;
56529
56530                     if (h.version === 2 || h.version === 3) {
56531                         var allEntities = {};
56532
56533                         h.entities.forEach(function(entity) {
56534                             allEntities[osmEntity.key(entity)] = osmEntity(entity);
56535                         });
56536
56537                         if (h.version === 3) {
56538                             // This merges originals for changed entities into the base of
56539                             // the _stack even if the current _stack doesn't have them (for
56540                             // example when iD has been restarted in a different region)
56541                             var baseEntities = h.baseEntities.map(function(d) { return osmEntity(d); });
56542                             var stack = _stack.map(function(state) { return state.graph; });
56543                             _stack[0].graph.rebase(baseEntities, stack, true);
56544                             _tree.rebase(baseEntities, true);
56545
56546                             // When we restore a modified way, we also need to fetch any missing
56547                             // childnodes that would normally have been downloaded with it.. #2142
56548                             if (loadChildNodes) {
56549                                 var osm = context.connection();
56550                                 var baseWays = baseEntities
56551                                     .filter(function(e) { return e.type === 'way'; });
56552                                 var nodeIDs = baseWays
56553                                     .reduce(function(acc, way) { return utilArrayUnion(acc, way.nodes); }, []);
56554                                 var missing = nodeIDs
56555                                     .filter(function(n) { return !_stack[0].graph.hasEntity(n); });
56556
56557                                 if (missing.length && osm) {
56558                                     loadComplete = false;
56559                                     context.map().redrawEnable(false);
56560
56561                                     var loading = uiLoading(context).blocking(true);
56562                                     context.container().call(loading);
56563
56564                                     var childNodesLoaded = function(err, result) {
56565                                         if (!err) {
56566                                             var visibleGroups = utilArrayGroupBy(result.data, 'visible');
56567                                             var visibles = visibleGroups.true || [];      // alive nodes
56568                                             var invisibles = visibleGroups.false || [];   // deleted nodes
56569
56570                                             if (visibles.length) {
56571                                                 var visibleIDs = visibles.map(function(entity) { return entity.id; });
56572                                                 var stack = _stack.map(function(state) { return state.graph; });
56573                                                 missing = utilArrayDifference(missing, visibleIDs);
56574                                                 _stack[0].graph.rebase(visibles, stack, true);
56575                                                 _tree.rebase(visibles, true);
56576                                             }
56577
56578                                             // fetch older versions of nodes that were deleted..
56579                                             invisibles.forEach(function(entity) {
56580                                                 osm.loadEntityVersion(entity.id, +entity.version - 1, childNodesLoaded);
56581                                             });
56582                                         }
56583
56584                                         if (err || !missing.length) {
56585                                             loading.close();
56586                                             context.map().redrawEnable(true);
56587                                             dispatch$1.call('change');
56588                                             dispatch$1.call('restore', this);
56589                                         }
56590                                     };
56591
56592                                     osm.loadMultiple(missing, childNodesLoaded);
56593                                 }
56594                             }
56595                         }
56596
56597                         _stack = h.stack.map(function(d) {
56598                             var entities = {}, entity;
56599
56600                             if (d.modified) {
56601                                 d.modified.forEach(function(key) {
56602                                     entity = allEntities[key];
56603                                     entities[entity.id] = entity;
56604                                 });
56605                             }
56606
56607                             if (d.deleted) {
56608                                 d.deleted.forEach(function(id) {
56609                                     entities[id] = undefined;
56610                                 });
56611                             }
56612
56613                             return {
56614                                 graph: coreGraph(_stack[0].graph).load(entities),
56615                                 annotation: d.annotation,
56616                                 imageryUsed: d.imageryUsed,
56617                                 photoOverlaysUsed: d.photoOverlaysUsed,
56618                                 transform: d.transform,
56619                                 selectedIDs: d.selectedIDs
56620                             };
56621                         });
56622
56623                     } else { // original version
56624                         _stack = h.stack.map(function(d) {
56625                             var entities = {};
56626
56627                             for (var i in d.entities) {
56628                                 var entity = d.entities[i];
56629                                 entities[i] = entity === 'undefined' ? undefined : osmEntity(entity);
56630                             }
56631
56632                             d.graph = coreGraph(_stack[0].graph).load(entities);
56633                             return d;
56634                         });
56635                     }
56636
56637                     var transform = _stack[_index].transform;
56638                     if (transform) {
56639                         context.map().transformEase(transform, 0);   // 0 = immediate, no easing
56640                     }
56641
56642                     if (loadComplete) {
56643                         dispatch$1.call('change');
56644                         dispatch$1.call('restore', this);
56645                     }
56646
56647                     return history;
56648                 },
56649
56650
56651                 lock: function() {
56652                     return lock.lock();
56653                 },
56654
56655
56656                 unlock: function() {
56657                     lock.unlock();
56658                 },
56659
56660
56661                 save: function() {
56662                     if (lock.locked() &&
56663                         // don't overwrite existing, unresolved changes
56664                         !_hasUnresolvedRestorableChanges) {
56665
56666                         corePreferences(getKey('saved_history'), history.toJSON() || null);
56667                     }
56668                     return history;
56669                 },
56670
56671
56672                 // delete the history version saved in localStorage
56673                 clearSaved: function() {
56674                     context.debouncedSave.cancel();
56675                     if (lock.locked()) {
56676                         _hasUnresolvedRestorableChanges = false;
56677                         corePreferences(getKey('saved_history'), null);
56678
56679                         // clear the changeset metadata associated with the saved history
56680                         corePreferences('comment', null);
56681                         corePreferences('hashtags', null);
56682                         corePreferences('source', null);
56683                     }
56684                     return history;
56685                 },
56686
56687
56688                 savedHistoryJSON: function() {
56689                     return corePreferences(getKey('saved_history'));
56690                 },
56691
56692
56693                 hasRestorableChanges: function() {
56694                     return _hasUnresolvedRestorableChanges;
56695                 },
56696
56697
56698                 // load history from a version stored in localStorage
56699                 restore: function() {
56700                     if (lock.locked()) {
56701                         _hasUnresolvedRestorableChanges = false;
56702                         var json = this.savedHistoryJSON();
56703                         if (json) history.fromJSON(json, true);
56704                     }
56705                 },
56706
56707
56708                 _getKey: getKey
56709
56710             };
56711
56712
56713             history.reset();
56714
56715             return utilRebind(history, dispatch$1, 'on');
56716         }
56717
56718         /**
56719          * Look for roads that can be connected to other roads with a short extension
56720          */
56721         function validationAlmostJunction(context) {
56722           const type = 'almost_junction';
56723           const EXTEND_TH_METERS = 5;
56724           const WELD_TH_METERS = 0.75;
56725           // Comes from considering bounding case of parallel ways
56726           const CLOSE_NODE_TH = EXTEND_TH_METERS - WELD_TH_METERS;
56727           // Comes from considering bounding case of perpendicular ways
56728           const SIG_ANGLE_TH = Math.atan(WELD_TH_METERS / EXTEND_TH_METERS);
56729
56730           function isHighway(entity) {
56731             return entity.type === 'way'
56732               && osmRoutableHighwayTagValues[entity.tags.highway];
56733           }
56734
56735           function isTaggedAsNotContinuing(node) {
56736             return node.tags.noexit === 'yes'
56737               || node.tags.amenity === 'parking_entrance'
56738               || (node.tags.entrance && node.tags.entrance !== 'no');
56739           }
56740
56741
56742           const validation = function checkAlmostJunction(entity, graph) {
56743             if (!isHighway(entity)) return [];
56744             if (entity.isDegenerate()) return [];
56745
56746             const tree = context.history().tree();
56747             const extendableNodeInfos = findConnectableEndNodesByExtension(entity);
56748
56749             let issues = [];
56750
56751             extendableNodeInfos.forEach(extendableNodeInfo => {
56752               issues.push(new validationIssue({
56753                 type,
56754                 subtype: 'highway-highway',
56755                 severity: 'warning',
56756                 message(context) {
56757                   const entity1 = context.hasEntity(this.entityIds[0]);
56758                   if (this.entityIds[0] === this.entityIds[2]) {
56759                     return entity1 ? _t('issues.almost_junction.self.message', {
56760                       feature: utilDisplayLabel(entity1, context.graph())
56761                     }) : '';
56762                   } else {
56763                     const entity2 = context.hasEntity(this.entityIds[2]);
56764                     return (entity1 && entity2) ? _t('issues.almost_junction.message', {
56765                       feature: utilDisplayLabel(entity1, context.graph()),
56766                       feature2: utilDisplayLabel(entity2, context.graph())
56767                     }) : '';
56768                   }
56769                 },
56770                 reference: showReference,
56771                 entityIds: [
56772                   entity.id,
56773                   extendableNodeInfo.node.id,
56774                   extendableNodeInfo.wid,
56775                 ],
56776                 loc: extendableNodeInfo.node.loc,
56777                 hash: JSON.stringify(extendableNodeInfo.node.loc),
56778                 data: {
56779                   midId: extendableNodeInfo.mid.id,
56780                   edge: extendableNodeInfo.edge,
56781                   cross_loc: extendableNodeInfo.cross_loc
56782                 },
56783                 dynamicFixes: makeFixes
56784               }));
56785             });
56786
56787             return issues;
56788
56789             function makeFixes(context) {
56790               let fixes = [new validationIssueFix({
56791                 icon: 'iD-icon-abutment',
56792                 title: _t('issues.fix.connect_features.title'),
56793                 onClick(context) {
56794                   const annotation = _t('issues.fix.connect_almost_junction.annotation');
56795                   const [, endNodeId, crossWayId] = this.issue.entityIds;
56796                   const midNode = context.entity(this.issue.data.midId);
56797                   const endNode = context.entity(endNodeId);
56798                   const crossWay = context.entity(crossWayId);
56799
56800                   // When endpoints are close, just join if resulting small change in angle (#7201)
56801                   const nearEndNodes = findNearbyEndNodes(endNode, crossWay);
56802                   if (nearEndNodes.length > 0) {
56803                     const collinear = findSmallJoinAngle(midNode, endNode, nearEndNodes);
56804                     if (collinear) {
56805                       context.perform(
56806                         actionMergeNodes([collinear.id, endNode.id], collinear.loc),
56807                         annotation
56808                       );
56809                       return;
56810                     }
56811                   }
56812
56813                   const targetEdge = this.issue.data.edge;
56814                   const crossLoc = this.issue.data.cross_loc;
56815                   const edgeNodes = [context.entity(targetEdge[0]), context.entity(targetEdge[1])];
56816                   const closestNodeInfo = geoSphericalClosestNode(edgeNodes, crossLoc);
56817
56818                   // already a point nearby, just connect to that
56819                   if (closestNodeInfo.distance < WELD_TH_METERS) {
56820                     context.perform(
56821                       actionMergeNodes([closestNodeInfo.node.id, endNode.id], closestNodeInfo.node.loc),
56822                       annotation
56823                     );
56824                   // else add the end node to the edge way
56825                   } else {
56826                     context.perform(
56827                       actionAddMidpoint({loc: crossLoc, edge: targetEdge}, endNode),
56828                       annotation
56829                     );
56830                   }
56831                 }
56832               })];
56833
56834               const node = context.hasEntity(this.entityIds[1]);
56835               if (node && !node.hasInterestingTags()) {
56836                 // node has no descriptive tags, suggest noexit fix
56837                 fixes.push(new validationIssueFix({
56838                   icon: 'maki-barrier',
56839                   title: _t('issues.fix.tag_as_disconnected.title'),
56840                   onClick(context) {
56841                     const nodeID = this.issue.entityIds[1];
56842                     const tags = Object.assign({}, context.entity(nodeID).tags);
56843                     tags.noexit = 'yes';
56844                     context.perform(
56845                       actionChangeTags(nodeID, tags),
56846                       _t('issues.fix.tag_as_disconnected.annotation')
56847                     );
56848                   }
56849                 }));
56850               }
56851
56852               return fixes;
56853             }
56854
56855             function showReference(selection) {
56856               selection.selectAll('.issue-reference')
56857                 .data([0])
56858                 .enter()
56859                 .append('div')
56860                 .attr('class', 'issue-reference')
56861                 .text(_t('issues.almost_junction.highway-highway.reference'));
56862             }
56863
56864             function isExtendableCandidate(node, way) {
56865               // can not accurately test vertices on tiles not downloaded from osm - #5938
56866               const osm = services.osm;
56867               if (osm && !osm.isDataLoaded(node.loc)) {
56868                 return false;
56869               }
56870               if (isTaggedAsNotContinuing(node) || graph.parentWays(node).length !== 1) {
56871                 return false;
56872               }
56873
56874               let occurences = 0;
56875               for (const index in way.nodes) {
56876                 if (way.nodes[index] === node.id) {
56877                   occurences += 1;
56878                   if (occurences > 1) {
56879                     return false;
56880                   }
56881                 }
56882               }
56883               return true;
56884             }
56885
56886             function findConnectableEndNodesByExtension(way) {
56887               let results = [];
56888               if (way.isClosed()) return results;
56889
56890               let testNodes;
56891               const indices = [0, way.nodes.length - 1];
56892               indices.forEach(nodeIndex => {
56893                 const nodeID = way.nodes[nodeIndex];
56894                 const node = graph.entity(nodeID);
56895
56896                 if (!isExtendableCandidate(node, way)) return;
56897
56898                 const connectionInfo = canConnectByExtend(way, nodeIndex);
56899                 if (!connectionInfo) return;
56900
56901                 testNodes = graph.childNodes(way).slice();   // shallow copy
56902                 testNodes[nodeIndex] = testNodes[nodeIndex].move(connectionInfo.cross_loc);
56903
56904                 // don't flag issue if connecting the ways would cause self-intersection
56905                 if (geoHasSelfIntersections(testNodes, nodeID)) return;
56906
56907                 results.push(connectionInfo);
56908               });
56909
56910               return results;
56911             }
56912
56913             function findNearbyEndNodes(node, way) {
56914               return [
56915                 way.nodes[0],
56916                 way.nodes[way.nodes.length - 1]
56917               ].map(d => graph.entity(d))
56918               .filter(d => {
56919                 // Node cannot be near to itself, but other endnode of same way could be
56920                 return d.id !== node.id
56921                   && geoSphericalDistance(node.loc, d.loc) <= CLOSE_NODE_TH;
56922               });
56923             }
56924
56925             function findSmallJoinAngle(midNode, tipNode, endNodes) {
56926               // Both nodes could be close, so want to join whichever is closest to collinear
56927               let joinTo;
56928               let minAngle = Infinity;
56929
56930               // Checks midNode -> tipNode -> endNode for collinearity
56931               endNodes.forEach(endNode => {
56932                 const a1 = geoAngle(midNode, tipNode, context.projection) + Math.PI;
56933                 const a2 = geoAngle(midNode, endNode, context.projection) + Math.PI;
56934                 const diff = Math.max(a1, a2) - Math.min(a1, a2);
56935
56936                 if (diff < minAngle) {
56937                   joinTo = endNode;
56938                   minAngle = diff;
56939                 }
56940               });
56941
56942               /* Threshold set by considering right angle triangle
56943               based on node joining threshold and extension distance */
56944               if (minAngle <= SIG_ANGLE_TH) return joinTo;
56945
56946               return null;
56947             }
56948
56949             function hasTag(tags, key) {
56950               return tags[key] !== undefined && tags[key] !== 'no';
56951             }
56952
56953             function canConnectWays(way, way2) {
56954
56955               // allow self-connections
56956               if (way.id === way2.id) return true;
56957
56958               // if one is bridge or tunnel, both must be bridge or tunnel
56959               if ((hasTag(way.tags, 'bridge') || hasTag(way2.tags, 'bridge')) &&
56960                 !(hasTag(way.tags, 'bridge') && hasTag(way2.tags, 'bridge'))) return false;
56961               if ((hasTag(way.tags, 'tunnel') || hasTag(way2.tags, 'tunnel')) &&
56962                 !(hasTag(way.tags, 'tunnel') && hasTag(way2.tags, 'tunnel'))) return false;
56963
56964               // must have equivalent layers and levels
56965               const layer1 = way.tags.layer || '0',
56966                 layer2 = way2.tags.layer || '0';
56967               if (layer1 !== layer2) return false;
56968
56969               const level1 = way.tags.level || '0',
56970                 level2 = way2.tags.level || '0';
56971               if (level1 !== level2) return false;
56972
56973               return true;
56974             }
56975
56976             function canConnectByExtend(way, endNodeIdx) {
56977               const tipNid = way.nodes[endNodeIdx];  // the 'tip' node for extension point
56978               const midNid = endNodeIdx === 0 ? way.nodes[1] : way.nodes[way.nodes.length - 2];  // the other node of the edge
56979               const tipNode = graph.entity(tipNid);
56980               const midNode = graph.entity(midNid);
56981               const lon = tipNode.loc[0];
56982               const lat = tipNode.loc[1];
56983               const lon_range = geoMetersToLon(EXTEND_TH_METERS, lat) / 2;
56984               const lat_range = geoMetersToLat(EXTEND_TH_METERS) / 2;
56985               const queryExtent = geoExtent([
56986                 [lon - lon_range, lat - lat_range],
56987                 [lon + lon_range, lat + lat_range]
56988               ]);
56989
56990               // first, extend the edge of [midNode -> tipNode] by EXTEND_TH_METERS and find the "extended tip" location
56991               const edgeLen = geoSphericalDistance(midNode.loc, tipNode.loc);
56992               const t = EXTEND_TH_METERS / edgeLen + 1.0;
56993               const extTipLoc = geoVecInterp(midNode.loc, tipNode.loc, t);
56994
56995               // then, check if the extension part [tipNode.loc -> extTipLoc] intersects any other ways
56996               const segmentInfos = tree.waySegments(queryExtent, graph);
56997               for (let i = 0; i < segmentInfos.length; i++) {
56998                 let segmentInfo = segmentInfos[i];
56999
57000                 let way2 = graph.entity(segmentInfo.wayId);
57001
57002                 if (!isHighway(way2)) continue;
57003
57004                 if (!canConnectWays(way, way2)) continue;
57005
57006                 let nAid = segmentInfo.nodes[0],
57007                   nBid = segmentInfo.nodes[1];
57008
57009                 if (nAid === tipNid || nBid === tipNid) continue;
57010
57011                 let nA = graph.entity(nAid),
57012                   nB = graph.entity(nBid);
57013                 let crossLoc = geoLineIntersection([tipNode.loc, extTipLoc], [nA.loc, nB.loc]);
57014                 if (crossLoc) {
57015                   return {
57016                     mid: midNode,
57017                     node: tipNode,
57018                     wid: way2.id,
57019                     edge: [nA.id, nB.id],
57020                     cross_loc: crossLoc
57021                   };
57022                 }
57023               }
57024               return null;
57025             }
57026           };
57027
57028           validation.type = type;
57029
57030           return validation;
57031         }
57032
57033         function validationCloseNodes(context) {
57034             var type = 'close_nodes';
57035
57036             var pointThresholdMeters = 0.2;
57037
57038             var validation = function(entity, graph) {
57039                 if (entity.type === 'node') {
57040                     return getIssuesForNode(entity);
57041                 } else if (entity.type === 'way') {
57042                     return getIssuesForWay(entity);
57043                 }
57044                 return [];
57045
57046                 function getIssuesForNode(node) {
57047                     var parentWays = graph.parentWays(node);
57048                     if (parentWays.length) {
57049                         return getIssuesForVertex(node, parentWays);
57050                     } else {
57051                         return getIssuesForDetachedPoint(node);
57052                     }
57053                 }
57054
57055                 function wayTypeFor(way) {
57056
57057                     if (way.tags.boundary && way.tags.boundary !== 'no') return 'boundary';
57058                     if (way.tags.indoor && way.tags.indoor !== 'no') return 'indoor';
57059                     if ((way.tags.building && way.tags.building !== 'no') ||
57060                         (way.tags['building:part'] && way.tags['building:part'] !== 'no')) return 'building';
57061                     if (osmPathHighwayTagValues[way.tags.highway]) return 'path';
57062
57063                     var parentRelations = graph.parentRelations(way);
57064                     for (var i in parentRelations) {
57065                         var relation = parentRelations[i];
57066
57067                         if (relation.tags.type === 'boundary') return 'boundary';
57068
57069                         if (relation.isMultipolygon()) {
57070                             if (relation.tags.indoor && relation.tags.indoor !== 'no') return 'indoor';
57071                             if ((relation.tags.building && relation.tags.building !== 'no') ||
57072                                 (relation.tags['building:part'] && relation.tags['building:part'] !== 'no')) return 'building';
57073                         }
57074                     }
57075
57076                     return 'other';
57077                 }
57078
57079                 function shouldCheckWay(way) {
57080
57081                     // don't flag issues where merging would create degenerate ways
57082                     if (way.nodes.length <= 2 ||
57083                         (way.isClosed() && way.nodes.length <= 4)) return false;
57084
57085                     var bbox = way.extent(graph).bbox();
57086                     var hypotenuseMeters = geoSphericalDistance([bbox.minX, bbox.minY], [bbox.maxX, bbox.maxY]);
57087                     // don't flag close nodes in very small ways
57088                     if (hypotenuseMeters < 1.5) return false;
57089
57090                     return true;
57091                 }
57092
57093                 function getIssuesForWay(way) {
57094                     if (!shouldCheckWay(way)) return [];
57095
57096                     var issues = [],
57097                         nodes = graph.childNodes(way);
57098                     for (var i = 0; i < nodes.length - 1; i++) {
57099                         var node1 = nodes[i];
57100                         var node2 = nodes[i+1];
57101
57102                         var issue = getWayIssueIfAny(node1, node2, way);
57103                         if (issue) issues.push(issue);
57104                     }
57105                     return issues;
57106                 }
57107
57108                 function getIssuesForVertex(node, parentWays) {
57109                     var issues = [];
57110
57111                     function checkForCloseness(node1, node2, way) {
57112                         var issue = getWayIssueIfAny(node1, node2, way);
57113                         if (issue) issues.push(issue);
57114                     }
57115
57116                     for (var i = 0; i < parentWays.length; i++) {
57117                         var parentWay = parentWays[i];
57118
57119                         if (!shouldCheckWay(parentWay)) continue;
57120
57121                         var lastIndex = parentWay.nodes.length - 1;
57122                         for (var j = 0; j < parentWay.nodes.length; j++) {
57123                             if (j !== 0) {
57124                                 if (parentWay.nodes[j-1] === node.id) {
57125                                     checkForCloseness(node, graph.entity(parentWay.nodes[j]), parentWay);
57126                                 }
57127                             }
57128                             if (j !== lastIndex) {
57129                                 if (parentWay.nodes[j+1] === node.id) {
57130                                     checkForCloseness(graph.entity(parentWay.nodes[j]), node, parentWay);
57131                                 }
57132                             }
57133                         }
57134                     }
57135                     return issues;
57136                 }
57137
57138                 function thresholdMetersForWay(way) {
57139                     if (!shouldCheckWay(way)) return 0;
57140
57141                     var wayType = wayTypeFor(way);
57142
57143                     // don't flag boundaries since they might be highly detailed and can't be easily verified
57144                     if (wayType === 'boundary') return 0;
57145                     // expect some features to be mapped with higher levels of detail
57146                     if (wayType === 'indoor') return 0.01;
57147                     if (wayType === 'building') return 0.05;
57148                     if (wayType === 'path') return 0.1;
57149                     return 0.2;
57150                 }
57151
57152                 function getIssuesForDetachedPoint(node) {
57153
57154                     var issues = [];
57155
57156                     var lon = node.loc[0];
57157                     var lat = node.loc[1];
57158                     var lon_range = geoMetersToLon(pointThresholdMeters, lat) / 2;
57159                     var lat_range = geoMetersToLat(pointThresholdMeters) / 2;
57160                     var queryExtent = geoExtent([
57161                         [lon - lon_range, lat - lat_range],
57162                         [lon + lon_range, lat + lat_range]
57163                     ]);
57164
57165                     var intersected = context.history().tree().intersects(queryExtent, graph);
57166                     for (var j = 0; j < intersected.length; j++) {
57167                         var nearby = intersected[j];
57168
57169                         if (nearby.id === node.id) continue;
57170                         if (nearby.type !== 'node' || nearby.geometry(graph) !== 'point') continue;
57171
57172                         if (nearby.loc === node.loc ||
57173                             geoSphericalDistance(node.loc, nearby.loc) < pointThresholdMeters) {
57174
57175                             // allow very close points if tags indicate the z-axis might vary
57176                             var zAxisKeys = { layer: true, level: true, 'addr:housenumber': true, 'addr:unit': true };
57177                             var zAxisDifferentiates = false;
57178                             for (var key in zAxisKeys) {
57179                                 var nodeValue = node.tags[key] || '0';
57180                                 var nearbyValue = nearby.tags[key] || '0';
57181                                 if (nodeValue !== nearbyValue) {
57182                                     zAxisDifferentiates = true;
57183                                     break;
57184                                 }
57185                             }
57186                             if (zAxisDifferentiates) continue;
57187
57188                             issues.push(new validationIssue({
57189                                 type: type,
57190                                 subtype: 'detached',
57191                                 severity: 'warning',
57192                                 message: function(context) {
57193                                     var entity = context.hasEntity(this.entityIds[0]),
57194                                         entity2 = context.hasEntity(this.entityIds[1]);
57195                                     return (entity && entity2) ? _t('issues.close_nodes.detached.message', {
57196                                         feature: utilDisplayLabel(entity, context.graph()),
57197                                         feature2: utilDisplayLabel(entity2, context.graph())
57198                                     }) : '';
57199                                 },
57200                                 reference: showReference,
57201                                 entityIds: [node.id, nearby.id],
57202                                 dynamicFixes: function() {
57203                                     return [
57204                                         new validationIssueFix({
57205                                             icon: 'iD-operation-disconnect',
57206                                             title: _t('issues.fix.move_points_apart.title')
57207                                         }),
57208                                         new validationIssueFix({
57209                                             icon: 'iD-icon-layers',
57210                                             title: _t('issues.fix.use_different_layers_or_levels.title')
57211                                         })
57212                                     ];
57213                                 }
57214                             }));
57215                         }
57216                     }
57217
57218                     return issues;
57219
57220                     function showReference(selection) {
57221                         var referenceText = _t('issues.close_nodes.detached.reference');
57222                         selection.selectAll('.issue-reference')
57223                             .data([0])
57224                             .enter()
57225                             .append('div')
57226                             .attr('class', 'issue-reference')
57227                             .text(referenceText);
57228                     }
57229                 }
57230
57231                 function getWayIssueIfAny(node1, node2, way) {
57232                     if (node1.id === node2.id ||
57233                         (node1.hasInterestingTags() && node2.hasInterestingTags())) {
57234                         return null;
57235                     }
57236
57237                     if (node1.loc !== node2.loc) {
57238                         var parentWays1 = graph.parentWays(node1);
57239                         var parentWays2 = new Set(graph.parentWays(node2));
57240
57241                         var sharedWays = parentWays1.filter(function(parentWay) {
57242                             return parentWays2.has(parentWay);
57243                         });
57244
57245                         var thresholds = sharedWays.map(function(parentWay) {
57246                             return thresholdMetersForWay(parentWay);
57247                         });
57248
57249                         var threshold = Math.min(...thresholds);
57250                         var distance = geoSphericalDistance(node1.loc, node2.loc);
57251                         if (distance > threshold) return null;
57252                     }
57253
57254                     return new validationIssue({
57255                         type: type,
57256                         subtype: 'vertices',
57257                         severity: 'warning',
57258                         message: function(context) {
57259                             var entity = context.hasEntity(this.entityIds[0]);
57260                             return entity ? _t('issues.close_nodes.message', { way: utilDisplayLabel(entity, context.graph()) }) : '';
57261                         },
57262                         reference: showReference,
57263                         entityIds: [way.id, node1.id, node2.id],
57264                         loc: node1.loc,
57265                         dynamicFixes: function() {
57266                             return [
57267                                 new validationIssueFix({
57268                                     icon: 'iD-icon-plus',
57269                                     title: _t('issues.fix.merge_points.title'),
57270                                     onClick: function(context) {
57271                                         var entityIds = this.issue.entityIds;
57272                                         var action = actionMergeNodes([entityIds[1], entityIds[2]]);
57273                                         context.perform(action, _t('issues.fix.merge_close_vertices.annotation'));
57274                                     }
57275                                 }),
57276                                 new validationIssueFix({
57277                                     icon: 'iD-operation-disconnect',
57278                                     title: _t('issues.fix.move_points_apart.title')
57279                                 })
57280                             ];
57281                         }
57282                     });
57283
57284                     function showReference(selection) {
57285                         var referenceText = _t('issues.close_nodes.reference');
57286                         selection.selectAll('.issue-reference')
57287                             .data([0])
57288                             .enter()
57289                             .append('div')
57290                             .attr('class', 'issue-reference')
57291                             .text(referenceText);
57292                     }
57293                 }
57294
57295             };
57296
57297
57298             validation.type = type;
57299
57300             return validation;
57301         }
57302
57303         function validationCrossingWays(context) {
57304             var type = 'crossing_ways';
57305
57306             // returns the way or its parent relation, whichever has a useful feature type
57307             function getFeatureWithFeatureTypeTagsForWay(way, graph) {
57308                 if (getFeatureType(way, graph) === null) {
57309                     // if the way doesn't match a feature type, check its parent relations
57310                     var parentRels = graph.parentRelations(way);
57311                     for (var i = 0; i < parentRels.length; i++) {
57312                         var rel = parentRels[i];
57313                         if (getFeatureType(rel, graph) !== null) {
57314                             return rel;
57315                         }
57316                     }
57317                 }
57318                 return way;
57319             }
57320
57321
57322             function hasTag(tags, key) {
57323                 return tags[key] !== undefined && tags[key] !== 'no';
57324             }
57325
57326             function taggedAsIndoor(tags) {
57327                 return hasTag(tags, 'indoor') ||
57328                     hasTag(tags, 'level') ||
57329                     tags.highway === 'corridor';
57330             }
57331
57332             function allowsBridge(featureType) {
57333                 return featureType === 'highway' || featureType === 'railway' || featureType === 'waterway';
57334             }
57335             function allowsTunnel(featureType) {
57336                 return featureType === 'highway' || featureType === 'railway' || featureType === 'waterway';
57337             }
57338
57339
57340             function getFeatureTypeForCrossingCheck(way, graph) {
57341                 var feature = getFeatureWithFeatureTypeTagsForWay(way, graph);
57342                 return getFeatureType(feature, graph);
57343             }
57344
57345             // discard
57346             var ignoredBuildings = {
57347                 demolished: true, dismantled: true, proposed: true, razed: true
57348             };
57349
57350
57351             function getFeatureType(entity, graph) {
57352
57353                 var geometry = entity.geometry(graph);
57354                 if (geometry !== 'line' && geometry !== 'area') return null;
57355
57356                 var tags = entity.tags;
57357
57358                 if (hasTag(tags, 'building') && !ignoredBuildings[tags.building]) return 'building';
57359                 if (hasTag(tags, 'highway') && osmRoutableHighwayTagValues[tags.highway]) return 'highway';
57360
57361                 // don't check railway or waterway areas
57362                 if (geometry !== 'line') return null;
57363
57364                 if (hasTag(tags, 'railway') && osmRailwayTrackTagValues[tags.railway]) return 'railway';
57365                 if (hasTag(tags, 'waterway') && osmFlowingWaterwayTagValues[tags.waterway]) return 'waterway';
57366
57367                 return null;
57368             }
57369
57370
57371             function isLegitCrossing(way1, featureType1, way2, featureType2) {
57372                 var tags1 = way1.tags;
57373                 var tags2 = way2.tags;
57374
57375                 // assume 0 by default
57376                 var level1 = tags1.level || '0';
57377                 var level2 = tags2.level || '0';
57378
57379                 if (taggedAsIndoor(tags1) && taggedAsIndoor(tags2) && level1 !== level2) {
57380                     // assume features don't interact if they're indoor on different levels
57381                     return true;
57382                 }
57383
57384                 // assume 0 by default; don't use way.layer() since we account for structures here
57385                 var layer1 = tags1.layer || '0';
57386                 var layer2 = tags2.layer || '0';
57387
57388                 if (allowsBridge(featureType1) && allowsBridge(featureType2)) {
57389                     if (hasTag(tags1, 'bridge') && !hasTag(tags2, 'bridge')) return true;
57390                     if (!hasTag(tags1, 'bridge') && hasTag(tags2, 'bridge')) return true;
57391                     // crossing bridges must use different layers
57392                     if (hasTag(tags1, 'bridge') && hasTag(tags2, 'bridge') && layer1 !== layer2) return true;
57393                 } else if (allowsBridge(featureType1) && hasTag(tags1, 'bridge')) return true;
57394                 else if (allowsBridge(featureType2) && hasTag(tags2, 'bridge')) return true;
57395
57396                 if (allowsTunnel(featureType1) && allowsTunnel(featureType2)) {
57397                     if (hasTag(tags1, 'tunnel') && !hasTag(tags2, 'tunnel')) return true;
57398                     if (!hasTag(tags1, 'tunnel') && hasTag(tags2, 'tunnel')) return true;
57399                     // crossing tunnels must use different layers
57400                     if (hasTag(tags1, 'tunnel') && hasTag(tags2, 'tunnel') && layer1 !== layer2) return true;
57401                 } else if (allowsTunnel(featureType1) && hasTag(tags1, 'tunnel')) return true;
57402                 else if (allowsTunnel(featureType2) && hasTag(tags2, 'tunnel')) return true;
57403
57404                 // don't flag crossing waterways and pier/highways
57405                 if (featureType1 === 'waterway' && featureType2 === 'highway' && tags2.man_made === 'pier') return true;
57406                 if (featureType2 === 'waterway' && featureType1 === 'highway' && tags1.man_made === 'pier') return true;
57407
57408                 if (featureType1 === 'building' || featureType2 === 'building') {
57409                     // for building crossings, different layers are enough
57410                     if (layer1 !== layer2) return true;
57411                 }
57412                 return false;
57413             }
57414
57415
57416             // highway values for which we shouldn't recommend connecting to waterways
57417             var highwaysDisallowingFords = {
57418                 motorway: true, motorway_link: true, trunk: true, trunk_link: true,
57419                 primary: true, primary_link: true, secondary: true, secondary_link: true
57420             };
57421             var nonCrossingHighways = { track: true };
57422
57423             function tagsForConnectionNodeIfAllowed(entity1, entity2, graph) {
57424                 var featureType1 = getFeatureType(entity1, graph);
57425                 var featureType2 = getFeatureType(entity2, graph);
57426
57427                 var geometry1 = entity1.geometry(graph);
57428                 var geometry2 = entity2.geometry(graph);
57429                 var bothLines = geometry1 === 'line' && geometry2 === 'line';
57430
57431                 if (featureType1 === featureType2) {
57432                     if (featureType1 === 'highway') {
57433                         var entity1IsPath = osmPathHighwayTagValues[entity1.tags.highway];
57434                         var entity2IsPath = osmPathHighwayTagValues[entity2.tags.highway];
57435                         if ((entity1IsPath || entity2IsPath) && entity1IsPath !== entity2IsPath) {
57436                             // one feature is a path but not both
57437
57438                             var roadFeature = entity1IsPath ? entity2 : entity1;
57439                             if (nonCrossingHighways[roadFeature.tags.highway]) {
57440                                 // don't mark path connections with certain roads as crossings
57441                                 return {};
57442                             }
57443                             var pathFeature = entity1IsPath ? entity1 : entity2;
57444                             if (['marked', 'unmarked'].indexOf(pathFeature.tags.crossing) !== -1) {
57445                                 // if the path is a crossing, match the crossing type
57446                                 return bothLines ? { highway: 'crossing', crossing: pathFeature.tags.crossing } : {};
57447                             }
57448                             // don't add a `crossing` subtag to ambiguous crossings
57449                             return bothLines ? { highway: 'crossing' } : {};
57450                         }
57451                         return {};
57452                     }
57453                     if (featureType1 === 'waterway') return {};
57454                     if (featureType1 === 'railway') return {};
57455
57456                 } else {
57457                     var featureTypes = [featureType1, featureType2];
57458                     if (featureTypes.indexOf('highway') !== -1) {
57459                         if (featureTypes.indexOf('railway') !== -1) {
57460                             if (osmPathHighwayTagValues[entity1.tags.highway] ||
57461                                 osmPathHighwayTagValues[entity2.tags.highway]) {
57462                                 // path-rail connections use this tag
57463                                 return bothLines ? { railway: 'crossing' } : {};
57464                             } else {
57465                                 // road-rail connections use this tag
57466                                 return bothLines ? { railway: 'level_crossing' } : {};
57467                             }
57468                         }
57469
57470                         if (featureTypes.indexOf('waterway') !== -1) {
57471                             // do not allow fords on structures
57472                             if (hasTag(entity1.tags, 'tunnel') && hasTag(entity2.tags, 'tunnel')) return null;
57473                             if (hasTag(entity1.tags, 'bridge') && hasTag(entity2.tags, 'bridge')) return null;
57474
57475                             if (highwaysDisallowingFords[entity1.tags.highway] ||
57476                                 highwaysDisallowingFords[entity2.tags.highway]) {
57477                                 // do not allow fords on major highways
57478                                 return null;
57479                             }
57480                             return bothLines ? { ford: 'yes' } : {};
57481                         }
57482                     }
57483                 }
57484                 return null;
57485             }
57486
57487
57488             function findCrossingsByWay(way1, graph, tree) {
57489                 var edgeCrossInfos = [];
57490                 if (way1.type !== 'way') return edgeCrossInfos;
57491
57492                 var way1FeatureType = getFeatureTypeForCrossingCheck(way1, graph);
57493                 if (way1FeatureType === null) return edgeCrossInfos;
57494
57495                 var checkedSingleCrossingWays = {};
57496
57497                 // declare vars ahead of time to reduce garbage collection
57498                 var i, j;
57499                 var extent;
57500                 var n1, n2, nA, nB, nAId, nBId;
57501                 var segment1, segment2;
57502                 var oneOnly;
57503                 var segmentInfos, segment2Info, way2, way2FeatureType;
57504                 var way1Nodes = graph.childNodes(way1);
57505                 var comparedWays = {};
57506                 for (i = 0; i < way1Nodes.length - 1; i++) {
57507                     n1 = way1Nodes[i];
57508                     n2 = way1Nodes[i + 1];
57509                     extent = geoExtent([
57510                         [
57511                             Math.min(n1.loc[0], n2.loc[0]),
57512                             Math.min(n1.loc[1], n2.loc[1])
57513                         ],
57514                         [
57515                             Math.max(n1.loc[0], n2.loc[0]),
57516                             Math.max(n1.loc[1], n2.loc[1])
57517                         ]
57518                     ]);
57519
57520                     // Optimize by only checking overlapping segments, not every segment
57521                     // of overlapping ways
57522                     segmentInfos = tree.waySegments(extent, graph);
57523
57524                     for (j = 0; j < segmentInfos.length; j++) {
57525                         segment2Info = segmentInfos[j];
57526
57527                         // don't check for self-intersection in this validation
57528                         if (segment2Info.wayId === way1.id) continue;
57529
57530                         // skip if this way was already checked and only one issue is needed
57531                         if (checkedSingleCrossingWays[segment2Info.wayId]) continue;
57532
57533                         // mark this way as checked even if there are no crossings
57534                         comparedWays[segment2Info.wayId] = true;
57535
57536                         way2 = graph.hasEntity(segment2Info.wayId);
57537                         if (!way2) continue;
57538
57539                         // only check crossing highway, waterway, building, and railway
57540                         way2FeatureType = getFeatureTypeForCrossingCheck(way2, graph);
57541                         if (way2FeatureType === null ||
57542                             isLegitCrossing(way1, way1FeatureType, way2, way2FeatureType)) {
57543                             continue;
57544                         }
57545
57546                         // create only one issue for building crossings
57547                         oneOnly = way1FeatureType === 'building' || way2FeatureType === 'building';
57548
57549                         nAId = segment2Info.nodes[0];
57550                         nBId = segment2Info.nodes[1];
57551                         if (nAId === n1.id || nAId === n2.id ||
57552                             nBId === n1.id || nBId === n2.id) {
57553                             // n1 or n2 is a connection node; skip
57554                             continue;
57555                         }
57556                         nA = graph.hasEntity(nAId);
57557                         if (!nA) continue;
57558                         nB = graph.hasEntity(nBId);
57559                         if (!nB) continue;
57560
57561                         segment1 = [n1.loc, n2.loc];
57562                         segment2 = [nA.loc, nB.loc];
57563                         var point = geoLineIntersection(segment1, segment2);
57564                         if (point) {
57565                             edgeCrossInfos.push({
57566                                 wayInfos: [
57567                                     {
57568                                         way: way1,
57569                                         featureType: way1FeatureType,
57570                                         edge: [n1.id, n2.id]
57571                                     },
57572                                     {
57573                                         way: way2,
57574                                         featureType: way2FeatureType,
57575                                         edge: [nA.id, nB.id]
57576                                     }
57577                                 ],
57578                                 crossPoint: point
57579                             });
57580                             if (oneOnly) {
57581                                 checkedSingleCrossingWays[way2.id] = true;
57582                                 break;
57583                             }
57584                         }
57585                     }
57586                 }
57587                 return edgeCrossInfos;
57588             }
57589
57590
57591             function waysToCheck(entity, graph) {
57592                 var featureType = getFeatureType(entity, graph);
57593                 if (!featureType) return [];
57594
57595                 if (entity.type === 'way') {
57596                     return [entity];
57597                 } else if (entity.type === 'relation') {
57598                     return entity.members.reduce(function(array, member) {
57599                         if (member.type === 'way' &&
57600                             // only look at geometry ways
57601                             (!member.role || member.role === 'outer' || member.role === 'inner')) {
57602                             var entity = graph.hasEntity(member.id);
57603                             // don't add duplicates
57604                             if (entity && array.indexOf(entity) === -1) {
57605                                 array.push(entity);
57606                             }
57607                         }
57608                         return array;
57609                     }, []);
57610                 }
57611                 return [];
57612             }
57613
57614
57615             var validation = function checkCrossingWays(entity, graph) {
57616
57617                 var tree = context.history().tree();
57618
57619                 var ways = waysToCheck(entity, graph);
57620
57621                 var issues = [];
57622                 // declare these here to reduce garbage collection
57623                 var wayIndex, crossingIndex, crossings;
57624                 for (wayIndex in ways) {
57625                     crossings = findCrossingsByWay(ways[wayIndex], graph, tree);
57626                     for (crossingIndex in crossings) {
57627                         issues.push(createIssue(crossings[crossingIndex], graph));
57628                     }
57629                 }
57630                 return issues;
57631             };
57632
57633
57634             function createIssue(crossing, graph) {
57635
57636                 // use the entities with the tags that define the feature type
57637                 crossing.wayInfos.sort(function(way1Info, way2Info) {
57638                     var type1 = way1Info.featureType;
57639                     var type2 = way2Info.featureType;
57640                     if (type1 === type2) {
57641                         return utilDisplayLabel(way1Info.way, graph) > utilDisplayLabel(way2Info.way, graph);
57642                     } else if (type1 === 'waterway') {
57643                         return true;
57644                     } else if (type2 === 'waterway') {
57645                         return false;
57646                     }
57647                     return type1 < type2;
57648                 });
57649                 var entities = crossing.wayInfos.map(function(wayInfo) {
57650                     return getFeatureWithFeatureTypeTagsForWay(wayInfo.way, graph);
57651                 });
57652                 var edges = [crossing.wayInfos[0].edge, crossing.wayInfos[1].edge];
57653                 var featureTypes = [crossing.wayInfos[0].featureType, crossing.wayInfos[1].featureType];
57654
57655                 var connectionTags = tagsForConnectionNodeIfAllowed(entities[0], entities[1], graph);
57656
57657                 var featureType1 = crossing.wayInfos[0].featureType;
57658                 var featureType2 = crossing.wayInfos[1].featureType;
57659
57660                 var isCrossingIndoors = taggedAsIndoor(entities[0].tags) && taggedAsIndoor(entities[1].tags);
57661                 var isCrossingTunnels = allowsTunnel(featureType1) && hasTag(entities[0].tags, 'tunnel') &&
57662                                         allowsTunnel(featureType2) && hasTag(entities[1].tags, 'tunnel');
57663                 var isCrossingBridges = allowsBridge(featureType1) && hasTag(entities[0].tags, 'bridge') &&
57664                                         allowsBridge(featureType2) && hasTag(entities[1].tags, 'bridge');
57665
57666                 var subtype = [featureType1, featureType2].sort().join('-');
57667
57668                 var crossingTypeID = subtype;
57669
57670                 if (isCrossingIndoors) {
57671                     crossingTypeID = 'indoor-indoor';
57672                 } else if (isCrossingTunnels) {
57673                     crossingTypeID = 'tunnel-tunnel';
57674                 } else if (isCrossingBridges) {
57675                     crossingTypeID = 'bridge-bridge';
57676                 }
57677                 if (connectionTags && (isCrossingIndoors || isCrossingTunnels || isCrossingBridges)) {
57678                     crossingTypeID += '_connectable';
57679                 }
57680
57681                 return new validationIssue({
57682                     type: type,
57683                     subtype: subtype,
57684                     severity: 'warning',
57685                     message: function(context) {
57686                         var graph = context.graph();
57687                         var entity1 = graph.hasEntity(this.entityIds[0]),
57688                             entity2 = graph.hasEntity(this.entityIds[1]);
57689                         return (entity1 && entity2) ? _t('issues.crossing_ways.message', {
57690                             feature: utilDisplayLabel(entity1, graph),
57691                             feature2: utilDisplayLabel(entity2, graph)
57692                         }) : '';
57693                     },
57694                     reference: showReference,
57695                     entityIds: entities.map(function(entity) {
57696                         return entity.id;
57697                     }),
57698                     data: {
57699                         edges: edges,
57700                         featureTypes: featureTypes,
57701                         connectionTags: connectionTags
57702                     },
57703                     // differentiate based on the loc since two ways can cross multiple times
57704                     hash: crossing.crossPoint.toString() +
57705                         // if the edges change then so does the fix
57706                         edges.slice().sort(function(edge1, edge2) {
57707                             // order to assure hash is deterministic
57708                             return edge1[0] < edge2[0] ? -1 : 1;
57709                         }).toString() +
57710                         // ensure the correct connection tags are added in the fix
57711                         JSON.stringify(connectionTags),
57712                     loc: crossing.crossPoint,
57713                     dynamicFixes: function(context) {
57714                         var mode = context.mode();
57715                         if (!mode || mode.id !== 'select' || mode.selectedIDs().length !== 1) return [];
57716
57717                         var selectedIndex = this.entityIds[0] === mode.selectedIDs()[0] ? 0 : 1;
57718                         var selectedFeatureType = this.data.featureTypes[selectedIndex];
57719                         var otherFeatureType = this.data.featureTypes[selectedIndex === 0 ? 1 : 0];
57720
57721                         var fixes = [];
57722
57723                         if (connectionTags) {
57724                             fixes.push(makeConnectWaysFix(this.data.connectionTags));
57725                         }
57726
57727                         if (isCrossingIndoors) {
57728                             fixes.push(new validationIssueFix({
57729                                 icon: 'iD-icon-layers',
57730                                 title: _t('issues.fix.use_different_levels.title')
57731                             }));
57732                         } else if (isCrossingTunnels ||
57733                             isCrossingBridges ||
57734                             featureType1 === 'building' ||
57735                             featureType2 === 'building')  {
57736
57737                             fixes.push(makeChangeLayerFix('higher'));
57738                             fixes.push(makeChangeLayerFix('lower'));
57739
57740                         // can only add bridge/tunnel if both features are lines
57741                         } else if (context.graph().geometry(this.entityIds[0]) === 'line' &&
57742                             context.graph().geometry(this.entityIds[1]) === 'line') {
57743
57744                             // don't recommend adding bridges to waterways since they're uncommmon
57745                             if (allowsBridge(selectedFeatureType) && selectedFeatureType !== 'waterway') {
57746                                 fixes.push(makeAddBridgeOrTunnelFix('add_a_bridge', 'temaki-bridge', 'bridge'));
57747                             }
57748
57749                             // don't recommend adding tunnels under waterways since they're uncommmon
57750                             var skipTunnelFix = otherFeatureType === 'waterway' && selectedFeatureType !== 'waterway';
57751                             if (allowsTunnel(selectedFeatureType) && !skipTunnelFix) {
57752                                 fixes.push(makeAddBridgeOrTunnelFix('add_a_tunnel', 'temaki-tunnel', 'tunnel'));
57753                             }
57754                         }
57755
57756                         // repositioning the features is always an option
57757                         fixes.push(new validationIssueFix({
57758                             icon: 'iD-operation-move',
57759                             title: _t('issues.fix.reposition_features.title')
57760                         }));
57761
57762                         return fixes;
57763                     }
57764                 });
57765
57766                 function showReference(selection) {
57767                     selection.selectAll('.issue-reference')
57768                         .data([0])
57769                         .enter()
57770                         .append('div')
57771                         .attr('class', 'issue-reference')
57772                         .text(_t('issues.crossing_ways.' + crossingTypeID + '.reference'));
57773                 }
57774             }
57775
57776             function makeAddBridgeOrTunnelFix(fixTitleID, iconName, bridgeOrTunnel){
57777                 return new validationIssueFix({
57778                     icon: iconName,
57779                     title: _t('issues.fix.' + fixTitleID + '.title'),
57780                     onClick: function(context) {
57781                         var mode = context.mode();
57782                         if (!mode || mode.id !== 'select') return;
57783
57784                         var selectedIDs = mode.selectedIDs();
57785                         if (selectedIDs.length !== 1) return;
57786
57787                         var selectedWayID = selectedIDs[0];
57788                         if (!context.hasEntity(selectedWayID)) return;
57789
57790                         var resultWayIDs = [selectedWayID];
57791
57792                         var edge, crossedEdge, crossedWayID;
57793                         if (this.issue.entityIds[0] === selectedWayID) {
57794                             edge = this.issue.data.edges[0];
57795                             crossedEdge = this.issue.data.edges[1];
57796                             crossedWayID = this.issue.entityIds[1];
57797                         } else {
57798                             edge = this.issue.data.edges[1];
57799                             crossedEdge = this.issue.data.edges[0];
57800                             crossedWayID = this.issue.entityIds[0];
57801                         }
57802
57803                         var crossingLoc = this.issue.loc;
57804
57805                         var projection = context.projection;
57806
57807                         var action = function actionAddStructure(graph) {
57808
57809                             var edgeNodes = [graph.entity(edge[0]), graph.entity(edge[1])];
57810
57811                             var crossedWay = graph.hasEntity(crossedWayID);
57812                             // use the explicit width of the crossed feature as the structure length, if available
57813                             var structLengthMeters = crossedWay && crossedWay.tags.width && parseFloat(crossedWay.tags.width);
57814                             if (!structLengthMeters) {
57815                                 // if no explicit width is set, approximate the width based on the tags
57816                                 structLengthMeters = crossedWay && crossedWay.impliedLineWidthMeters();
57817                             }
57818                             if (structLengthMeters) {
57819                                 if (getFeatureType(crossedWay, graph) === 'railway') {
57820                                     // bridges over railways are generally much longer than the rail bed itself, compensate
57821                                     structLengthMeters *= 2;
57822                                 }
57823                             } else {
57824                                 // should ideally never land here since all rail/water/road tags should have an implied width
57825                                 structLengthMeters = 8;
57826                             }
57827
57828                             var a1 = geoAngle(edgeNodes[0], edgeNodes[1], projection) + Math.PI;
57829                             var a2 = geoAngle(graph.entity(crossedEdge[0]), graph.entity(crossedEdge[1]), projection) + Math.PI;
57830                             var crossingAngle = Math.max(a1, a2) - Math.min(a1, a2);
57831                             if (crossingAngle > Math.PI) crossingAngle -= Math.PI;
57832                             // lengthen the structure to account for the angle of the crossing
57833                             structLengthMeters = ((structLengthMeters / 2) / Math.sin(crossingAngle)) * 2;
57834
57835                             // add padding since the structure must extend past the edges of the crossed feature
57836                             structLengthMeters += 4;
57837
57838                             // clamp the length to a reasonable range
57839                             structLengthMeters = Math.min(Math.max(structLengthMeters, 4), 50);
57840
57841                             function geomToProj(geoPoint) {
57842                                 return [
57843                                     geoLonToMeters(geoPoint[0], geoPoint[1]),
57844                                     geoLatToMeters(geoPoint[1])
57845                                 ];
57846                             }
57847                             function projToGeom(projPoint) {
57848                                 var lat = geoMetersToLat(projPoint[1]);
57849                                 return [
57850                                     geoMetersToLon(projPoint[0], lat),
57851                                     lat
57852                                 ];
57853                             }
57854
57855                             var projEdgeNode1 = geomToProj(edgeNodes[0].loc);
57856                             var projEdgeNode2 = geomToProj(edgeNodes[1].loc);
57857
57858                             var projectedAngle = geoVecAngle(projEdgeNode1, projEdgeNode2);
57859
57860                             var projectedCrossingLoc = geomToProj(crossingLoc);
57861                             var linearToSphericalMetersRatio = geoVecLength(projEdgeNode1, projEdgeNode2) /
57862                                 geoSphericalDistance(edgeNodes[0].loc, edgeNodes[1].loc);
57863
57864                             function locSphericalDistanceFromCrossingLoc(angle, distanceMeters) {
57865                                 var lengthSphericalMeters = distanceMeters * linearToSphericalMetersRatio;
57866                                 return projToGeom([
57867                                     projectedCrossingLoc[0] + Math.cos(angle) * lengthSphericalMeters,
57868                                     projectedCrossingLoc[1] + Math.sin(angle) * lengthSphericalMeters
57869                                 ]);
57870                             }
57871
57872                             var endpointLocGetter1 = function(lengthMeters) {
57873                                 return locSphericalDistanceFromCrossingLoc(projectedAngle, lengthMeters);
57874                             };
57875                             var endpointLocGetter2 = function(lengthMeters) {
57876                                 return locSphericalDistanceFromCrossingLoc(projectedAngle + Math.PI, lengthMeters);
57877                             };
57878
57879                             // avoid creating very short edges from splitting too close to another node
57880                             var minEdgeLengthMeters = 0.55;
57881
57882                             // decide where to bound the structure along the way, splitting as necessary
57883                             function determineEndpoint(edge, endNode, locGetter) {
57884                                 var newNode;
57885
57886                                 var idealLengthMeters = structLengthMeters / 2;
57887
57888                                 // distance between the crossing location and the end of the edge,
57889                                 // the maximum length of this side of the structure
57890                                 var crossingToEdgeEndDistance = geoSphericalDistance(crossingLoc, endNode.loc);
57891
57892                                 if (crossingToEdgeEndDistance - idealLengthMeters > minEdgeLengthMeters) {
57893                                     // the edge is long enough to insert a new node
57894
57895                                     // the loc that would result in the full expected length
57896                                     var idealNodeLoc = locGetter(idealLengthMeters);
57897
57898                                     newNode = osmNode();
57899                                     graph = actionAddMidpoint({ loc: idealNodeLoc, edge: edge }, newNode)(graph);
57900
57901                                 } else {
57902                                     var edgeCount = 0;
57903                                     endNode.parentIntersectionWays(graph).forEach(function(way) {
57904                                         way.nodes.forEach(function(nodeID) {
57905                                             if (nodeID === endNode.id) {
57906                                                 if ((endNode.id === way.first() && endNode.id !== way.last()) ||
57907                                                     (endNode.id === way.last() && endNode.id !== way.first())) {
57908                                                     edgeCount += 1;
57909                                                 } else {
57910                                                     edgeCount += 2;
57911                                                 }
57912                                             }
57913                                         });
57914                                     });
57915
57916                                     if (edgeCount >= 3) {
57917                                         // the end node is a junction, try to leave a segment
57918                                         // between it and the structure - #7202
57919
57920                                         var insetLength = crossingToEdgeEndDistance - minEdgeLengthMeters;
57921                                         if (insetLength > minEdgeLengthMeters) {
57922                                             var insetNodeLoc = locGetter(insetLength);
57923                                             newNode = osmNode();
57924                                             graph = actionAddMidpoint({ loc: insetNodeLoc, edge: edge }, newNode)(graph);
57925                                         }
57926                                     }
57927                                 }
57928
57929                                 // if the edge is too short to subdivide as desired, then
57930                                 // just bound the structure at the existing end node
57931                                 if (!newNode) newNode = endNode;
57932
57933                                 var splitAction = actionSplit(newNode.id)
57934                                     .limitWays(resultWayIDs); // only split selected or created ways
57935
57936                                 // do the split
57937                                 graph = splitAction(graph);
57938                                 if (splitAction.getCreatedWayIDs().length) {
57939                                     resultWayIDs.push(splitAction.getCreatedWayIDs()[0]);
57940                                 }
57941
57942                                 return newNode;
57943                             }
57944
57945                             var structEndNode1 = determineEndpoint(edge, edgeNodes[1], endpointLocGetter1);
57946                             var structEndNode2 = determineEndpoint([edgeNodes[0].id, structEndNode1.id], edgeNodes[0], endpointLocGetter2);
57947
57948                             var structureWay = resultWayIDs.map(function(id) {
57949                                 return graph.entity(id);
57950                             }).find(function(way) {
57951                                 return way.nodes.indexOf(structEndNode1.id) !== -1 &&
57952                                     way.nodes.indexOf(structEndNode2.id) !== -1;
57953                             });
57954
57955                             var tags = Object.assign({}, structureWay.tags); // copy tags
57956                             if (bridgeOrTunnel === 'bridge'){
57957                                 tags.bridge = 'yes';
57958                                 tags.layer = '1';
57959                             } else {
57960                                 var tunnelValue = 'yes';
57961                                 if (getFeatureType(structureWay, graph) === 'waterway') {
57962                                     // use `tunnel=culvert` for waterways by default
57963                                     tunnelValue = 'culvert';
57964                                 }
57965                                 tags.tunnel = tunnelValue;
57966                                 tags.layer = '-1';
57967                             }
57968                             // apply the structure tags to the way
57969                             graph = actionChangeTags(structureWay.id, tags)(graph);
57970                             return graph;
57971                         };
57972
57973                         context.perform(action, _t('issues.fix.' + fixTitleID + '.annotation'));
57974                         context.enter(modeSelect(context, resultWayIDs));
57975                     }
57976                 });
57977             }
57978
57979             function makeConnectWaysFix(connectionTags) {
57980
57981                 var fixTitleID = 'connect_features';
57982                 if (connectionTags.ford) {
57983                     fixTitleID = 'connect_using_ford';
57984                 }
57985
57986                 return new validationIssueFix({
57987                     icon: 'iD-icon-crossing',
57988                     title: _t('issues.fix.' + fixTitleID + '.title'),
57989                     onClick: function(context) {
57990                         var loc = this.issue.loc;
57991                         var connectionTags = this.issue.data.connectionTags;
57992                         var edges = this.issue.data.edges;
57993
57994                         context.perform(
57995                             function actionConnectCrossingWays(graph) {
57996                                 // create the new node for the points
57997                                 var node = osmNode({ loc: loc, tags: connectionTags });
57998                                 graph = graph.replace(node);
57999
58000                                 var nodesToMerge = [node.id];
58001                                 var mergeThresholdInMeters = 0.75;
58002
58003                                 edges.forEach(function(edge) {
58004                                     var edgeNodes = [graph.entity(edge[0]), graph.entity(edge[1])];
58005                                     var closestNodeInfo = geoSphericalClosestNode(edgeNodes, loc);
58006                                     // if there is already a point nearby, use that
58007                                     if (closestNodeInfo.distance < mergeThresholdInMeters) {
58008                                         nodesToMerge.push(closestNodeInfo.node.id);
58009                                     // else add the new node to the way
58010                                     } else {
58011                                         graph = actionAddMidpoint({loc: loc, edge: edge}, node)(graph);
58012                                     }
58013                                 });
58014
58015                                 if (nodesToMerge.length > 1) {
58016                                     // if we're using nearby nodes, merge them with the new node
58017                                     graph = actionMergeNodes(nodesToMerge, loc)(graph);
58018                                 }
58019
58020                                 return graph;
58021                             },
58022                             _t('issues.fix.connect_crossing_features.annotation')
58023                         );
58024                     }
58025                 });
58026             }
58027
58028             function makeChangeLayerFix(higherOrLower) {
58029                 return new validationIssueFix({
58030                     icon: 'iD-icon-' + (higherOrLower === 'higher' ? 'up' : 'down'),
58031                     title: _t('issues.fix.tag_this_as_' + higherOrLower + '.title'),
58032                     onClick: function(context) {
58033
58034                         var mode = context.mode();
58035                         if (!mode || mode.id !== 'select') return;
58036
58037                         var selectedIDs = mode.selectedIDs();
58038                         if (selectedIDs.length !== 1) return;
58039
58040                         var selectedID = selectedIDs[0];
58041                         if (!this.issue.entityIds.some(function(entityId) {
58042                             return entityId === selectedID;
58043                         })) return;
58044
58045                         var entity = context.hasEntity(selectedID);
58046                         if (!entity) return;
58047
58048                         var tags = Object.assign({}, entity.tags);   // shallow copy
58049                         var layer = tags.layer && Number(tags.layer);
58050                         if (layer && !isNaN(layer)) {
58051                             if (higherOrLower === 'higher') {
58052                                 layer += 1;
58053                             } else {
58054                                 layer -= 1;
58055                             }
58056                         } else {
58057                             if (higherOrLower === 'higher') {
58058                                 layer = 1;
58059                             } else {
58060                                 layer = -1;
58061                             }
58062                         }
58063                         tags.layer = layer.toString();
58064                         context.perform(
58065                             actionChangeTags(entity.id, tags),
58066                             _t('operations.change_tags.annotation')
58067                         );
58068                     }
58069                 });
58070             }
58071
58072             validation.type = type;
58073
58074             return validation;
58075         }
58076
58077         function validationDisconnectedWay() {
58078             var type = 'disconnected_way';
58079
58080             function isTaggedAsHighway(entity) {
58081                 return osmRoutableHighwayTagValues[entity.tags.highway];
58082             }
58083
58084             var validation = function checkDisconnectedWay(entity, graph) {
58085
58086                 var routingIslandWays = routingIslandForEntity(entity);
58087                 if (!routingIslandWays) return [];
58088
58089                 return [new validationIssue({
58090                     type: type,
58091                     subtype: 'highway',
58092                     severity: 'warning',
58093                     message: function(context) {
58094                         if (this.entityIds.length === 1) {
58095                             var entity = context.hasEntity(this.entityIds[0]);
58096                             return entity ? _t('issues.disconnected_way.highway.message', { highway: utilDisplayLabel(entity, context.graph()) }) : '';
58097                         }
58098                         return _t('issues.disconnected_way.routable.message.multiple', { count: this.entityIds.length.toString() });
58099                     },
58100                     reference: showReference,
58101                     entityIds: Array.from(routingIslandWays).map(function(way) { return way.id; }),
58102                     dynamicFixes: makeFixes
58103                 })];
58104
58105
58106                 function makeFixes(context) {
58107
58108                     var fixes = [];
58109
58110                     var singleEntity = this.entityIds.length === 1 && context.hasEntity(this.entityIds[0]);
58111
58112                     if (singleEntity) {
58113
58114                         if (singleEntity.type === 'way' && !singleEntity.isClosed()) {
58115
58116                             var textDirection = _mainLocalizer.textDirection();
58117
58118                             var startFix = makeContinueDrawingFixIfAllowed(textDirection, singleEntity.first(), 'start');
58119                             if (startFix) fixes.push(startFix);
58120
58121                             var endFix = makeContinueDrawingFixIfAllowed(textDirection, singleEntity.last(), 'end');
58122                             if (endFix) fixes.push(endFix);
58123                         }
58124                         if (!fixes.length) {
58125                             fixes.push(new validationIssueFix({
58126                                 title: _t('issues.fix.connect_feature.title')
58127                             }));
58128                         }
58129
58130                         fixes.push(new validationIssueFix({
58131                             icon: 'iD-operation-delete',
58132                             title: _t('issues.fix.delete_feature.title'),
58133                             entityIds: [singleEntity.id],
58134                             onClick: function(context) {
58135                                 var id = this.issue.entityIds[0];
58136                                 var operation = operationDelete(context, [id]);
58137                                 if (!operation.disabled()) {
58138                                     operation();
58139                                 }
58140                             }
58141                         }));
58142                     } else {
58143                         fixes.push(new validationIssueFix({
58144                             title: _t('issues.fix.connect_features.title')
58145                         }));
58146                     }
58147
58148                     return fixes;
58149                 }
58150
58151
58152                 function showReference(selection) {
58153                     selection.selectAll('.issue-reference')
58154                         .data([0])
58155                         .enter()
58156                         .append('div')
58157                         .attr('class', 'issue-reference')
58158                         .text(_t('issues.disconnected_way.routable.reference'));
58159                 }
58160
58161                 function routingIslandForEntity(entity) {
58162
58163                     var routingIsland = new Set();  // the interconnected routable features
58164                     var waysToCheck = [];           // the queue of remaining routable ways to traverse
58165
58166                     function queueParentWays(node) {
58167                         graph.parentWays(node).forEach(function(parentWay) {
58168                             if (!routingIsland.has(parentWay) &&    // only check each feature once
58169                                 isRoutableWay(parentWay, false)) {  // only check routable features
58170                                 routingIsland.add(parentWay);
58171                                 waysToCheck.push(parentWay);
58172                             }
58173                         });
58174                     }
58175
58176                     if (entity.type === 'way' && isRoutableWay(entity, true)) {
58177
58178                         routingIsland.add(entity);
58179                         waysToCheck.push(entity);
58180
58181                     } else if (entity.type === 'node' && isRoutableNode(entity)) {
58182
58183                         routingIsland.add(entity);
58184                         queueParentWays(entity);
58185
58186                     } else {
58187                         // this feature isn't routable, cannot be a routing island
58188                         return null;
58189                     }
58190
58191                     while (waysToCheck.length) {
58192                         var wayToCheck = waysToCheck.pop();
58193                         var childNodes = graph.childNodes(wayToCheck);
58194                         for (var i in childNodes) {
58195                             var vertex = childNodes[i];
58196
58197                             if (isConnectedVertex(vertex)) {
58198                                 // found a link to the wider network, not a routing island
58199                                 return null;
58200                             }
58201
58202                             if (isRoutableNode(vertex)) {
58203                                 routingIsland.add(vertex);
58204                             }
58205
58206                             queueParentWays(vertex);
58207                         }
58208                     }
58209
58210                     // no network link found, this is a routing island, return its members
58211                     return routingIsland;
58212                 }
58213
58214                 function isConnectedVertex(vertex) {
58215                     // assume ways overlapping unloaded tiles are connected to the wider road network  - #5938
58216                     var osm = services.osm;
58217                     if (osm && !osm.isDataLoaded(vertex.loc)) return true;
58218
58219                     // entrances are considered connected
58220                     if (vertex.tags.entrance &&
58221                         vertex.tags.entrance !== 'no') return true;
58222                     if (vertex.tags.amenity === 'parking_entrance') return true;
58223
58224                     return false;
58225                 }
58226
58227                 function isRoutableNode(node) {
58228                     // treat elevators as distinct features in the highway network
58229                     if (node.tags.highway === 'elevator') return true;
58230                     return false;
58231                 }
58232
58233                 function isRoutableWay(way, ignoreInnerWays) {
58234                     if (isTaggedAsHighway(way) || way.tags.route === 'ferry') return true;
58235
58236                     return graph.parentRelations(way).some(function(parentRelation) {
58237                         if (parentRelation.tags.type === 'route' &&
58238                             parentRelation.tags.route === 'ferry') return true;
58239
58240                         if (parentRelation.isMultipolygon() &&
58241                             isTaggedAsHighway(parentRelation) &&
58242                             (!ignoreInnerWays || parentRelation.memberById(way.id).role !== 'inner')) return true;
58243                     });
58244                 }
58245
58246                 function makeContinueDrawingFixIfAllowed(textDirection, vertexID, whichEnd) {
58247                     var vertex = graph.hasEntity(vertexID);
58248                     if (!vertex || vertex.tags.noexit === 'yes') return null;
58249
58250                     var useLeftContinue = (whichEnd === 'start' && textDirection === 'ltr') ||
58251                         (whichEnd === 'end' && textDirection === 'rtl');
58252
58253                     return new validationIssueFix({
58254                         icon: 'iD-operation-continue' + (useLeftContinue ? '-left' : ''),
58255                         title: _t('issues.fix.continue_from_' + whichEnd + '.title'),
58256                         entityIds: [vertexID],
58257                         onClick: function(context) {
58258                             var wayId = this.issue.entityIds[0];
58259                             var way = context.hasEntity(wayId);
58260                             var vertexId = this.entityIds[0];
58261                             var vertex = context.hasEntity(vertexId);
58262
58263                             if (!way || !vertex) return;
58264
58265                             // make sure the vertex is actually visible and editable
58266                             var map = context.map();
58267                             if (!context.editable() || !map.trimmedExtent().contains(vertex.loc)) {
58268                                 map.zoomToEase(vertex);
58269                             }
58270
58271                             context.enter(
58272                                 modeDrawLine(context, wayId, context.graph(), 'line', way.affix(vertexId), true)
58273                             );
58274                         }
58275                     });
58276                 }
58277
58278             };
58279
58280             validation.type = type;
58281
58282             return validation;
58283         }
58284
58285         function validationFormatting() {
58286             var type = 'invalid_format';
58287
58288             var validation = function(entity) {
58289                 var issues = [];
58290
58291                 function isValidEmail(email) {
58292                     // Emails in OSM are going to be official so they should be pretty simple
58293                     // Using negated lists to better support all possible unicode characters (#6494)
58294                     var valid_email = /^[^\(\)\\,":;<>@\[\]]+@[^\(\)\\,":;<>@\[\]\.]+(?:\.[a-z0-9-]+)*$/i;
58295
58296                     // An empty value is also acceptable
58297                     return (!email || valid_email.test(email));
58298                 }
58299                 /*
58300                 function isSchemePresent(url) {
58301                     var valid_scheme = /^https?:\/\//i;
58302                     return (!url || valid_scheme.test(url));
58303                 }
58304                 */
58305                 function showReferenceEmail(selection) {
58306                     selection.selectAll('.issue-reference')
58307                         .data([0])
58308                         .enter()
58309                         .append('div')
58310                         .attr('class', 'issue-reference')
58311                         .text(_t('issues.invalid_format.email.reference'));
58312                 }
58313                 /*
58314                 function showReferenceWebsite(selection) {
58315                     selection.selectAll('.issue-reference')
58316                         .data([0])
58317                         .enter()
58318                         .append('div')
58319                         .attr('class', 'issue-reference')
58320                         .text(t('issues.invalid_format.website.reference'));
58321                 }
58322
58323                 if (entity.tags.website) {
58324                     // Multiple websites are possible
58325                     // If ever we support ES6, arrow functions make this nicer
58326                     var websites = entity.tags.website
58327                         .split(';')
58328                         .map(function(s) { return s.trim(); })
58329                         .filter(function(x) { return !isSchemePresent(x); });
58330
58331                     if (websites.length) {
58332                         issues.push(new validationIssue({
58333                             type: type,
58334                             subtype: 'website',
58335                             severity: 'warning',
58336                             message: function(context) {
58337                                 var entity = context.hasEntity(this.entityIds[0]);
58338                                 return entity ? t('issues.invalid_format.website.message' + this.data,
58339                                     { feature: utilDisplayLabel(entity, context.graph()), site: websites.join(', ') }) : '';
58340                             },
58341                             reference: showReferenceWebsite,
58342                             entityIds: [entity.id],
58343                             hash: websites.join(),
58344                             data: (websites.length > 1) ? '_multi' : ''
58345                         }));
58346                     }
58347                 }
58348                 */
58349                 if (entity.tags.email) {
58350                     // Multiple emails are possible
58351                     var emails = entity.tags.email
58352                         .split(';')
58353                         .map(function(s) { return s.trim(); })
58354                         .filter(function(x) { return !isValidEmail(x); });
58355
58356                     if (emails.length) {
58357                         issues.push(new validationIssue({
58358                             type: type,
58359                             subtype: 'email',
58360                             severity: 'warning',
58361                             message: function(context) {
58362                                 var entity = context.hasEntity(this.entityIds[0]);
58363                                 return entity ? _t('issues.invalid_format.email.message' + this.data,
58364                                     { feature: utilDisplayLabel(entity, context.graph()), email: emails.join(', ') }) : '';
58365                             },
58366                             reference: showReferenceEmail,
58367                             entityIds: [entity.id],
58368                             hash: emails.join(),
58369                             data: (emails.length > 1) ? '_multi' : ''
58370                         }));
58371                     }
58372                 }
58373
58374                 return issues;
58375             };
58376
58377             validation.type = type;
58378
58379             return validation;
58380         }
58381
58382         function validationHelpRequest(context) {
58383             var type = 'help_request';
58384
58385             var validation = function checkFixmeTag(entity) {
58386
58387                 if (!entity.tags.fixme) return [];
58388
58389                 // don't flag fixmes on features added by the user
58390                 if (entity.version === undefined) return [];
58391
58392                 if (entity.v !== undefined) {
58393                     var baseEntity = context.history().base().hasEntity(entity.id);
58394                     // don't flag fixmes added by the user on existing features
58395                     if (!baseEntity || !baseEntity.tags.fixme) return [];
58396                 }
58397
58398                 return [new validationIssue({
58399                     type: type,
58400                     subtype: 'fixme_tag',
58401                     severity: 'warning',
58402                     message: function(context) {
58403                         var entity = context.hasEntity(this.entityIds[0]);
58404                         return entity ? _t('issues.fixme_tag.message', { feature: utilDisplayLabel(entity, context.graph()) }) : '';
58405                     },
58406                     dynamicFixes: function() {
58407                         return [
58408                             new validationIssueFix({
58409                                 title: _t('issues.fix.address_the_concern.title')
58410                             })
58411                         ];
58412                     },
58413                     reference: showReference,
58414                     entityIds: [entity.id]
58415                 })];
58416
58417                 function showReference(selection) {
58418                     selection.selectAll('.issue-reference')
58419                         .data([0])
58420                         .enter()
58421                         .append('div')
58422                         .attr('class', 'issue-reference')
58423                         .text(_t('issues.fixme_tag.reference'));
58424                 }
58425             };
58426
58427             validation.type = type;
58428
58429             return validation;
58430         }
58431
58432         function validationImpossibleOneway() {
58433             var type = 'impossible_oneway';
58434
58435             var validation = function checkImpossibleOneway(entity, graph) {
58436
58437                 if (entity.type !== 'way' || entity.geometry(graph) !== 'line') return [];
58438
58439                 if (entity.isClosed()) return [];
58440
58441                 if (!typeForWay(entity)) return [];
58442
58443                 if (!isOneway(entity)) return [];
58444
58445                 var firstIssues = issuesForNode(entity, entity.first());
58446                 var lastIssues = issuesForNode(entity, entity.last());
58447
58448                 return firstIssues.concat(lastIssues);
58449
58450                 function typeForWay(way) {
58451                     if (way.geometry(graph) !== 'line') return null;
58452
58453                     if (osmRoutableHighwayTagValues[way.tags.highway]) return 'highway';
58454                     if (osmFlowingWaterwayTagValues[way.tags.waterway]) return 'waterway';
58455                     return null;
58456                 }
58457
58458                 function isOneway(way) {
58459                     if (way.tags.oneway === 'yes') return true;
58460                     if (way.tags.oneway) return false;
58461
58462                     for (var key in way.tags) {
58463                         if (osmOneWayTags[key] && osmOneWayTags[key][way.tags[key]]) {
58464                             return true;
58465                         }
58466                     }
58467                     return false;
58468                 }
58469
58470                 function nodeOccursMoreThanOnce(way, nodeID) {
58471                     var occurences = 0;
58472                     for (var index in way.nodes) {
58473                         if (way.nodes[index] === nodeID) {
58474                             occurences += 1;
58475                             if (occurences > 1) return true;
58476                         }
58477                     }
58478                     return false;
58479                 }
58480
58481                 function isConnectedViaOtherTypes(way, node) {
58482
58483                     var wayType = typeForWay(way);
58484
58485                     if (wayType === 'highway') {
58486                         // entrances are considered connected
58487                         if (node.tags.entrance && node.tags.entrance !== 'no') return true;
58488                         if (node.tags.amenity === 'parking_entrance') return true;
58489                     } else if (wayType === 'waterway') {
58490                         if (node.id === way.first()) {
58491                             // multiple waterways may start at the same spring
58492                             if (node.tags.natural === 'spring') return true;
58493                         } else {
58494                             // multiple waterways may end at the same drain
58495                             if (node.tags.manhole === 'drain') return true;
58496                         }
58497                     }
58498
58499                     return graph.parentWays(node).some(function(parentWay) {
58500                         if (parentWay.id === way.id) return false;
58501
58502                         if (wayType === 'highway') {
58503
58504                             // allow connections to highway areas
58505                             if (parentWay.geometry(graph) === 'area' &&
58506                                 osmRoutableHighwayTagValues[parentWay.tags.highway]) return true;
58507
58508                             // count connections to ferry routes as connected
58509                             if (parentWay.tags.route === 'ferry') return true;
58510
58511                             return graph.parentRelations(parentWay).some(function(parentRelation) {
58512                                 if (parentRelation.tags.type === 'route' &&
58513                                     parentRelation.tags.route === 'ferry') return true;
58514
58515                                 // allow connections to highway multipolygons
58516                                 return parentRelation.isMultipolygon() && osmRoutableHighwayTagValues[parentRelation.tags.highway];
58517                             });
58518                         } else if (wayType === 'waterway') {
58519                             // multiple waterways may start or end at a water body at the same node
58520                             if (parentWay.tags.natural === 'water' ||
58521                                 parentWay.tags.natural === 'coastline') return true;
58522                         }
58523                         return false;
58524                     });
58525                 }
58526
58527                 function issuesForNode(way, nodeID) {
58528
58529                     var isFirst = nodeID === way.first();
58530
58531                     var wayType = typeForWay(way);
58532
58533                     // ignore if this way is self-connected at this node
58534                     if (nodeOccursMoreThanOnce(way, nodeID)) return [];
58535
58536                     var osm = services.osm;
58537                     if (!osm) return [];
58538
58539                     var node = graph.hasEntity(nodeID);
58540
58541                     // ignore if this node or its tile are unloaded
58542                     if (!node || !osm.isDataLoaded(node.loc)) return [];
58543
58544                     if (isConnectedViaOtherTypes(way, node)) return [];
58545
58546                     var attachedWaysOfSameType = graph.parentWays(node).filter(function(parentWay) {
58547                         if (parentWay.id === way.id) return false;
58548                         return typeForWay(parentWay) === wayType;
58549                     });
58550
58551                     // assume it's okay for waterways to start or end disconnected for now
58552                     if (wayType === 'waterway' && attachedWaysOfSameType.length === 0) return [];
58553
58554                     var attachedOneways = attachedWaysOfSameType.filter(function(attachedWay) {
58555                         return isOneway(attachedWay);
58556                     });
58557
58558                     // ignore if the way is connected to some non-oneway features
58559                     if (attachedOneways.length < attachedWaysOfSameType.length) return [];
58560
58561                     if (attachedOneways.length) {
58562                         var connectedEndpointsOkay = attachedOneways.some(function(attachedOneway) {
58563                             if ((isFirst ? attachedOneway.first() : attachedOneway.last()) !== nodeID) return true;
58564                             if (nodeOccursMoreThanOnce(attachedOneway, nodeID)) return true;
58565                             return false;
58566                         });
58567                         if (connectedEndpointsOkay) return [];
58568                     }
58569
58570                     var placement = isFirst ? 'start' : 'end',
58571                         messageID = wayType + '.',
58572                         referenceID = wayType + '.';
58573
58574                     if (wayType === 'waterway') {
58575                         messageID += 'connected.' + placement;
58576                         referenceID += 'connected';
58577                     } else {
58578                         messageID += placement;
58579                         referenceID += placement;
58580                     }
58581
58582                     return [new validationIssue({
58583                         type: type,
58584                         subtype: wayType,
58585                         severity: 'warning',
58586                         message: function(context) {
58587                             var entity = context.hasEntity(this.entityIds[0]);
58588                             return entity ? _t('issues.impossible_oneway.' + messageID + '.message', {
58589                                 feature: utilDisplayLabel(entity, context.graph())
58590                             }) : '';
58591                         },
58592                         reference: getReference(referenceID),
58593                         entityIds: [way.id, node.id],
58594                         dynamicFixes: function() {
58595
58596                             var fixes = [];
58597
58598                             if (attachedOneways.length) {
58599                                 fixes.push(new validationIssueFix({
58600                                     icon: 'iD-operation-reverse',
58601                                     title: _t('issues.fix.reverse_feature.title'),
58602                                     entityIds: [way.id],
58603                                     onClick: function(context) {
58604                                         var id = this.issue.entityIds[0];
58605                                         context.perform(actionReverse(id), _t('operations.reverse.annotation'));
58606                                     }
58607                                 }));
58608                             }
58609                             if (node.tags.noexit !== 'yes') {
58610                                 var textDirection = _mainLocalizer.textDirection();
58611                                 var useLeftContinue = (isFirst && textDirection === 'ltr') ||
58612                                     (!isFirst && textDirection === 'rtl');
58613                                 fixes.push(new validationIssueFix({
58614                                     icon: 'iD-operation-continue' + (useLeftContinue ? '-left' : ''),
58615                                     title: _t('issues.fix.continue_from_' + (isFirst ? 'start' : 'end') + '.title'),
58616                                     onClick: function(context) {
58617                                         var entityID = this.issue.entityIds[0];
58618                                         var vertexID = this.issue.entityIds[1];
58619                                         var way = context.entity(entityID);
58620                                         var vertex = context.entity(vertexID);
58621                                         continueDrawing(way, vertex, context);
58622                                     }
58623                                 }));
58624                             }
58625
58626                             return fixes;
58627                         },
58628                         loc: node.loc
58629                     })];
58630
58631                     function getReference(referenceID) {
58632                         return function showReference(selection) {
58633                             selection.selectAll('.issue-reference')
58634                                 .data([0])
58635                                 .enter()
58636                                 .append('div')
58637                                 .attr('class', 'issue-reference')
58638                                 .text(_t('issues.impossible_oneway.' + referenceID + '.reference'));
58639                         };
58640                     }
58641                 }
58642             };
58643
58644             function continueDrawing(way, vertex, context) {
58645                 // make sure the vertex is actually visible and editable
58646                 var map = context.map();
58647                 if (!context.editable() || !map.trimmedExtent().contains(vertex.loc)) {
58648                     map.zoomToEase(vertex);
58649                 }
58650
58651                 context.enter(
58652                     modeDrawLine(context, way.id, context.graph(), 'line', way.affix(vertex.id), true)
58653                 );
58654             }
58655
58656             validation.type = type;
58657
58658             return validation;
58659         }
58660
58661         function validationIncompatibleSource() {
58662             var type = 'incompatible_source';
58663             var invalidSources = [
58664                 {
58665                     id:'google', regex:'google', exceptRegex: 'books.google|Google Books|drive.google|googledrive|Google Drive'
58666                 }
58667             ];
58668
58669             var validation = function checkIncompatibleSource(entity) {
58670
58671                 var entitySources = entity.tags && entity.tags.source && entity.tags.source.split(';');
58672
58673                 if (!entitySources) return [];
58674
58675                 var issues = [];
58676
58677                 invalidSources.forEach(function(invalidSource) {
58678
58679                     var hasInvalidSource = entitySources.some(function(source) {
58680                         if (!source.match(new RegExp(invalidSource.regex, 'i'))) return false;
58681                         if (invalidSource.exceptRegex && source.match(new RegExp(invalidSource.exceptRegex, 'i'))) return false;
58682                         return true;
58683                     });
58684
58685                     if (!hasInvalidSource) return;
58686
58687                     issues.push(new validationIssue({
58688                         type: type,
58689                         severity: 'warning',
58690                         message: function(context) {
58691                             var entity = context.hasEntity(this.entityIds[0]);
58692                             return entity ? _t('issues.incompatible_source.' + invalidSource.id + '.feature.message', {
58693                                 feature: utilDisplayLabel(entity, context.graph())
58694                             }) : '';
58695                         },
58696                         reference: getReference(invalidSource.id),
58697                         entityIds: [entity.id],
58698                         dynamicFixes: function() {
58699                             return [
58700                                 new validationIssueFix({
58701                                     title: _t('issues.fix.remove_proprietary_data.title')
58702                                 })
58703                             ];
58704                         }
58705                     }));
58706                 });
58707
58708                 return issues;
58709
58710
58711                 function getReference(id) {
58712                     return function showReference(selection) {
58713                         selection.selectAll('.issue-reference')
58714                             .data([0])
58715                             .enter()
58716                             .append('div')
58717                             .attr('class', 'issue-reference')
58718                             .text(_t('issues.incompatible_source.' + id + '.reference'));
58719                     };
58720                 }
58721             };
58722
58723             validation.type = type;
58724
58725             return validation;
58726         }
58727
58728         function validationMaprules() {
58729             var type = 'maprules';
58730
58731             var validation = function checkMaprules(entity, graph) {
58732                 if (!services.maprules) return [];
58733
58734                 var rules = services.maprules.validationRules();
58735                 var issues = [];
58736
58737                 for (var i = 0; i < rules.length; i++) {
58738                     var rule = rules[i];
58739                     rule.findIssues(entity, graph, issues);
58740                 }
58741
58742                 return issues;
58743             };
58744
58745
58746             validation.type = type;
58747
58748             return validation;
58749         }
58750
58751         function validationMismatchedGeometry() {
58752             var type = 'mismatched_geometry';
58753
58754             function tagSuggestingLineIsArea(entity) {
58755                 if (entity.type !== 'way' || entity.isClosed()) return null;
58756
58757                 var tagSuggestingArea = entity.tagSuggestingArea();
58758                 if (!tagSuggestingArea) {
58759                     return null;
58760                 }
58761
58762                 var asLine = _mainPresetIndex.matchTags(tagSuggestingArea, 'line');
58763                 var asArea = _mainPresetIndex.matchTags(tagSuggestingArea, 'area');
58764                 if (asLine && asArea && (asLine === asArea)) {
58765                     // these tags also allow lines and making this an area wouldn't matter
58766                     return null;
58767                 }
58768
58769                 return tagSuggestingArea;
58770             }
58771
58772
58773             function makeConnectEndpointsFixOnClick(way, graph) {
58774                 // must have at least three nodes to close this automatically
58775                 if (way.nodes.length < 3) return null;
58776
58777                 var nodes = graph.childNodes(way), testNodes;
58778                 var firstToLastDistanceMeters = geoSphericalDistance(nodes[0].loc, nodes[nodes.length-1].loc);
58779
58780                 // if the distance is very small, attempt to merge the endpoints
58781                 if (firstToLastDistanceMeters < 0.75) {
58782                     testNodes = nodes.slice();   // shallow copy
58783                     testNodes.pop();
58784                     testNodes.push(testNodes[0]);
58785                     // make sure this will not create a self-intersection
58786                     if (!geoHasSelfIntersections(testNodes, testNodes[0].id)) {
58787                         return function(context) {
58788                             var way = context.entity(this.issue.entityIds[0]);
58789                             context.perform(
58790                                 actionMergeNodes([way.nodes[0], way.nodes[way.nodes.length-1]], nodes[0].loc),
58791                                 _t('issues.fix.connect_endpoints.annotation')
58792                             );
58793                         };
58794                     }
58795                 }
58796
58797                 // if the points were not merged, attempt to close the way
58798                 testNodes = nodes.slice();   // shallow copy
58799                 testNodes.push(testNodes[0]);
58800                 // make sure this will not create a self-intersection
58801                 if (!geoHasSelfIntersections(testNodes, testNodes[0].id)) {
58802                     return function(context) {
58803                         var wayId = this.issue.entityIds[0];
58804                         var way = context.entity(wayId);
58805                         var nodeId = way.nodes[0];
58806                         var index = way.nodes.length;
58807                         context.perform(
58808                             actionAddVertex(wayId, nodeId, index),
58809                             _t('issues.fix.connect_endpoints.annotation')
58810                         );
58811                     };
58812                 }
58813             }
58814
58815             function lineTaggedAsAreaIssue(entity) {
58816
58817                 var tagSuggestingArea = tagSuggestingLineIsArea(entity);
58818                 if (!tagSuggestingArea) return null;
58819
58820                 return new validationIssue({
58821                     type: type,
58822                     subtype: 'area_as_line',
58823                     severity: 'warning',
58824                     message: function(context) {
58825                         var entity = context.hasEntity(this.entityIds[0]);
58826                         return entity ? _t('issues.tag_suggests_area.message', {
58827                             feature: utilDisplayLabel(entity, context.graph()),
58828                             tag: utilTagText({ tags: tagSuggestingArea })
58829                         }) : '';
58830                     },
58831                     reference: showReference,
58832                     entityIds: [entity.id],
58833                     hash: JSON.stringify(tagSuggestingArea),
58834                     dynamicFixes: function(context) {
58835
58836                         var fixes = [];
58837
58838                         var entity = context.entity(this.entityIds[0]);
58839                         var connectEndsOnClick = makeConnectEndpointsFixOnClick(entity, context.graph());
58840
58841                         fixes.push(new validationIssueFix({
58842                             title: _t('issues.fix.connect_endpoints.title'),
58843                             onClick: connectEndsOnClick
58844                         }));
58845
58846                         fixes.push(new validationIssueFix({
58847                             icon: 'iD-operation-delete',
58848                             title: _t('issues.fix.remove_tag.title'),
58849                             onClick: function(context) {
58850                                 var entityId = this.issue.entityIds[0];
58851                                 var entity = context.entity(entityId);
58852                                 var tags = Object.assign({}, entity.tags);  // shallow copy
58853                                 for (var key in tagSuggestingArea) {
58854                                     delete tags[key];
58855                                 }
58856                                 context.perform(
58857                                     actionChangeTags(entityId, tags),
58858                                     _t('issues.fix.remove_tag.annotation')
58859                                 );
58860                             }
58861                         }));
58862
58863                         return fixes;
58864                     }
58865                 });
58866
58867
58868                 function showReference(selection) {
58869                     selection.selectAll('.issue-reference')
58870                         .data([0])
58871                         .enter()
58872                         .append('div')
58873                         .attr('class', 'issue-reference')
58874                         .text(_t('issues.tag_suggests_area.reference'));
58875                 }
58876             }
58877
58878             function vertexTaggedAsPointIssue(entity, graph) {
58879                 // we only care about nodes
58880                 if (entity.type !== 'node') return null;
58881
58882                 // ignore tagless points
58883                 if (Object.keys(entity.tags).length === 0) return null;
58884
58885                 // address lines are special so just ignore them
58886                 if (entity.isOnAddressLine(graph)) return null;
58887
58888                 var geometry = entity.geometry(graph);
58889                 var allowedGeometries = osmNodeGeometriesForTags(entity.tags);
58890
58891                 if (geometry === 'point' && !allowedGeometries.point && allowedGeometries.vertex) {
58892
58893                     return new validationIssue({
58894                         type: type,
58895                         subtype: 'vertex_as_point',
58896                         severity: 'warning',
58897                         message: function(context) {
58898                             var entity = context.hasEntity(this.entityIds[0]);
58899                             return entity ? _t('issues.vertex_as_point.message', {
58900                                 feature: utilDisplayLabel(entity, context.graph())
58901                             }) : '';
58902                         },
58903                         reference: function showReference(selection) {
58904                             selection.selectAll('.issue-reference')
58905                                 .data([0])
58906                                 .enter()
58907                                 .append('div')
58908                                 .attr('class', 'issue-reference')
58909                                 .text(_t('issues.vertex_as_point.reference'));
58910                         },
58911                         entityIds: [entity.id]
58912                     });
58913
58914                 } else if (geometry === 'vertex' && !allowedGeometries.vertex && allowedGeometries.point) {
58915
58916                     return new validationIssue({
58917                         type: type,
58918                         subtype: 'point_as_vertex',
58919                         severity: 'warning',
58920                         message: function(context) {
58921                             var entity = context.hasEntity(this.entityIds[0]);
58922                             return entity ? _t('issues.point_as_vertex.message', {
58923                                 feature: utilDisplayLabel(entity, context.graph())
58924                             }) : '';
58925                         },
58926                         reference: function showReference(selection) {
58927                             selection.selectAll('.issue-reference')
58928                                 .data([0])
58929                                 .enter()
58930                                 .append('div')
58931                                 .attr('class', 'issue-reference')
58932                                 .text(_t('issues.point_as_vertex.reference'));
58933                         },
58934                         entityIds: [entity.id],
58935                         dynamicFixes: function(context) {
58936
58937                             var entityId = this.entityIds[0];
58938
58939                             var extractOnClick = null;
58940                             if (!context.hasHiddenConnections(entityId)) {
58941
58942                                 extractOnClick = function(context) {
58943                                     var entityId = this.issue.entityIds[0];
58944                                     var action = actionExtract(entityId);
58945                                     context.perform(
58946                                         action,
58947                                         _t('operations.extract.annotation.single')
58948                                     );
58949                                     // re-enter mode to trigger updates
58950                                     context.enter(modeSelect(context, [action.getExtractedNodeID()]));
58951                                 };
58952                             }
58953
58954                             return [
58955                                 new validationIssueFix({
58956                                     icon: 'iD-operation-extract',
58957                                     title: _t('issues.fix.extract_point.title'),
58958                                     onClick: extractOnClick
58959                                 })
58960                             ];
58961                         }
58962                     });
58963                 }
58964
58965                 return null;
58966             }
58967
58968             function unclosedMultipolygonPartIssues(entity, graph) {
58969
58970                 if (entity.type !== 'relation' ||
58971                     !entity.isMultipolygon() ||
58972                     entity.isDegenerate() ||
58973                     // cannot determine issues for incompletely-downloaded relations
58974                     !entity.isComplete(graph)) return null;
58975
58976                 var sequences = osmJoinWays(entity.members, graph);
58977
58978                 var issues = [];
58979
58980                 for (var i in sequences) {
58981                     var sequence = sequences[i];
58982
58983                     if (!sequence.nodes) continue;
58984
58985                     var firstNode = sequence.nodes[0];
58986                     var lastNode = sequence.nodes[sequence.nodes.length - 1];
58987
58988                     // part is closed if the first and last nodes are the same
58989                     if (firstNode === lastNode) continue;
58990
58991                     var issue = new validationIssue({
58992                         type: type,
58993                         subtype: 'unclosed_multipolygon_part',
58994                         severity: 'warning',
58995                         message: function(context) {
58996                             var entity = context.hasEntity(this.entityIds[0]);
58997                             return entity ? _t('issues.unclosed_multipolygon_part.message', {
58998                                 feature: utilDisplayLabel(entity, context.graph())
58999                             }) : '';
59000                         },
59001                         reference: showReference,
59002                         loc: sequence.nodes[0].loc,
59003                         entityIds: [entity.id],
59004                         hash: sequence.map(function(way) {
59005                             return way.id;
59006                         }).join()
59007                     });
59008                     issues.push(issue);
59009                 }
59010
59011                 return issues;
59012
59013                 function showReference(selection) {
59014                     selection.selectAll('.issue-reference')
59015                         .data([0])
59016                         .enter()
59017                         .append('div')
59018                         .attr('class', 'issue-reference')
59019                         .text(_t('issues.unclosed_multipolygon_part.reference'));
59020                 }
59021             }
59022
59023             var validation = function checkMismatchedGeometry(entity, graph) {
59024                 var issues = [
59025                     vertexTaggedAsPointIssue(entity, graph),
59026                     lineTaggedAsAreaIssue(entity)
59027                 ];
59028                 issues = issues.concat(unclosedMultipolygonPartIssues(entity, graph));
59029                 return issues.filter(Boolean);
59030             };
59031
59032             validation.type = type;
59033
59034             return validation;
59035         }
59036
59037         function validationMissingRole() {
59038             var type = 'missing_role';
59039
59040             var validation = function checkMissingRole(entity, graph) {
59041                 var issues = [];
59042                 if (entity.type === 'way') {
59043                     graph.parentRelations(entity).forEach(function(relation) {
59044                         if (!relation.isMultipolygon()) return;
59045
59046                         var member = relation.memberById(entity.id);
59047                         if (member && isMissingRole(member)) {
59048                             issues.push(makeIssue(entity, relation, member));
59049                         }
59050                     });
59051                 } else if (entity.type === 'relation' && entity.isMultipolygon()) {
59052                     entity.indexedMembers().forEach(function(member) {
59053                         var way = graph.hasEntity(member.id);
59054                         if (way && isMissingRole(member)) {
59055                             issues.push(makeIssue(way, entity, member));
59056                         }
59057                     });
59058                 }
59059
59060                 return issues;
59061             };
59062
59063
59064             function isMissingRole(member) {
59065                 return !member.role || !member.role.trim().length;
59066             }
59067
59068
59069             function makeIssue(way, relation, member) {
59070                 return new validationIssue({
59071                     type: type,
59072                     severity: 'warning',
59073                     message: function(context) {
59074                         var member = context.hasEntity(this.entityIds[1]),
59075                             relation = context.hasEntity(this.entityIds[0]);
59076                         return (member && relation) ? _t('issues.missing_role.message', {
59077                             member: utilDisplayLabel(member, context.graph()),
59078                             relation: utilDisplayLabel(relation, context.graph())
59079                         }) : '';
59080                     },
59081                     reference: showReference,
59082                     entityIds: [relation.id, way.id],
59083                     data: {
59084                         member: member
59085                     },
59086                     hash: member.index.toString(),
59087                     dynamicFixes: function() {
59088                         return [
59089                             makeAddRoleFix('inner'),
59090                             makeAddRoleFix('outer'),
59091                             new validationIssueFix({
59092                                 icon: 'iD-operation-delete',
59093                                 title: _t('issues.fix.remove_from_relation.title'),
59094                                 onClick: function(context) {
59095                                     context.perform(
59096                                         actionDeleteMember(this.issue.entityIds[0], this.issue.data.member.index),
59097                                         _t('operations.delete_member.annotation')
59098                                     );
59099                                 }
59100                             })
59101                         ];
59102                     }
59103                 });
59104
59105
59106                 function showReference(selection) {
59107                     selection.selectAll('.issue-reference')
59108                         .data([0])
59109                         .enter()
59110                         .append('div')
59111                         .attr('class', 'issue-reference')
59112                         .text(_t('issues.missing_role.multipolygon.reference'));
59113                 }
59114             }
59115
59116
59117             function makeAddRoleFix(role) {
59118                 return new validationIssueFix({
59119                     title: _t('issues.fix.set_as_' + role + '.title'),
59120                     onClick: function(context) {
59121                         var oldMember = this.issue.data.member;
59122                         var member = { id: this.issue.entityIds[1], type: oldMember.type, role: role };
59123                         context.perform(
59124                             actionChangeMember(this.issue.entityIds[0], member, oldMember.index),
59125                             _t('operations.change_role.annotation')
59126                         );
59127                     }
59128                 });
59129             }
59130
59131             validation.type = type;
59132
59133             return validation;
59134         }
59135
59136         function validationMissingTag(context) {
59137             var type = 'missing_tag';
59138
59139             function hasDescriptiveTags(entity, graph) {
59140                 var keys = Object.keys(entity.tags)
59141                     .filter(function(k) {
59142                         if (k === 'area' || k === 'name') {
59143                             return false;
59144                         } else {
59145                             return osmIsInterestingTag(k);
59146                         }
59147                     });
59148
59149                 if (entity.type === 'relation' &&
59150                     keys.length === 1 &&
59151                     entity.tags.type === 'multipolygon') {
59152                     // this relation's only interesting tag just says its a multipolygon,
59153                     // which is not descriptive enough
59154
59155                     // It's okay for a simple multipolygon to have no descriptive tags
59156                     // if its outer way has them (old model, see `outdated_tags.js`)
59157                     return osmOldMultipolygonOuterMemberOfRelation(entity, graph);
59158                 }
59159
59160                 return keys.length > 0;
59161             }
59162
59163             function isUnknownRoad(entity) {
59164                 return entity.type === 'way' && entity.tags.highway === 'road';
59165             }
59166
59167             function isUntypedRelation(entity) {
59168                 return entity.type === 'relation' && !entity.tags.type;
59169             }
59170
59171             var validation = function checkMissingTag(entity, graph) {
59172
59173                 var subtype;
59174
59175                 var osm = context.connection();
59176                 var isUnloadedNode = entity.type === 'node' && osm && !osm.isDataLoaded(entity.loc);
59177
59178                 // we can't know if the node is a vertex if the tile is undownloaded
59179                 if (!isUnloadedNode &&
59180                     // allow untagged nodes that are part of ways
59181                     entity.geometry(graph) !== 'vertex' &&
59182                     // allow untagged entities that are part of relations
59183                     !entity.hasParentRelations(graph)) {
59184
59185                     if (Object.keys(entity.tags).length === 0) {
59186                         subtype = 'any';
59187                     } else if (!hasDescriptiveTags(entity, graph)) {
59188                         subtype = 'descriptive';
59189                     } else if (isUntypedRelation(entity)) {
59190                         subtype = 'relation_type';
59191                     }
59192                 }
59193
59194                 // flag an unknown road even if it's a member of a relation
59195                 if (!subtype && isUnknownRoad(entity)) {
59196                     subtype = 'highway_classification';
59197                 }
59198
59199                 if (!subtype) return [];
59200
59201                 var messageID = subtype === 'highway_classification' ? 'unknown_road' : 'missing_tag.' + subtype;
59202                 var referenceID = subtype === 'highway_classification' ? 'unknown_road' : 'missing_tag';
59203
59204                 // can always delete if the user created it in the first place..
59205                 var canDelete = (entity.version === undefined || entity.v !== undefined);
59206                 var severity = (canDelete && subtype !== 'highway_classification') ? 'error' : 'warning';
59207
59208                 return [new validationIssue({
59209                     type: type,
59210                     subtype: subtype,
59211                     severity: severity,
59212                     message: function(context) {
59213                         var entity = context.hasEntity(this.entityIds[0]);
59214                         return entity ? _t('issues.' + messageID + '.message', {
59215                             feature: utilDisplayLabel(entity, context.graph())
59216                         }) : '';
59217                     },
59218                     reference: showReference,
59219                     entityIds: [entity.id],
59220                     dynamicFixes: function(context) {
59221
59222                         var fixes = [];
59223
59224                         var selectFixType = subtype === 'highway_classification' ? 'select_road_type' : 'select_preset';
59225
59226                         fixes.push(new validationIssueFix({
59227                             icon: 'iD-icon-search',
59228                             title: _t('issues.fix.' + selectFixType + '.title'),
59229                             onClick: function(context) {
59230                                 context.ui().sidebar.showPresetList();
59231                             }
59232                         }));
59233
59234                         var deleteOnClick;
59235
59236                         var id = this.entityIds[0];
59237                         var operation = operationDelete(context, [id]);
59238                         var disabledReasonID = operation.disabled();
59239                         if (!disabledReasonID) {
59240                             deleteOnClick = function(context) {
59241                                 var id = this.issue.entityIds[0];
59242                                 var operation = operationDelete(context, [id]);
59243                                 if (!operation.disabled()) {
59244                                     operation();
59245                                 }
59246                             };
59247                         }
59248
59249                         fixes.push(
59250                             new validationIssueFix({
59251                                 icon: 'iD-operation-delete',
59252                                 title: _t('issues.fix.delete_feature.title'),
59253                                 disabledReason: disabledReasonID ? _t('operations.delete.' + disabledReasonID + '.single') : undefined,
59254                                 onClick: deleteOnClick
59255                             })
59256                         );
59257
59258                         return fixes;
59259                     }
59260                 })];
59261
59262                 function showReference(selection) {
59263                     selection.selectAll('.issue-reference')
59264                         .data([0])
59265                         .enter()
59266                         .append('div')
59267                         .attr('class', 'issue-reference')
59268                         .text(_t('issues.' + referenceID + '.reference'));
59269                 }
59270             };
59271
59272             validation.type = type;
59273
59274             return validation;
59275         }
59276
59277         // remove spaces, punctuation, diacritics
59278         var simplify = (str) => {
59279           return diacritics.remove(
59280             str
59281               .replace(/&/g, 'and')
59282               .replace(/[\s\-=_!"#%'*{},.\/:;?\(\)\[\]@\\$\^*+<>~`’\u00a1\u00a7\u00b6\u00b7\u00bf\u037e\u0387\u055a-\u055f\u0589\u05c0\u05c3\u05c6\u05f3\u05f4\u0609\u060a\u060c\u060d\u061b\u061e\u061f\u066a-\u066d\u06d4\u0700-\u070d\u07f7-\u07f9\u0830-\u083e\u085e\u0964\u0965\u0970\u0af0\u0df4\u0e4f\u0e5a\u0e5b\u0f04-\u0f12\u0f14\u0f85\u0fd0-\u0fd4\u0fd9\u0fda\u104a-\u104f\u10fb\u1360-\u1368\u166d\u166e\u16eb-\u16ed\u1735\u1736\u17d4-\u17d6\u17d8-\u17da\u1800-\u1805\u1807-\u180a\u1944\u1945\u1a1e\u1a1f\u1aa0-\u1aa6\u1aa8-\u1aad\u1b5a-\u1b60\u1bfc-\u1bff\u1c3b-\u1c3f\u1c7e\u1c7f\u1cc0-\u1cc7\u1cd3\u2016\u2017\u2020-\u2027\u2030-\u2038\u203b-\u203e\u2041-\u2043\u2047-\u2051\u2053\u2055-\u205e\u2cf9-\u2cfc\u2cfe\u2cff\u2d70\u2e00\u2e01\u2e06-\u2e08\u2e0b\u2e0e-\u2e16\u2e18\u2e19\u2e1b\u2e1e\u2e1f\u2e2a-\u2e2e\u2e30-\u2e39\u3001-\u3003\u303d\u30fb\ua4fe\ua4ff\ua60d-\ua60f\ua673\ua67e\ua6f2-\ua6f7\ua874-\ua877\ua8ce\ua8cf\ua8f8-\ua8fa\ua92e\ua92f\ua95f\ua9c1-\ua9cd\ua9de\ua9df\uaa5c-\uaa5f\uaade\uaadf\uaaf0\uaaf1\uabeb\ufe10-\ufe16\ufe19\ufe30\ufe45\ufe46\ufe49-\ufe4c\ufe50-\ufe52\ufe54-\ufe57\ufe5f-\ufe61\ufe68\ufe6a\ufe6b\uff01-\uff03\uff05-\uff07\uff0a\uff0c\uff0e\uff0f\uff1a\uff1b\uff1f\uff20\uff3c\uff61\uff64\uff65]+/g,'')
59283               .toLowerCase()
59284           );
59285         };
59286
59287         // toParts - split a name-suggestion-index key into parts
59288         // {
59289         //   kvnd:        "amenity/fast_food|Thaï Express~(North America)",
59290         //   kvn:         "amenity/fast_food|Thaï Express",
59291         //   kv:          "amenity/fast_food",
59292         //   k:           "amenity",
59293         //   v:           "fast_food",
59294         //   n:           "Thaï Express",
59295         //   d:           "(North America)",
59296         //   nsimple:     "thaiexpress",
59297         //   kvnnsimple:  "amenity/fast_food|thaiexpress"
59298         // }
59299         var to_parts = (kvnd) => {
59300           const parts = {};
59301           parts.kvnd = kvnd;
59302
59303           const kvndparts = kvnd.split('~', 2);
59304           if (kvndparts.length > 1) parts.d = kvndparts[1];
59305
59306           parts.kvn = kvndparts[0];
59307           const kvnparts = parts.kvn.split('|', 2);
59308           if (kvnparts.length > 1) parts.n = kvnparts[1];
59309
59310           parts.kv = kvnparts[0];
59311           const kvparts = parts.kv.split('/', 2);
59312           parts.k = kvparts[0];
59313           parts.v = kvparts[1];
59314
59315           parts.nsimple = simplify(parts.n);
59316           parts.kvnsimple = parts.kv + '|' + parts.nsimple;
59317           return parts;
59318         };
59319
59320         var matchGroups = {adult_gaming_centre:["amenity/casino","amenity/gambling","leisure/adult_gaming_centre"],beauty:["shop/beauty","shop/hairdresser_supply"],bed:["shop/bed","shop/furniture"],beverages:["shop/alcohol","shop/beverages"],camping:["leisure/park","tourism/camp_site","tourism/caravan_site"],car_parts:["shop/car_parts","shop/car_repair","shop/tires","shop/tyres"],confectionery:["shop/candy","shop/chocolate","shop/confectionery"],convenience:["shop/beauty","shop/chemist","shop/convenience","shop/cosmetics","shop/newsagent"],coworking:["amenity/coworking_space","office/coworking","office/coworking_space"],electronics:["office/telecommunication","shop/computer","shop/electronics","shop/hifi","shop/mobile","shop/mobile_phone","shop/telecommunication"],fashion:["shop/accessories","shop/bag","shop/botique","shop/clothes","shop/department_store","shop/fashion","shop/fashion_accessories","shop/sports","shop/shoes"],financial:["amenity/bank","office/accountant","office/financial","office/financial_advisor","office/tax_advisor","shop/tax"],fitness:["leisure/fitness_centre","leisure/fitness_center","leisure/sports_centre","leisure/sports_center"],food:["amenity/cafe","amenity/fast_food","amenity/ice_cream","amenity/restaurant","shop/bakery","shop/ice_cream","shop/pastry","shop/tea","shop/coffee"],fuel:["amenity/fuel","shop/gas","shop/convenience;gas","shop/gas;convenience"],gift:["shop/gift","shop/card","shop/cards","shop/stationery"],hardware:["shop/carpet","shop/diy","shop/doityourself","shop/doors","shop/electrical","shop/flooring","shop/hardware","shop/power_tools","shop/tool_hire","shop/tools","shop/trade"],health_food:["shop/health","shop/health_food","shop/herbalist","shop/nutrition_supplements"],houseware:["shop/houseware","shop/interior_decoration"],lodging:["tourism/hotel","tourism/motel"],money_transfer:["amenity/money_transfer","shop/money_transfer"],outdoor:["shop/outdoor","shop/sports"],rental:["amenity/bicycle_rental","amenity/boat_rental","amenity/car_rental","amenity/truck_rental","amenity/vehicle_rental","shop/rental"],school:["amenity/childcare","amenity/college","amenity/kindergarten","amenity/language_school","amenity/prep_school","amenity/school","amenity/university"],supermarket:["shop/food","shop/frozen_food","shop/greengrocer","shop/grocery","shop/supermarket","shop/wholesale"],variety_store:["shop/variety_store","shop/supermarket","shop/discount","shop/convenience"],vending:["amenity/vending_machine","shop/vending_machine"],wholesale:["shop/wholesale","shop/supermarket","shop/department_store"]};
59321         var match_groups = {
59322         matchGroups: matchGroups
59323         };
59324
59325         var match_groups$1 = /*#__PURE__*/Object.freeze({
59326                 __proto__: null,
59327                 matchGroups: matchGroups,
59328                 'default': match_groups
59329         });
59330
59331         var require$$0 = getCjsExportFromNamespace(match_groups$1);
59332
59333         const matchGroups$1 = require$$0.matchGroups;
59334
59335
59336         var matcher$1 = () => {
59337           let _warnings = [];  // array of match conflict pairs
59338           let _ambiguous = {};
59339           let _matchIndex = {};
59340           let matcher = {};
59341
59342
59343           // Create an index of all the keys/simplenames for fast matching
59344           matcher.buildMatchIndex = (brands) => {
59345             // two passes - once for primary names, once for secondary/alternate names
59346             Object.keys(brands).forEach(kvnd => insertNames(kvnd, 'primary'));
59347             Object.keys(brands).forEach(kvnd => insertNames(kvnd, 'secondary'));
59348
59349
59350             function insertNames(kvnd, which) {
59351               const obj = brands[kvnd];
59352               const parts = to_parts(kvnd);
59353
59354               // Exit early for ambiguous names in the second pass.
59355               // They were collected in the first pass and we don't gather alt names for them.
59356               if (which === 'secondary' && parts.d) return;
59357
59358
59359               if (obj.countryCodes) {
59360                 parts.countryCodes = obj.countryCodes.slice();  // copy
59361               }
59362
59363               const nomatches = (obj.nomatch || []);
59364               if (nomatches.some(s => s === kvnd)) {
59365                 console.log(`WARNING match/nomatch conflict for ${kvnd}`);
59366                 return;
59367               }
59368
59369               const match_kv = [parts.kv]
59370                 .concat(obj.matchTags || [])
59371                 .concat([`${parts.k}/yes`, `building/yes`])   // #3454 - match some generic tags
59372                 .map(s => s.toLowerCase());
59373
59374               let match_nsimple = [];
59375               if (which === 'primary') {
59376                 match_nsimple = [parts.n]
59377                   .concat(obj.matchNames || [])
59378                   .concat(obj.tags.official_name || [])   // #2732 - match alternate names
59379                   .map(simplify);
59380
59381               } else if (which === 'secondary') {
59382                 match_nsimple = []
59383                   .concat(obj.tags.alt_name || [])        // #2732 - match alternate names
59384                   .concat(obj.tags.short_name || [])      // #2732 - match alternate names
59385                   .map(simplify);
59386               }
59387
59388               if (!match_nsimple.length) return;  // nothing to do
59389
59390               match_kv.forEach(kv => {
59391                 match_nsimple.forEach(nsimple => {
59392                   if (parts.d) {
59393                     // Known ambiguous names with disambiguation string ~(USA) / ~(Canada)
59394                     // FIXME: Name collisions will overwrite the initial entry (ok for now)
59395                     if (!_ambiguous[kv]) _ambiguous[kv] = {};
59396                     _ambiguous[kv][nsimple] = parts;
59397
59398                   } else {
59399                     // Names we mostly expect to be unique..
59400                     if (!_matchIndex[kv]) _matchIndex[kv] = {};
59401
59402                     const m = _matchIndex[kv][nsimple];
59403                     if (m) {  // There already is a match for this name, skip it
59404                       // Warn if we detect collisions in a primary name.
59405                       // Skip warning if a secondary name or a generic `*=yes` tag - #2972 / #3454
59406                       if (which === 'primary' && !/\/yes$/.test(kv)) {
59407                         _warnings.push([m.kvnd, `${kvnd} (${kv}/${nsimple})`]);
59408                       }
59409                     } else {
59410                       _matchIndex[kv][nsimple] = parts;   // insert
59411                     }
59412                   }
59413                 });
59414               });
59415
59416             }
59417           };
59418
59419
59420           // pass a `key`, `value`, `name` and return the best match,
59421           // `countryCode` optional (if supplied, must match that too)
59422           matcher.matchKVN = (key, value, name, countryCode) => {
59423             return matcher.matchParts(to_parts(`${key}/${value}|${name}`), countryCode);
59424           };
59425
59426
59427           // pass a parts object and return the best match,
59428           // `countryCode` optional (if supplied, must match that too)
59429           matcher.matchParts = (parts, countryCode) => {
59430             let match = null;
59431             let inGroup = false;
59432
59433             // fixme: we currently return a single match for ambiguous
59434             match = _ambiguous[parts.kv] && _ambiguous[parts.kv][parts.nsimple];
59435             if (match && matchesCountryCode(match)) return match;
59436
59437             // try to return an exact match
59438             match = _matchIndex[parts.kv] && _matchIndex[parts.kv][parts.nsimple];
59439             if (match && matchesCountryCode(match)) return match;
59440
59441             // look in match groups
59442             for (let mg in matchGroups$1) {
59443               const matchGroup = matchGroups$1[mg];
59444               match = null;
59445               inGroup = false;
59446
59447               for (let i = 0; i < matchGroup.length; i++) {
59448                 const otherkv = matchGroup[i].toLowerCase();
59449                 if (!inGroup) {
59450                   inGroup = otherkv === parts.kv;
59451                 }
59452                 if (!match) {
59453                   // fixme: we currently return a single match for ambiguous
59454                   match = _ambiguous[otherkv] && _ambiguous[otherkv][parts.nsimple];
59455                 }
59456                 if (!match) {
59457                   match = _matchIndex[otherkv] && _matchIndex[otherkv][parts.nsimple];
59458                 }
59459
59460                 if (match && !matchesCountryCode(match)) {
59461                   match = null;
59462                 }
59463
59464                 if (inGroup && match) {
59465                   return match;
59466                 }
59467               }
59468             }
59469
59470             return null;
59471
59472             function matchesCountryCode(match) {
59473               if (!countryCode) return true;
59474               if (!match.countryCodes) return true;
59475               return match.countryCodes.indexOf(countryCode) !== -1;
59476             }
59477           };
59478
59479
59480           matcher.getWarnings = () => _warnings;
59481
59482           return matcher;
59483         };
59484
59485         var quickselect$1 = createCommonjsModule(function (module, exports) {
59486         (function (global, factory) {
59487                  module.exports = factory() ;
59488         }(commonjsGlobal, (function () {
59489         function quickselect(arr, k, left, right, compare) {
59490             quickselectStep(arr, k, left || 0, right || (arr.length - 1), compare || defaultCompare);
59491         }
59492
59493         function quickselectStep(arr, k, left, right, compare) {
59494
59495             while (right > left) {
59496                 if (right - left > 600) {
59497                     var n = right - left + 1;
59498                     var m = k - left + 1;
59499                     var z = Math.log(n);
59500                     var s = 0.5 * Math.exp(2 * z / 3);
59501                     var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
59502                     var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
59503                     var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
59504                     quickselectStep(arr, k, newLeft, newRight, compare);
59505                 }
59506
59507                 var t = arr[k];
59508                 var i = left;
59509                 var j = right;
59510
59511                 swap(arr, left, k);
59512                 if (compare(arr[right], t) > 0) swap(arr, left, right);
59513
59514                 while (i < j) {
59515                     swap(arr, i, j);
59516                     i++;
59517                     j--;
59518                     while (compare(arr[i], t) < 0) i++;
59519                     while (compare(arr[j], t) > 0) j--;
59520                 }
59521
59522                 if (compare(arr[left], t) === 0) swap(arr, left, j);
59523                 else {
59524                     j++;
59525                     swap(arr, j, right);
59526                 }
59527
59528                 if (j <= k) left = j + 1;
59529                 if (k <= j) right = j - 1;
59530             }
59531         }
59532
59533         function swap(arr, i, j) {
59534             var tmp = arr[i];
59535             arr[i] = arr[j];
59536             arr[j] = tmp;
59537         }
59538
59539         function defaultCompare(a, b) {
59540             return a < b ? -1 : a > b ? 1 : 0;
59541         }
59542
59543         return quickselect;
59544
59545         })));
59546         });
59547
59548         var rbush_1 = rbush;
59549         var _default$2 = rbush;
59550
59551
59552
59553         function rbush(maxEntries, format) {
59554             if (!(this instanceof rbush)) return new rbush(maxEntries, format);
59555
59556             // max entries in a node is 9 by default; min node fill is 40% for best performance
59557             this._maxEntries = Math.max(4, maxEntries || 9);
59558             this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
59559
59560             if (format) {
59561                 this._initFormat(format);
59562             }
59563
59564             this.clear();
59565         }
59566
59567         rbush.prototype = {
59568
59569             all: function () {
59570                 return this._all(this.data, []);
59571             },
59572
59573             search: function (bbox) {
59574
59575                 var node = this.data,
59576                     result = [],
59577                     toBBox = this.toBBox;
59578
59579                 if (!intersects$1(bbox, node)) return result;
59580
59581                 var nodesToSearch = [],
59582                     i, len, child, childBBox;
59583
59584                 while (node) {
59585                     for (i = 0, len = node.children.length; i < len; i++) {
59586
59587                         child = node.children[i];
59588                         childBBox = node.leaf ? toBBox(child) : child;
59589
59590                         if (intersects$1(bbox, childBBox)) {
59591                             if (node.leaf) result.push(child);
59592                             else if (contains$2(bbox, childBBox)) this._all(child, result);
59593                             else nodesToSearch.push(child);
59594                         }
59595                     }
59596                     node = nodesToSearch.pop();
59597                 }
59598
59599                 return result;
59600             },
59601
59602             collides: function (bbox) {
59603
59604                 var node = this.data,
59605                     toBBox = this.toBBox;
59606
59607                 if (!intersects$1(bbox, node)) return false;
59608
59609                 var nodesToSearch = [],
59610                     i, len, child, childBBox;
59611
59612                 while (node) {
59613                     for (i = 0, len = node.children.length; i < len; i++) {
59614
59615                         child = node.children[i];
59616                         childBBox = node.leaf ? toBBox(child) : child;
59617
59618                         if (intersects$1(bbox, childBBox)) {
59619                             if (node.leaf || contains$2(bbox, childBBox)) return true;
59620                             nodesToSearch.push(child);
59621                         }
59622                     }
59623                     node = nodesToSearch.pop();
59624                 }
59625
59626                 return false;
59627             },
59628
59629             load: function (data) {
59630                 if (!(data && data.length)) return this;
59631
59632                 if (data.length < this._minEntries) {
59633                     for (var i = 0, len = data.length; i < len; i++) {
59634                         this.insert(data[i]);
59635                     }
59636                     return this;
59637                 }
59638
59639                 // recursively build the tree with the given data from scratch using OMT algorithm
59640                 var node = this._build(data.slice(), 0, data.length - 1, 0);
59641
59642                 if (!this.data.children.length) {
59643                     // save as is if tree is empty
59644                     this.data = node;
59645
59646                 } else if (this.data.height === node.height) {
59647                     // split root if trees have the same height
59648                     this._splitRoot(this.data, node);
59649
59650                 } else {
59651                     if (this.data.height < node.height) {
59652                         // swap trees if inserted one is bigger
59653                         var tmpNode = this.data;
59654                         this.data = node;
59655                         node = tmpNode;
59656                     }
59657
59658                     // insert the small tree into the large tree at appropriate level
59659                     this._insert(node, this.data.height - node.height - 1, true);
59660                 }
59661
59662                 return this;
59663             },
59664
59665             insert: function (item) {
59666                 if (item) this._insert(item, this.data.height - 1);
59667                 return this;
59668             },
59669
59670             clear: function () {
59671                 this.data = createNode$1([]);
59672                 return this;
59673             },
59674
59675             remove: function (item, equalsFn) {
59676                 if (!item) return this;
59677
59678                 var node = this.data,
59679                     bbox = this.toBBox(item),
59680                     path = [],
59681                     indexes = [],
59682                     i, parent, index, goingUp;
59683
59684                 // depth-first iterative tree traversal
59685                 while (node || path.length) {
59686
59687                     if (!node) { // go up
59688                         node = path.pop();
59689                         parent = path[path.length - 1];
59690                         i = indexes.pop();
59691                         goingUp = true;
59692                     }
59693
59694                     if (node.leaf) { // check current node
59695                         index = findItem$1(item, node.children, equalsFn);
59696
59697                         if (index !== -1) {
59698                             // item found, remove the item and condense tree upwards
59699                             node.children.splice(index, 1);
59700                             path.push(node);
59701                             this._condense(path);
59702                             return this;
59703                         }
59704                     }
59705
59706                     if (!goingUp && !node.leaf && contains$2(node, bbox)) { // go down
59707                         path.push(node);
59708                         indexes.push(i);
59709                         i = 0;
59710                         parent = node;
59711                         node = node.children[0];
59712
59713                     } else if (parent) { // go right
59714                         i++;
59715                         node = parent.children[i];
59716                         goingUp = false;
59717
59718                     } else node = null; // nothing found
59719                 }
59720
59721                 return this;
59722             },
59723
59724             toBBox: function (item) { return item; },
59725
59726             compareMinX: compareNodeMinX$1,
59727             compareMinY: compareNodeMinY$1,
59728
59729             toJSON: function () { return this.data; },
59730
59731             fromJSON: function (data) {
59732                 this.data = data;
59733                 return this;
59734             },
59735
59736             _all: function (node, result) {
59737                 var nodesToSearch = [];
59738                 while (node) {
59739                     if (node.leaf) result.push.apply(result, node.children);
59740                     else nodesToSearch.push.apply(nodesToSearch, node.children);
59741
59742                     node = nodesToSearch.pop();
59743                 }
59744                 return result;
59745             },
59746
59747             _build: function (items, left, right, height) {
59748
59749                 var N = right - left + 1,
59750                     M = this._maxEntries,
59751                     node;
59752
59753                 if (N <= M) {
59754                     // reached leaf level; return leaf
59755                     node = createNode$1(items.slice(left, right + 1));
59756                     calcBBox$1(node, this.toBBox);
59757                     return node;
59758                 }
59759
59760                 if (!height) {
59761                     // target height of the bulk-loaded tree
59762                     height = Math.ceil(Math.log(N) / Math.log(M));
59763
59764                     // target number of root entries to maximize storage utilization
59765                     M = Math.ceil(N / Math.pow(M, height - 1));
59766                 }
59767
59768                 node = createNode$1([]);
59769                 node.leaf = false;
59770                 node.height = height;
59771
59772                 // split the items into M mostly square tiles
59773
59774                 var N2 = Math.ceil(N / M),
59775                     N1 = N2 * Math.ceil(Math.sqrt(M)),
59776                     i, j, right2, right3;
59777
59778                 multiSelect$1(items, left, right, N1, this.compareMinX);
59779
59780                 for (i = left; i <= right; i += N1) {
59781
59782                     right2 = Math.min(i + N1 - 1, right);
59783
59784                     multiSelect$1(items, i, right2, N2, this.compareMinY);
59785
59786                     for (j = i; j <= right2; j += N2) {
59787
59788                         right3 = Math.min(j + N2 - 1, right2);
59789
59790                         // pack each entry recursively
59791                         node.children.push(this._build(items, j, right3, height - 1));
59792                     }
59793                 }
59794
59795                 calcBBox$1(node, this.toBBox);
59796
59797                 return node;
59798             },
59799
59800             _chooseSubtree: function (bbox, node, level, path) {
59801
59802                 var i, len, child, targetNode, area, enlargement, minArea, minEnlargement;
59803
59804                 while (true) {
59805                     path.push(node);
59806
59807                     if (node.leaf || path.length - 1 === level) break;
59808
59809                     minArea = minEnlargement = Infinity;
59810
59811                     for (i = 0, len = node.children.length; i < len; i++) {
59812                         child = node.children[i];
59813                         area = bboxArea$1(child);
59814                         enlargement = enlargedArea$1(bbox, child) - area;
59815
59816                         // choose entry with the least area enlargement
59817                         if (enlargement < minEnlargement) {
59818                             minEnlargement = enlargement;
59819                             minArea = area < minArea ? area : minArea;
59820                             targetNode = child;
59821
59822                         } else if (enlargement === minEnlargement) {
59823                             // otherwise choose one with the smallest area
59824                             if (area < minArea) {
59825                                 minArea = area;
59826                                 targetNode = child;
59827                             }
59828                         }
59829                     }
59830
59831                     node = targetNode || node.children[0];
59832                 }
59833
59834                 return node;
59835             },
59836
59837             _insert: function (item, level, isNode) {
59838
59839                 var toBBox = this.toBBox,
59840                     bbox = isNode ? item : toBBox(item),
59841                     insertPath = [];
59842
59843                 // find the best node for accommodating the item, saving all nodes along the path too
59844                 var node = this._chooseSubtree(bbox, this.data, level, insertPath);
59845
59846                 // put the item into the node
59847                 node.children.push(item);
59848                 extend$3(node, bbox);
59849
59850                 // split on node overflow; propagate upwards if necessary
59851                 while (level >= 0) {
59852                     if (insertPath[level].children.length > this._maxEntries) {
59853                         this._split(insertPath, level);
59854                         level--;
59855                     } else break;
59856                 }
59857
59858                 // adjust bboxes along the insertion path
59859                 this._adjustParentBBoxes(bbox, insertPath, level);
59860             },
59861
59862             // split overflowed node into two
59863             _split: function (insertPath, level) {
59864
59865                 var node = insertPath[level],
59866                     M = node.children.length,
59867                     m = this._minEntries;
59868
59869                 this._chooseSplitAxis(node, m, M);
59870
59871                 var splitIndex = this._chooseSplitIndex(node, m, M);
59872
59873                 var newNode = createNode$1(node.children.splice(splitIndex, node.children.length - splitIndex));
59874                 newNode.height = node.height;
59875                 newNode.leaf = node.leaf;
59876
59877                 calcBBox$1(node, this.toBBox);
59878                 calcBBox$1(newNode, this.toBBox);
59879
59880                 if (level) insertPath[level - 1].children.push(newNode);
59881                 else this._splitRoot(node, newNode);
59882             },
59883
59884             _splitRoot: function (node, newNode) {
59885                 // split root node
59886                 this.data = createNode$1([node, newNode]);
59887                 this.data.height = node.height + 1;
59888                 this.data.leaf = false;
59889                 calcBBox$1(this.data, this.toBBox);
59890             },
59891
59892             _chooseSplitIndex: function (node, m, M) {
59893
59894                 var i, bbox1, bbox2, overlap, area, minOverlap, minArea, index;
59895
59896                 minOverlap = minArea = Infinity;
59897
59898                 for (i = m; i <= M - m; i++) {
59899                     bbox1 = distBBox$1(node, 0, i, this.toBBox);
59900                     bbox2 = distBBox$1(node, i, M, this.toBBox);
59901
59902                     overlap = intersectionArea$1(bbox1, bbox2);
59903                     area = bboxArea$1(bbox1) + bboxArea$1(bbox2);
59904
59905                     // choose distribution with minimum overlap
59906                     if (overlap < minOverlap) {
59907                         minOverlap = overlap;
59908                         index = i;
59909
59910                         minArea = area < minArea ? area : minArea;
59911
59912                     } else if (overlap === minOverlap) {
59913                         // otherwise choose distribution with minimum area
59914                         if (area < minArea) {
59915                             minArea = area;
59916                             index = i;
59917                         }
59918                     }
59919                 }
59920
59921                 return index;
59922             },
59923
59924             // sorts node children by the best axis for split
59925             _chooseSplitAxis: function (node, m, M) {
59926
59927                 var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX$1,
59928                     compareMinY = node.leaf ? this.compareMinY : compareNodeMinY$1,
59929                     xMargin = this._allDistMargin(node, m, M, compareMinX),
59930                     yMargin = this._allDistMargin(node, m, M, compareMinY);
59931
59932                 // if total distributions margin value is minimal for x, sort by minX,
59933                 // otherwise it's already sorted by minY
59934                 if (xMargin < yMargin) node.children.sort(compareMinX);
59935             },
59936
59937             // total margin of all possible split distributions where each node is at least m full
59938             _allDistMargin: function (node, m, M, compare) {
59939
59940                 node.children.sort(compare);
59941
59942                 var toBBox = this.toBBox,
59943                     leftBBox = distBBox$1(node, 0, m, toBBox),
59944                     rightBBox = distBBox$1(node, M - m, M, toBBox),
59945                     margin = bboxMargin$1(leftBBox) + bboxMargin$1(rightBBox),
59946                     i, child;
59947
59948                 for (i = m; i < M - m; i++) {
59949                     child = node.children[i];
59950                     extend$3(leftBBox, node.leaf ? toBBox(child) : child);
59951                     margin += bboxMargin$1(leftBBox);
59952                 }
59953
59954                 for (i = M - m - 1; i >= m; i--) {
59955                     child = node.children[i];
59956                     extend$3(rightBBox, node.leaf ? toBBox(child) : child);
59957                     margin += bboxMargin$1(rightBBox);
59958                 }
59959
59960                 return margin;
59961             },
59962
59963             _adjustParentBBoxes: function (bbox, path, level) {
59964                 // adjust bboxes along the given tree path
59965                 for (var i = level; i >= 0; i--) {
59966                     extend$3(path[i], bbox);
59967                 }
59968             },
59969
59970             _condense: function (path) {
59971                 // go through the path, removing empty nodes and updating bboxes
59972                 for (var i = path.length - 1, siblings; i >= 0; i--) {
59973                     if (path[i].children.length === 0) {
59974                         if (i > 0) {
59975                             siblings = path[i - 1].children;
59976                             siblings.splice(siblings.indexOf(path[i]), 1);
59977
59978                         } else this.clear();
59979
59980                     } else calcBBox$1(path[i], this.toBBox);
59981                 }
59982             },
59983
59984             _initFormat: function (format) {
59985                 // data format (minX, minY, maxX, maxY accessors)
59986
59987                 // uses eval-type function compilation instead of just accepting a toBBox function
59988                 // because the algorithms are very sensitive to sorting functions performance,
59989                 // so they should be dead simple and without inner calls
59990
59991                 var compareArr = ['return a', ' - b', ';'];
59992
59993                 this.compareMinX = new Function('a', 'b', compareArr.join(format[0]));
59994                 this.compareMinY = new Function('a', 'b', compareArr.join(format[1]));
59995
59996                 this.toBBox = new Function('a',
59997                     'return {minX: a' + format[0] +
59998                     ', minY: a' + format[1] +
59999                     ', maxX: a' + format[2] +
60000                     ', maxY: a' + format[3] + '};');
60001             }
60002         };
60003
60004         function findItem$1(item, items, equalsFn) {
60005             if (!equalsFn) return items.indexOf(item);
60006
60007             for (var i = 0; i < items.length; i++) {
60008                 if (equalsFn(item, items[i])) return i;
60009             }
60010             return -1;
60011         }
60012
60013         // calculate node's bbox from bboxes of its children
60014         function calcBBox$1(node, toBBox) {
60015             distBBox$1(node, 0, node.children.length, toBBox, node);
60016         }
60017
60018         // min bounding rectangle of node children from k to p-1
60019         function distBBox$1(node, k, p, toBBox, destNode) {
60020             if (!destNode) destNode = createNode$1(null);
60021             destNode.minX = Infinity;
60022             destNode.minY = Infinity;
60023             destNode.maxX = -Infinity;
60024             destNode.maxY = -Infinity;
60025
60026             for (var i = k, child; i < p; i++) {
60027                 child = node.children[i];
60028                 extend$3(destNode, node.leaf ? toBBox(child) : child);
60029             }
60030
60031             return destNode;
60032         }
60033
60034         function extend$3(a, b) {
60035             a.minX = Math.min(a.minX, b.minX);
60036             a.minY = Math.min(a.minY, b.minY);
60037             a.maxX = Math.max(a.maxX, b.maxX);
60038             a.maxY = Math.max(a.maxY, b.maxY);
60039             return a;
60040         }
60041
60042         function compareNodeMinX$1(a, b) { return a.minX - b.minX; }
60043         function compareNodeMinY$1(a, b) { return a.minY - b.minY; }
60044
60045         function bboxArea$1(a)   { return (a.maxX - a.minX) * (a.maxY - a.minY); }
60046         function bboxMargin$1(a) { return (a.maxX - a.minX) + (a.maxY - a.minY); }
60047
60048         function enlargedArea$1(a, b) {
60049             return (Math.max(b.maxX, a.maxX) - Math.min(b.minX, a.minX)) *
60050                    (Math.max(b.maxY, a.maxY) - Math.min(b.minY, a.minY));
60051         }
60052
60053         function intersectionArea$1(a, b) {
60054             var minX = Math.max(a.minX, b.minX),
60055                 minY = Math.max(a.minY, b.minY),
60056                 maxX = Math.min(a.maxX, b.maxX),
60057                 maxY = Math.min(a.maxY, b.maxY);
60058
60059             return Math.max(0, maxX - minX) *
60060                    Math.max(0, maxY - minY);
60061         }
60062
60063         function contains$2(a, b) {
60064             return a.minX <= b.minX &&
60065                    a.minY <= b.minY &&
60066                    b.maxX <= a.maxX &&
60067                    b.maxY <= a.maxY;
60068         }
60069
60070         function intersects$1(a, b) {
60071             return b.minX <= a.maxX &&
60072                    b.minY <= a.maxY &&
60073                    b.maxX >= a.minX &&
60074                    b.maxY >= a.minY;
60075         }
60076
60077         function createNode$1(children) {
60078             return {
60079                 children: children,
60080                 height: 1,
60081                 leaf: true,
60082                 minX: Infinity,
60083                 minY: Infinity,
60084                 maxX: -Infinity,
60085                 maxY: -Infinity
60086             };
60087         }
60088
60089         // sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
60090         // combines selection algorithm with binary divide & conquer approach
60091
60092         function multiSelect$1(arr, left, right, n, compare) {
60093             var stack = [left, right],
60094                 mid;
60095
60096             while (stack.length) {
60097                 right = stack.pop();
60098                 left = stack.pop();
60099
60100                 if (right - left <= n) continue;
60101
60102                 mid = left + Math.ceil((right - left) / n / 2) * n;
60103                 quickselect$1(arr, mid, left, right, compare);
60104
60105                 stack.push(left, mid, mid, right);
60106             }
60107         }
60108         rbush_1.default = _default$2;
60109
60110         var lineclip_1$1 = lineclip$1;
60111
60112         lineclip$1.polyline = lineclip$1;
60113         lineclip$1.polygon = polygonclip$1;
60114
60115
60116         // Cohen-Sutherland line clippign algorithm, adapted to efficiently
60117         // handle polylines rather than just segments
60118
60119         function lineclip$1(points, bbox, result) {
60120
60121             var len = points.length,
60122                 codeA = bitCode$1(points[0], bbox),
60123                 part = [],
60124                 i, a, b, codeB, lastCode;
60125
60126             if (!result) result = [];
60127
60128             for (i = 1; i < len; i++) {
60129                 a = points[i - 1];
60130                 b = points[i];
60131                 codeB = lastCode = bitCode$1(b, bbox);
60132
60133                 while (true) {
60134
60135                     if (!(codeA | codeB)) { // accept
60136                         part.push(a);
60137
60138                         if (codeB !== lastCode) { // segment went outside
60139                             part.push(b);
60140
60141                             if (i < len - 1) { // start a new line
60142                                 result.push(part);
60143                                 part = [];
60144                             }
60145                         } else if (i === len - 1) {
60146                             part.push(b);
60147                         }
60148                         break;
60149
60150                     } else if (codeA & codeB) { // trivial reject
60151                         break;
60152
60153                     } else if (codeA) { // a outside, intersect with clip edge
60154                         a = intersect$1(a, b, codeA, bbox);
60155                         codeA = bitCode$1(a, bbox);
60156
60157                     } else { // b outside
60158                         b = intersect$1(a, b, codeB, bbox);
60159                         codeB = bitCode$1(b, bbox);
60160                     }
60161                 }
60162
60163                 codeA = lastCode;
60164             }
60165
60166             if (part.length) result.push(part);
60167
60168             return result;
60169         }
60170
60171         // Sutherland-Hodgeman polygon clipping algorithm
60172
60173         function polygonclip$1(points, bbox) {
60174
60175             var result, edge, prev, prevInside, i, p, inside;
60176
60177             // clip against each side of the clip rectangle
60178             for (edge = 1; edge <= 8; edge *= 2) {
60179                 result = [];
60180                 prev = points[points.length - 1];
60181                 prevInside = !(bitCode$1(prev, bbox) & edge);
60182
60183                 for (i = 0; i < points.length; i++) {
60184                     p = points[i];
60185                     inside = !(bitCode$1(p, bbox) & edge);
60186
60187                     // if segment goes through the clip window, add an intersection
60188                     if (inside !== prevInside) result.push(intersect$1(prev, p, edge, bbox));
60189
60190                     if (inside) result.push(p); // add a point if it's inside
60191
60192                     prev = p;
60193                     prevInside = inside;
60194                 }
60195
60196                 points = result;
60197
60198                 if (!points.length) break;
60199             }
60200
60201             return result;
60202         }
60203
60204         // intersect a segment against one of the 4 lines that make up the bbox
60205
60206         function intersect$1(a, b, edge, bbox) {
60207             return edge & 8 ? [a[0] + (b[0] - a[0]) * (bbox[3] - a[1]) / (b[1] - a[1]), bbox[3]] : // top
60208                    edge & 4 ? [a[0] + (b[0] - a[0]) * (bbox[1] - a[1]) / (b[1] - a[1]), bbox[1]] : // bottom
60209                    edge & 2 ? [bbox[2], a[1] + (b[1] - a[1]) * (bbox[2] - a[0]) / (b[0] - a[0])] : // right
60210                    edge & 1 ? [bbox[0], a[1] + (b[1] - a[1]) * (bbox[0] - a[0]) / (b[0] - a[0])] : // left
60211                    null;
60212         }
60213
60214         // bit code reflects the point position relative to the bbox:
60215
60216         //         left  mid  right
60217         //    top  1001  1000  1010
60218         //    mid  0001  0000  0010
60219         // bottom  0101  0100  0110
60220
60221         function bitCode$1(p, bbox) {
60222             var code = 0;
60223
60224             if (p[0] < bbox[0]) code |= 1; // left
60225             else if (p[0] > bbox[2]) code |= 2; // right
60226
60227             if (p[1] < bbox[1]) code |= 4; // bottom
60228             else if (p[1] > bbox[3]) code |= 8; // top
60229
60230             return code;
60231         }
60232
60233         var whichPolygon_1 = whichPolygon;
60234
60235         function whichPolygon(data) {
60236             var bboxes = [];
60237             for (var i = 0; i < data.features.length; i++) {
60238                 var feature = data.features[i];
60239                 var coords = feature.geometry.coordinates;
60240
60241                 if (feature.geometry.type === 'Polygon') {
60242                     bboxes.push(treeItem(coords, feature.properties));
60243
60244                 } else if (feature.geometry.type === 'MultiPolygon') {
60245                     for (var j = 0; j < coords.length; j++) {
60246                         bboxes.push(treeItem(coords[j], feature.properties));
60247                     }
60248                 }
60249             }
60250
60251             var tree = rbush_1().load(bboxes);
60252
60253             function query(p, multi) {
60254                 var output = [],
60255                     result = tree.search({
60256                         minX: p[0],
60257                         minY: p[1],
60258                         maxX: p[0],
60259                         maxY: p[1]
60260                     });
60261                 for (var i = 0; i < result.length; i++) {
60262                     if (insidePolygon(result[i].coords, p)) {
60263                         if (multi)
60264                             output.push(result[i].props);
60265                         else
60266                             return result[i].props;
60267                     }
60268                 }
60269                 return multi && output.length ? output : null;
60270             }
60271
60272             query.tree = tree;
60273             query.bbox = function queryBBox(bbox) {
60274                 var output = [];
60275                 var result = tree.search({
60276                     minX: bbox[0],
60277                     minY: bbox[1],
60278                     maxX: bbox[2],
60279                     maxY: bbox[3]
60280                 });
60281                 for (var i = 0; i < result.length; i++) {
60282                     if (polygonIntersectsBBox(result[i].coords, bbox)) {
60283                         output.push(result[i].props);
60284                     }
60285                 }
60286                 return output;
60287             };
60288
60289             return query;
60290         }
60291
60292         function polygonIntersectsBBox(polygon, bbox) {
60293             var bboxCenter = [
60294                 (bbox[0] + bbox[2]) / 2,
60295                 (bbox[1] + bbox[3]) / 2
60296             ];
60297             if (insidePolygon(polygon, bboxCenter)) return true;
60298             for (var i = 0; i < polygon.length; i++) {
60299                 if (lineclip_1$1(polygon[i], bbox).length > 0) return true;
60300             }
60301             return false;
60302         }
60303
60304         // ray casting algorithm for detecting if point is in polygon
60305         function insidePolygon(rings, p) {
60306             var inside = false;
60307             for (var i = 0, len = rings.length; i < len; i++) {
60308                 var ring = rings[i];
60309                 for (var j = 0, len2 = ring.length, k = len2 - 1; j < len2; k = j++) {
60310                     if (rayIntersect(p, ring[j], ring[k])) inside = !inside;
60311                 }
60312             }
60313             return inside;
60314         }
60315
60316         function rayIntersect(p, p1, p2) {
60317             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]);
60318         }
60319
60320         function treeItem(coords, props) {
60321             var item = {
60322                 minX: Infinity,
60323                 minY: Infinity,
60324                 maxX: -Infinity,
60325                 maxY: -Infinity,
60326                 coords: coords,
60327                 props: props
60328             };
60329
60330             for (var i = 0; i < coords[0].length; i++) {
60331                 var p = coords[0][i];
60332                 item.minX = Math.min(item.minX, p[0]);
60333                 item.minY = Math.min(item.minY, p[1]);
60334                 item.maxX = Math.max(item.maxX, p[0]);
60335                 item.maxY = Math.max(item.maxY, p[1]);
60336             }
60337             return item;
60338         }
60339
60340         var type = "FeatureCollection";
60341         var features = [{type:"Feature",properties:{m49:"680",wikidata:"Q3405693",nameEn:"Sark",country:"GB",groups:["GG","830","154","150"],level:"subterritory",driveSide:"left",roadSpeedUnit:"mph",callingCodes:["44 01481"]},geometry:{type:"MultiPolygon",coordinates:[[[[-2.36485,49.48223],[-2.65349,49.15373],[-2.09454,49.46288],[-2.36485,49.48223]]]]}},{type:"Feature",properties:{m49:"001",wikidata:"Q2",nameEn:"World",aliases:["Earth","Planet"],level:"world"},geometry:null},{type:"Feature",properties:{m49:"142",wikidata:"Q48",nameEn:"Asia",level:"region"},geometry:null},{type:"Feature",properties:{m49:"143",wikidata:"Q27275",nameEn:"Central Asia",groups:["142"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"145",wikidata:"Q27293",nameEn:"Western Asia",groups:["142"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"150",wikidata:"Q46",nameEn:"Europe",level:"region"},geometry:null},{type:"Feature",properties:{m49:"151",wikidata:"Q27468",nameEn:"Eastern Europe",groups:["150"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"154",wikidata:"Q27479",nameEn:"Northern Europe",groups:["150"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"155",wikidata:"Q27496",nameEn:"Western Europe",groups:["150"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"202",wikidata:"Q132959",nameEn:"Sub-Saharan Africa",groups:["002"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"419",wikidata:"Q72829598",nameEn:"Latin America and the Caribbean",groups:["019"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"830",wikidata:"Q42314",nameEn:"Channel Islands",groups:["150","154"],level:"intermediateRegion"},geometry:null},{type:"Feature",properties:{m49:"019",wikidata:"Q828",nameEn:"Americas",level:"region"},geometry:null},{type:"Feature",properties:{m49:"029",wikidata:"Q664609",nameEn:"Caribbean",groups:["419","019","003"],level:"intermediateRegion"},geometry:null},{type:"Feature",properties:{m49:"034",wikidata:"Q771405",nameEn:"Southern Asia",groups:["142"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"002",wikidata:"Q15",nameEn:"Africa",level:"region"},geometry:null},{type:"Feature",properties:{m49:"003",wikidata:"Q49",nameEn:"North America",groups:["019"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"017",wikidata:"Q27433",nameEn:"Middle Africa",groups:["202","002"],level:"intermediateRegion"},geometry:null},{type:"Feature",properties:{m49:"039",wikidata:"Q27449",nameEn:"Southern Europe",groups:["150"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"005",wikidata:"Q18",nameEn:"South America",groups:["419","019"],level:"intermediateRegion"},geometry:null},{type:"Feature",properties:{m49:"009",wikidata:"Q538",nameEn:"Oceania",level:"region"},geometry:null},{type:"Feature",properties:{m49:"061",wikidata:"Q35942",nameEn:"Polynesia",groups:["009"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"014",wikidata:"Q27407",nameEn:"Eastern Africa",groups:["202","002"],level:"intermediateRegion"},geometry:null},{type:"Feature",properties:{m49:"053",wikidata:"Q45256",nameEn:"Australia and New Zealand",aliases:["Australasia"],groups:["009"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"011",wikidata:"Q4412",nameEn:"Western Africa",groups:["202","002"],level:"intermediateRegion"},geometry:null},{type:"Feature",properties:{m49:"013",wikidata:"Q27611",nameEn:"Central America",groups:["419","019","003"],level:"intermediateRegion"},geometry:null},{type:"Feature",properties:{m49:"021",wikidata:"Q2017699",nameEn:"Northern America",groups:["019","003"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"035",wikidata:"Q11708",nameEn:"South-eastern Asia",groups:["142"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"018",wikidata:"Q27394",nameEn:"Southern Africa",groups:["202","002"],level:"intermediateRegion"},geometry:null},{type:"Feature",properties:{m49:"030",wikidata:"Q27231",nameEn:"Eastern Asia",groups:["142"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"015",wikidata:"Q27381",nameEn:"Northern Africa",groups:["002"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"054",wikidata:"Q37394",nameEn:"Melanesia",groups:["009"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"057",wikidata:"Q3359409",nameEn:"Micronesia",groups:["009"],level:"subregion"},geometry:null},{type:"Feature",properties:{iso1A2:"AC",iso1A3:"ASC",wikidata:"Q46197",nameEn:"Ascension Island",country:"GB",groups:["SH","011","202","002"],isoStatus:"excRes",driveSide:"left",roadSpeedUnit:"mph",callingCodes:["247"]},geometry:{type:"MultiPolygon",coordinates:[[[[-14.82771,-8.70814],[-13.33271,-8.07391],[-14.91926,-6.63386],[-14.82771,-8.70814]]]]}},{type:"Feature",properties:{iso1A2:"AD",iso1A3:"AND",iso1N3:"020",wikidata:"Q228",nameEn:"Andorra",groups:["039","150"],callingCodes:["376"]},geometry:{type:"MultiPolygon",coordinates:[[[[1.72515,42.50338],[1.73683,42.55492],[1.7858,42.57698],[1.72588,42.59098],[1.73452,42.61515],[1.68267,42.62533],[1.6625,42.61982],[1.63485,42.62957],[1.60085,42.62703],[1.55418,42.65669],[1.50867,42.64483],[1.48043,42.65203],[1.46718,42.63296],[1.47986,42.61346],[1.44197,42.60217],[1.42512,42.58292],[1.44529,42.56722],[1.4234,42.55959],[1.41245,42.53539],[1.44759,42.54431],[1.46661,42.50949],[1.41648,42.48315],[1.43838,42.47848],[1.44529,42.43724],[1.5127,42.42959],[1.55073,42.43299],[1.55937,42.45808],[1.57953,42.44957],[1.58933,42.46275],[1.65674,42.47125],[1.66826,42.50779],[1.70571,42.48867],[1.72515,42.50338]]]]}},{type:"Feature",properties:{iso1A2:"AE",iso1A3:"ARE",iso1N3:"784",wikidata:"Q878",nameEn:"United Arab Emirates",groups:["145","142"],callingCodes:["971"]},geometry:{type:"MultiPolygon",coordinates:[[[[56.26534,25.62825],[56.25341,25.61443],[56.26636,25.60643],[56.25365,25.60211],[56.20473,25.61119],[56.18363,25.65508],[56.14826,25.66351],[56.13579,25.73524],[56.17416,25.77239],[56.13963,25.82765],[56.19334,25.9795],[56.15498,26.06828],[56.08666,26.05038],[55.81777,26.18798],[55.14145,25.62624],[53.97892,24.64436],[52.82259,25.51697],[52.35509,25.00368],[52.02277,24.75635],[51.83108,24.71675],[51.58834,24.66608],[51.41644,24.39615],[51.58871,24.27256],[51.59617,24.12041],[52.56622,22.94341],[55.13599,22.63334],[55.2137,22.71065],[55.22634,23.10378],[55.57358,23.669],[55.48677,23.94946],[55.73301,24.05994],[55.8308,24.01633],[56.01799,24.07426],[55.95472,24.2172],[55.83367,24.20193],[55.77658,24.23476],[55.76558,24.23227],[55.75257,24.23466],[55.75382,24.2466],[55.75939,24.26114],[55.76781,24.26209],[55.79145,24.27914],[55.80747,24.31069],[55.83395,24.32776],[55.83271,24.41521],[55.76461,24.5287],[55.83271,24.68567],[55.83408,24.77858],[55.81348,24.80102],[55.81116,24.9116],[55.85094,24.96858],[55.90849,24.96771],[55.96316,25.00857],[56.05715,24.95727],[56.05106,24.87461],[55.97467,24.89639],[55.97836,24.87673],[56.03535,24.81161],[56.06128,24.74457],[56.13684,24.73699],[56.20062,24.78565],[56.20568,24.85063],[56.30269,24.88334],[56.34873,24.93205],[56.3227,24.97284],[56.86325,25.03856],[56.82555,25.7713],[56.26534,25.62825]],[[56.26062,25.33108],[56.3005,25.31815],[56.3111,25.30107],[56.35172,25.30681],[56.34438,25.26653],[56.27628,25.23404],[56.24341,25.22867],[56.20872,25.24104],[56.20838,25.25668],[56.24465,25.27505],[56.25008,25.28843],[56.23362,25.31253],[56.26062,25.33108]]],[[[56.28423,25.26344],[56.29379,25.2754],[56.28102,25.28486],[56.2716,25.27916],[56.27086,25.26128],[56.28423,25.26344]]]]}},{type:"Feature",properties:{iso1A2:"AF",iso1A3:"AFG",iso1N3:"004",wikidata:"Q889",nameEn:"Afghanistan",groups:["034","142"],callingCodes:["93"]},geometry:{type:"MultiPolygon",coordinates:[[[[70.61526,38.34774],[70.60407,38.28046],[70.54673,38.24541],[70.4898,38.12546],[70.17206,37.93276],[70.1863,37.84296],[70.27694,37.81258],[70.28243,37.66706],[70.15015,37.52519],[69.95971,37.5659],[69.93362,37.61378],[69.84435,37.60616],[69.80041,37.5746],[69.51888,37.5844],[69.44954,37.4869],[69.36645,37.40462],[69.45022,37.23315],[69.39529,37.16752],[69.25152,37.09426],[69.03274,37.25174],[68.96407,37.32603],[68.88168,37.33368],[68.91189,37.26704],[68.80889,37.32494],[68.81438,37.23862],[68.6798,37.27906],[68.61851,37.19815],[68.41888,37.13906],[68.41201,37.10402],[68.29253,37.10621],[68.27605,37.00977],[68.18542,37.02074],[68.02194,36.91923],[67.87917,37.0591],[67.7803,37.08978],[67.78329,37.1834],[67.51868,37.26102],[67.2581,37.17216],[67.2224,37.24545],[67.13039,37.27168],[67.08232,37.35469],[66.95598,37.40162],[66.64699,37.32958],[66.55743,37.35409],[66.30993,37.32409],[65.72274,37.55438],[65.64137,37.45061],[65.64263,37.34388],[65.51778,37.23881],[64.97945,37.21913],[64.61141,36.6351],[64.62514,36.44311],[64.57295,36.34362],[64.43288,36.24401],[64.05385,36.10433],[63.98519,36.03773],[63.56496,35.95106],[63.53475,35.90881],[63.29579,35.85985],[63.12276,35.86208],[63.10318,35.81782],[63.23262,35.67487],[63.10079,35.63024],[63.12276,35.53196],[63.0898,35.43131],[62.90853,35.37086],[62.74098,35.25432],[62.62288,35.22067],[62.48006,35.28796],[62.29878,35.13312],[62.29191,35.25964],[62.15871,35.33278],[62.05709,35.43803],[61.97743,35.4604],[61.77693,35.41341],[61.58742,35.43803],[61.27371,35.61482],[61.18187,35.30249],[61.0991,35.27845],[61.12831,35.09938],[61.06926,34.82139],[61.00197,34.70631],[60.99922,34.63064],[60.72316,34.52857],[60.91321,34.30411],[60.66502,34.31539],[60.50209,34.13992],[60.5838,33.80793],[60.5485,33.73422],[60.57762,33.59772],[60.69573,33.56054],[60.91133,33.55596],[60.88908,33.50219],[60.56485,33.12944],[60.86191,32.22565],[60.84541,31.49561],[61.70929,31.37391],[61.80569,31.16167],[61.80957,31.12576],[61.83257,31.0452],[61.8335,30.97669],[61.78268,30.92724],[61.80829,30.84224],[60.87231,29.86514],[62.47751,29.40782],[63.5876,29.50456],[64.12966,29.39157],[64.19796,29.50407],[64.62116,29.58903],[65.04005,29.53957],[66.24175,29.85181],[66.36042,29.9583],[66.23609,30.06321],[66.34869,30.404],[66.28413,30.57001],[66.39194,30.9408],[66.42645,30.95309],[66.58175,30.97532],[66.68166,31.07597],[66.72561,31.20526],[66.83273,31.26867],[67.04147,31.31561],[67.03323,31.24519],[67.29964,31.19586],[67.78854,31.33203],[67.7748,31.4188],[67.62374,31.40473],[67.58323,31.52772],[67.72056,31.52304],[67.86887,31.63536],[68.00071,31.6564],[68.1655,31.82691],[68.25614,31.80357],[68.27605,31.75863],[68.44222,31.76446],[68.57475,31.83158],[68.6956,31.75687],[68.79997,31.61665],[68.91078,31.59687],[68.95995,31.64822],[69.00939,31.62249],[69.11514,31.70782],[69.20577,31.85957],[69.3225,31.93186],[69.27032,32.14141],[69.27932,32.29119],[69.23599,32.45946],[69.2868,32.53938],[69.38155,32.56601],[69.44747,32.6678],[69.43649,32.7302],[69.38018,32.76601],[69.47082,32.85834],[69.5436,32.8768],[69.49854,32.88843],[69.49004,33.01509],[69.57656,33.09911],[69.71526,33.09911],[69.79766,33.13247],[69.85259,33.09451],[70.02563,33.14282],[70.07369,33.22557],[70.13686,33.21064],[70.32775,33.34496],[70.17062,33.53535],[70.20141,33.64387],[70.14785,33.6553],[70.14236,33.71701],[70.00503,33.73528],[69.85671,33.93719],[69.87307,33.9689],[69.90203,34.04194],[70.54336,33.9463],[70.88119,33.97933],[71.07345,34.06242],[71.06933,34.10564],[71.09307,34.11961],[71.09453,34.13524],[71.13078,34.16503],[71.12815,34.26619],[71.17662,34.36769],[71.02401,34.44835],[71.0089,34.54568],[71.11602,34.63047],[71.08718,34.69034],[71.28356,34.80882],[71.29472,34.87728],[71.50329,34.97328],[71.49917,35.00478],[71.55273,35.02615],[71.52938,35.09023],[71.67495,35.21262],[71.5541,35.28776],[71.54294,35.31037],[71.65435,35.4479],[71.49917,35.6267],[71.55273,35.71483],[71.37969,35.95865],[71.19505,36.04134],[71.60491,36.39429],[71.80267,36.49924],[72.18135,36.71838],[72.6323,36.84601],[73.82685,36.91421],[74.04856,36.82648],[74.43389,37.00977],[74.53739,36.96224],[74.56453,37.03023],[74.49981,37.24518],[74.80605,37.21565],[74.88887,37.23275],[74.8294,37.3435],[74.68383,37.3948],[74.56161,37.37734],[74.41055,37.3948],[74.23339,37.41116],[74.20308,37.34208],[73.8564,37.26158],[73.82552,37.22659],[73.64974,37.23643],[73.61129,37.27469],[73.76647,37.33913],[73.77197,37.4417],[73.29633,37.46495],[73.06884,37.31729],[72.79693,37.22222],[72.66381,37.02014],[72.54095,37.00007],[72.31676,36.98115],[71.83229,36.68084],[71.67083,36.67346],[71.57195,36.74943],[71.51502,36.89128],[71.48481,36.93218],[71.46923,36.99925],[71.45578,37.03094],[71.43097,37.05855],[71.44127,37.11856],[71.4494,37.18137],[71.4555,37.21418],[71.47386,37.2269],[71.48339,37.23937],[71.4824,37.24921],[71.48536,37.26017],[71.50674,37.31502],[71.49821,37.31975],[71.4862,37.33405],[71.47685,37.40281],[71.49612,37.4279],[71.5256,37.47971],[71.50616,37.50733],[71.49693,37.53527],[71.5065,37.60912],[71.51972,37.61945],[71.54186,37.69691],[71.55234,37.73209],[71.53053,37.76534],[71.54324,37.77104],[71.55752,37.78677],[71.59255,37.79956],[71.58843,37.92425],[71.51565,37.95349],[71.32871,37.88564],[71.296,37.93403],[71.2809,37.91995],[71.24969,37.93031],[71.27278,37.96496],[71.27622,37.99946],[71.28922,38.01272],[71.29878,38.04429],[71.36444,38.15358],[71.37803,38.25641],[71.33869,38.27335],[71.33114,38.30339],[71.21291,38.32797],[71.1451,38.40106],[71.10957,38.40671],[71.10592,38.42077],[71.09542,38.42517],[71.0556,38.40176],[71.03545,38.44779],[70.98693,38.48862],[70.92728,38.43021],[70.88719,38.46826],[70.84376,38.44688],[70.82538,38.45394],[70.81697,38.44507],[70.80521,38.44447],[70.79766,38.44944],[70.78702,38.45031],[70.78581,38.45502],[70.77132,38.45548],[70.75455,38.4252],[70.72485,38.4131],[70.69807,38.41861],[70.67438,38.40597],[70.6761,38.39144],[70.69189,38.37031],[70.64966,38.34999],[70.61526,38.34774]]]]}},{type:"Feature",properties:{iso1A2:"AG",iso1A3:"ATG",iso1N3:"028",wikidata:"Q781",nameEn:"Antigua and Barbuda",groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 268"]},geometry:{type:"MultiPolygon",coordinates:[[[[-62.12601,17.9235],[-62.27053,17.22145],[-62.62949,16.82364],[-62.52079,16.69392],[-62.14123,17.02632],[-61.83929,16.66647],[-61.44461,16.81958],[-61.45764,17.9187],[-62.12601,17.9235]]]]}},{type:"Feature",properties:{iso1A2:"AI",iso1A3:"AIA",iso1N3:"660",wikidata:"Q25228",nameEn:"Anguilla",country:"GB",groups:["029","003","419","019"],driveSide:"left",callingCodes:["1 264"]},geometry:{type:"MultiPolygon",coordinates:[[[[-63.83866,18.82518],[-63.35989,18.06012],[-62.86666,18.19278],[-62.75637,18.13489],[-62.46233,19.00569],[-63.83866,18.82518]]]]}},{type:"Feature",properties:{iso1A2:"AL",iso1A3:"ALB",iso1N3:"008",wikidata:"Q222",nameEn:"Albania",groups:["039","150"],callingCodes:["355"]},geometry:{type:"MultiPolygon",coordinates:[[[[20.07761,42.55582],[20.01834,42.54622],[20.00842,42.5109],[19.9324,42.51699],[19.82333,42.46581],[19.76549,42.50237],[19.74731,42.57422],[19.77375,42.58517],[19.73244,42.66299],[19.65972,42.62774],[19.4836,42.40831],[19.42352,42.36546],[19.42,42.33019],[19.28623,42.17745],[19.40687,42.10024],[19.37548,42.06835],[19.36867,42.02564],[19.37691,41.96977],[19.34601,41.95675],[19.33812,41.90669],[19.37451,41.8842],[19.37597,41.84849],[19.26406,41.74971],[19.0384,40.35325],[19.95905,39.82857],[19.97622,39.78684],[19.92466,39.69533],[19.98042,39.6504],[20.00957,39.69227],[20.05189,39.69112],[20.12956,39.65805],[20.15988,39.652],[20.22376,39.64532],[20.22707,39.67459],[20.27412,39.69884],[20.31961,39.72799],[20.29152,39.80421],[20.30804,39.81563],[20.38572,39.78516],[20.41475,39.81437],[20.41546,39.82832],[20.31135,39.99438],[20.37911,39.99058],[20.42373,40.06777],[20.48487,40.06271],[20.51297,40.08168],[20.55593,40.06524],[20.61081,40.07866],[20.62566,40.0897],[20.67162,40.09433],[20.71789,40.27739],[20.78234,40.35803],[20.7906,40.42726],[20.83688,40.47882],[20.94925,40.46625],[20.96908,40.51526],[21.03932,40.56299],[21.05833,40.66586],[20.98134,40.76046],[20.95752,40.76982],[20.98396,40.79109],[20.97887,40.85475],[20.97693,40.90103],[20.94305,40.92399],[20.83671,40.92752],[20.81567,40.89662],[20.73504,40.9081],[20.71634,40.91781],[20.65558,41.08009],[20.63454,41.0889],[20.59832,41.09066],[20.58546,41.11179],[20.59715,41.13644],[20.51068,41.2323],[20.49432,41.33679],[20.52119,41.34381],[20.55976,41.4087],[20.51301,41.442],[20.49039,41.49277],[20.45331,41.51436],[20.45809,41.5549],[20.52103,41.56473],[20.55508,41.58113],[20.51769,41.65975],[20.52937,41.69292],[20.51301,41.72433],[20.53405,41.78099],[20.57144,41.7897],[20.55976,41.87068],[20.59524,41.8818],[20.57946,41.91593],[20.63069,41.94913],[20.59434,42.03879],[20.55633,42.08173],[20.56955,42.12097],[20.48857,42.25444],[20.3819,42.3029],[20.34479,42.32656],[20.24399,42.32168],[20.21797,42.41237],[20.17127,42.50469],[20.07761,42.55582]]]]}},{type:"Feature",properties:{iso1A2:"AM",iso1A3:"ARM",iso1N3:"051",wikidata:"Q399",nameEn:"Armenia",groups:["145","142"],callingCodes:["374"]},geometry:{type:"MultiPolygon",coordinates:[[[[45.0133,41.29747],[44.93493,41.25685],[44.81437,41.30371],[44.80053,41.25949],[44.81749,41.23488],[44.84358,41.23088],[44.89911,41.21366],[44.87887,41.20195],[44.82084,41.21513],[44.72814,41.20338],[44.61462,41.24018],[44.59322,41.1933],[44.46791,41.18204],[44.34417,41.2382],[44.34337,41.20312],[44.32139,41.2079],[44.18148,41.24644],[44.16591,41.19141],[43.84835,41.16329],[43.74717,41.1117],[43.67712,41.13398],[43.4717,41.12611],[43.44984,41.0988],[43.47319,41.02251],[43.58683,40.98961],[43.67712,40.93084],[43.67712,40.84846],[43.74872,40.7365],[43.7425,40.66805],[43.63664,40.54159],[43.54791,40.47413],[43.60862,40.43267],[43.59928,40.34019],[43.71136,40.16673],[43.65221,40.14889],[43.65688,40.11199],[43.92307,40.01787],[44.1057,40.03555],[44.1778,40.02845],[44.26973,40.04866],[44.46635,39.97733],[44.61845,39.8281],[44.75779,39.7148],[44.88354,39.74432],[44.92869,39.72157],[45.06604,39.79277],[45.18554,39.67846],[45.17464,39.58614],[45.21784,39.58074],[45.23535,39.61373],[45.30385,39.61373],[45.29606,39.57654],[45.46992,39.49888],[45.70547,39.60174],[45.80804,39.56716],[45.83,39.46487],[45.79225,39.3695],[45.99774,39.28931],[46.02303,39.09978],[46.06973,39.0744],[46.14785,38.84206],[46.20601,38.85262],[46.34059,38.92076],[46.53497,38.86548],[46.51805,38.94982],[46.54296,39.07078],[46.44022,39.19636],[46.52584,39.18912],[46.54141,39.15895],[46.58032,39.21204],[46.63481,39.23013],[46.56476,39.24942],[46.50093,39.33736],[46.43244,39.35181],[46.37795,39.42039],[46.4013,39.45405],[46.53051,39.47809],[46.51027,39.52373],[46.57721,39.54414],[46.57098,39.56694],[46.52117,39.58734],[46.42465,39.57534],[46.40286,39.63651],[46.18493,39.60533],[45.96543,39.78859],[45.82533,39.82925],[45.7833,39.9475],[45.60895,39.97733],[45.59806,40.0131],[45.78642,40.03218],[45.83779,39.98925],[45.97944,40.181],[45.95609,40.27846],[45.65098,40.37696],[45.42994,40.53804],[45.45484,40.57707],[45.35366,40.65979],[45.4206,40.7424],[45.55914,40.78366],[45.60584,40.87436],[45.40814,40.97904],[45.44083,41.01663],[45.39725,41.02603],[45.35677,40.99784],[45.28859,41.03757],[45.26162,41.0228],[45.25897,41.0027],[45.1994,41.04518],[45.16493,41.05068],[45.1634,41.08082],[45.1313,41.09369],[45.12923,41.06059],[45.06784,41.05379],[45.08028,41.10917],[45.19942,41.13299],[45.1969,41.168],[45.11811,41.19967],[45.05201,41.19211],[45.02932,41.2101],[45.05497,41.2464],[45.0133,41.29747]],[[45.21324,40.9817],[45.21219,40.99001],[45.20518,40.99348],[45.19312,40.98998],[45.18382,41.0066],[45.20625,41.01484],[45.23487,41.00226],[45.23095,40.97828],[45.21324,40.9817]],[[45.00864,41.03411],[44.9903,41.05657],[44.96031,41.06345],[44.95383,41.07553],[44.97169,41.09176],[45.00864,41.09407],[45.03406,41.07931],[45.04517,41.06653],[45.03792,41.03938],[45.00864,41.03411]]],[[[45.50279,40.58424],[45.56071,40.64765],[45.51825,40.67382],[45.47927,40.65023],[45.50279,40.58424]]]]}},{type:"Feature",properties:{iso1A2:"AO",iso1A3:"AGO",iso1N3:"024",wikidata:"Q916",nameEn:"Angola",groups:["017","202","002"],callingCodes:["244"]},geometry:{type:"MultiPolygon",coordinates:[[[[16.55507,-5.85631],[13.04371,-5.87078],[12.42245,-6.07585],[11.95767,-5.94705],[12.20376,-5.76338],[12.26557,-5.74031],[12.52318,-5.74353],[12.52301,-5.17481],[12.53599,-5.1618],[12.53586,-5.14658],[12.51589,-5.1332],[12.49815,-5.14058],[12.46297,-5.09408],[12.60251,-5.01715],[12.63465,-4.94632],[12.70868,-4.95505],[12.8733,-4.74346],[13.11195,-4.67745],[13.09648,-4.63739],[12.91489,-4.47907],[12.87096,-4.40315],[12.76844,-4.38709],[12.64835,-4.55937],[12.40964,-4.60609],[12.32324,-4.78415],[12.25587,-4.79437],[12.20901,-4.75642],[12.16068,-4.90089],[12.00924,-5.02627],[11.50888,-5.33417],[10.5065,-17.25284],[11.75063,-17.25013],[12.07076,-17.15165],[12.52111,-17.24495],[12.97145,-16.98567],[13.36212,-16.98048],[13.95896,-17.43141],[14.28743,-17.38814],[18.39229,-17.38927],[18.84226,-17.80375],[21.14283,-17.94318],[21.42741,-18.02787],[23.47474,-17.62877],[23.20038,-17.47563],[22.17217,-16.50269],[22.00323,-16.18028],[21.97988,-13.00148],[24.03339,-12.99091],[23.90937,-12.844],[24.06672,-12.29058],[23.98804,-12.13149],[24.02603,-11.15368],[24.00027,-10.89356],[23.86868,-11.02856],[23.45631,-10.946],[23.16602,-11.10577],[22.54205,-11.05784],[22.25951,-11.24911],[22.17954,-10.85884],[22.32604,-10.76291],[22.19039,-9.94628],[21.84856,-9.59871],[21.79824,-7.29628],[20.56263,-7.28566],[20.61689,-6.90876],[20.31846,-6.91953],[20.30218,-6.98955],[19.5469,-7.00195],[19.33698,-7.99743],[18.33635,-8.00126],[17.5828,-8.13784],[16.96282,-7.21787],[16.55507,-5.85631]]]]}},{type:"Feature",properties:{iso1A2:"AQ",iso1A3:"ATA",iso1N3:"010",wikidata:"Q51",nameEn:"Antarctica",level:"region",callingCodes:["672"]},geometry:{type:"MultiPolygon",coordinates:[[[[180,-60],[-180,-60],[-180,-90],[180,-90],[180,-60]]]]}},{type:"Feature",properties:{iso1A2:"AR",iso1A3:"ARG",iso1N3:"032",wikidata:"Q414",nameEn:"Argentina",aliases:["RA"],groups:["005","419","019"],callingCodes:["54"]},geometry:{type:"MultiPolygon",coordinates:[[[[-72.31343,-50.58411],[-72.33873,-51.59954],[-71.99889,-51.98018],[-69.97824,-52.00845],[-68.41683,-52.33516],[-68.60702,-52.65781],[-68.60733,-54.9125],[-68.01394,-54.8753],[-67.46182,-54.92205],[-67.11046,-54.94199],[-66.07313,-55.19618],[-63.67376,-55.11859],[-54.78916,-36.21945],[-57.83001,-34.69099],[-58.34425,-34.15035],[-58.44442,-33.84033],[-58.40475,-33.11777],[-58.1224,-32.98842],[-58.22362,-32.52416],[-58.10036,-32.25338],[-58.20252,-31.86966],[-58.00076,-31.65016],[-58.0023,-31.53084],[-58.07569,-31.44916],[-57.98127,-31.3872],[-57.9908,-31.34924],[-57.86729,-31.06352],[-57.89476,-30.95994],[-57.8024,-30.77193],[-57.89115,-30.49572],[-57.64859,-30.35095],[-57.61478,-30.25165],[-57.65132,-30.19229],[-57.09386,-29.74211],[-56.81251,-29.48154],[-56.62789,-29.18073],[-56.57295,-29.11357],[-56.54171,-29.11447],[-56.05265,-28.62651],[-56.00458,-28.60421],[-56.01729,-28.51223],[-55.65418,-28.18304],[-55.6262,-28.17124],[-55.33303,-27.94661],[-55.16872,-27.86224],[-55.1349,-27.89759],[-54.90805,-27.73149],[-54.90159,-27.63132],[-54.67657,-27.57214],[-54.50416,-27.48232],[-54.41888,-27.40882],[-54.19268,-27.30751],[-54.19062,-27.27639],[-54.15978,-27.2889],[-53.80144,-27.09844],[-53.73372,-26.6131],[-53.68269,-26.33359],[-53.64505,-26.28089],[-53.64186,-26.25976],[-53.64632,-26.24798],[-53.63881,-26.25075],[-53.63739,-26.2496],[-53.65237,-26.23289],[-53.65018,-26.19501],[-53.73968,-26.10012],[-53.73391,-26.07006],[-53.7264,-26.0664],[-53.73086,-26.05842],[-53.73511,-26.04211],[-53.83691,-25.94849],[-53.90831,-25.55513],[-54.52926,-25.62846],[-54.5502,-25.58915],[-54.59398,-25.59224],[-54.62063,-25.91213],[-54.60664,-25.9691],[-54.67359,-25.98607],[-54.69333,-26.37705],[-54.70732,-26.45099],[-54.80868,-26.55669],[-55.00584,-26.78754],[-55.06351,-26.80195],[-55.16948,-26.96068],[-55.25243,-26.93808],[-55.39611,-26.97679],[-55.62322,-27.1941],[-55.59094,-27.32444],[-55.74475,-27.44485],[-55.89195,-27.3467],[-56.18313,-27.29851],[-56.85337,-27.5165],[-58.04205,-27.2387],[-58.59549,-27.29973],[-58.65321,-27.14028],[-58.3198,-26.83443],[-58.1188,-26.16704],[-57.87176,-25.93604],[-57.57431,-25.47269],[-57.80821,-25.13863],[-58.25492,-24.92528],[-58.33055,-24.97099],[-59.33886,-24.49935],[-59.45482,-24.34787],[-60.03367,-24.00701],[-60.28163,-24.04436],[-60.99754,-23.80934],[-61.0782,-23.62932],[-61.9756,-23.0507],[-62.22768,-22.55807],[-62.51761,-22.37684],[-62.64455,-22.25091],[-62.8078,-22.12534],[-62.81124,-21.9987],[-63.66482,-21.99918],[-63.68113,-22.0544],[-63.70963,-21.99934],[-63.93287,-21.99934],[-64.22918,-22.55807],[-64.31489,-22.88824],[-64.35108,-22.73282],[-64.4176,-22.67692],[-64.58888,-22.25035],[-64.67174,-22.18957],[-64.90014,-22.12136],[-64.99524,-22.08255],[-65.47435,-22.08908],[-65.57743,-22.07675],[-65.58694,-22.09794],[-65.61166,-22.09504],[-65.7467,-22.10105],[-65.9261,-21.93335],[-66.04832,-21.9187],[-66.03836,-21.84829],[-66.24077,-21.77837],[-66.29714,-22.08741],[-66.7298,-22.23644],[-67.18382,-22.81525],[-66.99632,-22.99839],[-67.33563,-24.04237],[-68.24825,-24.42596],[-68.56909,-24.69831],[-68.38372,-25.08636],[-68.57622,-25.32505],[-68.38372,-26.15353],[-68.56909,-26.28146],[-68.59048,-26.49861],[-68.27677,-26.90626],[-68.43363,-27.08414],[-68.77586,-27.16029],[-69.22504,-27.95042],[-69.66709,-28.44055],[-69.80969,-29.07185],[-69.99507,-29.28351],[-69.8596,-30.26131],[-70.14479,-30.36595],[-70.55832,-31.51559],[-69.88099,-33.34489],[-69.87386,-34.13344],[-70.49416,-35.24145],[-70.38008,-36.02375],[-70.95047,-36.4321],[-71.24279,-37.20264],[-70.89532,-38.6923],[-71.37826,-38.91474],[-71.92726,-40.72714],[-71.74901,-42.11711],[-72.15541,-42.15941],[-72.14828,-42.85321],[-71.64206,-43.64774],[-71.81318,-44.38097],[-71.16436,-44.46244],[-71.26418,-44.75684],[-72.06985,-44.81756],[-71.35687,-45.22075],[-71.75614,-45.61611],[-71.68577,-46.55385],[-71.94152,-47.13595],[-72.50478,-47.80586],[-72.27662,-48.28727],[-72.54042,-48.52392],[-72.56894,-48.81116],[-73.09655,-49.14342],[-73.45156,-49.79461],[-73.55259,-49.92488],[-73.15765,-50.78337],[-72.31343,-50.58411]]]]}},{type:"Feature",properties:{iso1A2:"AS",iso1A3:"ASM",iso1N3:"016",wikidata:"Q16641",nameEn:"American Samoa",country:"US",groups:["061","009"],roadSpeedUnit:"mph",callingCodes:["1 684"]},geometry:{type:"MultiPolygon",coordinates:[[[[-174.18596,-12.48057],[-171.14953,-12.4725],[-171.14262,-14.93704],[-167.73854,-14.92809],[-167.75195,-10.12005],[-174.17993,-10.13616],[-174.18596,-12.48057]]]]}},{type:"Feature",properties:{iso1A2:"AT",iso1A3:"AUT",iso1N3:"040",wikidata:"Q40",nameEn:"Austria",groups:["EU","155","150"],callingCodes:["43"]},geometry:{type:"MultiPolygon",coordinates:[[[[15.34823,48.98444],[15.28305,48.98831],[15.26177,48.95766],[15.16358,48.94278],[15.15534,48.99056],[14.99878,49.01444],[14.97612,48.96983],[14.98917,48.90082],[14.95072,48.79101],[14.98032,48.77959],[14.9782,48.7766],[14.98112,48.77524],[14.9758,48.76857],[14.95641,48.75915],[14.94773,48.76268],[14.81545,48.7874],[14.80821,48.77711],[14.80584,48.73489],[14.72756,48.69502],[14.71794,48.59794],[14.66762,48.58215],[14.60808,48.62881],[14.56139,48.60429],[14.4587,48.64695],[14.43076,48.58855],[14.33909,48.55852],[14.20691,48.5898],[14.09104,48.5943],[14.01482,48.63788],[14.06151,48.66873],[13.84023,48.76988],[13.82266,48.75544],[13.81863,48.73257],[13.79337,48.71375],[13.81791,48.69832],[13.81283,48.68426],[13.81901,48.6761],[13.82609,48.62345],[13.80038,48.59487],[13.80519,48.58026],[13.76921,48.55324],[13.7513,48.5624],[13.74816,48.53058],[13.72802,48.51208],[13.66113,48.53558],[13.65186,48.55092],[13.62508,48.55501],[13.59705,48.57013],[13.57535,48.55912],[13.51291,48.59023],[13.50131,48.58091],[13.50663,48.57506],[13.46967,48.55157],[13.45214,48.56472],[13.43695,48.55776],[13.45727,48.51092],[13.42527,48.45711],[13.43929,48.43386],[13.40709,48.37292],[13.30897,48.31575],[13.26039,48.29422],[13.18093,48.29577],[13.126,48.27867],[13.0851,48.27711],[13.02083,48.25689],[12.95306,48.20629],[12.87126,48.20318],[12.84475,48.16556],[12.836,48.1647],[12.8362,48.15876],[12.82673,48.15245],[12.80676,48.14979],[12.78595,48.12445],[12.7617,48.12796],[12.74973,48.10885],[12.76141,48.07373],[12.8549,48.01122],[12.87476,47.96195],[12.91683,47.95647],[12.9211,47.95135],[12.91985,47.94069],[12.92668,47.93879],[12.93419,47.94063],[12.93642,47.94436],[12.93886,47.94046],[12.94163,47.92927],[13.00588,47.84374],[12.98543,47.82896],[12.96311,47.79957],[12.93202,47.77302],[12.94371,47.76281],[12.9353,47.74788],[12.91711,47.74026],[12.90274,47.72513],[12.91333,47.7178],[12.92969,47.71094],[12.98578,47.7078],[13.01382,47.72116],[13.07692,47.68814],[13.09562,47.63304],[13.06407,47.60075],[13.06641,47.58577],[13.04537,47.58183],[13.05355,47.56291],[13.03252,47.53373],[13.04537,47.49426],[12.9998,47.46267],[12.98344,47.48716],[12.9624,47.47452],[12.85256,47.52741],[12.84672,47.54556],[12.80699,47.54477],[12.77427,47.58025],[12.82101,47.61493],[12.76492,47.64485],[12.77777,47.66689],[12.7357,47.6787],[12.6071,47.6741],[12.57438,47.63238],[12.53816,47.63553],[12.50076,47.62293],[12.44117,47.6741],[12.43883,47.6977],[12.37222,47.68433],[12.336,47.69534],[12.27991,47.68827],[12.26004,47.67725],[12.24017,47.69534],[12.26238,47.73544],[12.2542,47.7433],[12.22571,47.71776],[12.18303,47.70065],[12.16217,47.70105],[12.16769,47.68167],[12.18347,47.66663],[12.18507,47.65984],[12.19895,47.64085],[12.20801,47.61082],[12.20398,47.60667],[12.18568,47.6049],[12.17737,47.60121],[12.18145,47.61019],[12.17824,47.61506],[12.13734,47.60639],[12.05788,47.61742],[12.02282,47.61033],[12.0088,47.62451],[11.85572,47.60166],[11.84052,47.58354],[11.63934,47.59202],[11.60681,47.57881],[11.58811,47.55515],[11.58578,47.52281],[11.52618,47.50939],[11.4362,47.51413],[11.38128,47.47465],[11.4175,47.44621],[11.33804,47.44937],[11.29597,47.42566],[11.27844,47.39956],[11.22002,47.3964],[11.25157,47.43277],[11.20482,47.43198],[11.12536,47.41222],[11.11835,47.39719],[10.97111,47.39561],[10.97111,47.41617],[10.98513,47.42882],[10.92437,47.46991],[10.93839,47.48018],[10.90918,47.48571],[10.87061,47.4786],[10.86945,47.5015],[10.91268,47.51334],[10.88814,47.53701],[10.77596,47.51729],[10.7596,47.53228],[10.6965,47.54253],[10.68832,47.55752],[10.63456,47.5591],[10.60337,47.56755],[10.56912,47.53584],[10.48849,47.54057],[10.47329,47.58552],[10.43473,47.58394],[10.44992,47.5524],[10.4324,47.50111],[10.44291,47.48453],[10.46278,47.47901],[10.47446,47.43318],[10.4359,47.41183],[10.4324,47.38494],[10.39851,47.37623],[10.33424,47.30813],[10.23257,47.27088],[10.17531,47.27167],[10.17648,47.29149],[10.2147,47.31014],[10.19998,47.32832],[10.23757,47.37609],[10.22774,47.38904],[10.2127,47.38019],[10.17648,47.38889],[10.16362,47.36674],[10.11805,47.37228],[10.09819,47.35724],[10.06897,47.40709],[10.1052,47.4316],[10.09001,47.46005],[10.07131,47.45531],[10.03859,47.48927],[10.00003,47.48216],[9.96029,47.53899],[9.92407,47.53111],[9.87733,47.54688],[9.87499,47.52953],[9.8189,47.54688],[9.82591,47.58158],[9.80254,47.59419],[9.76748,47.5934],[9.72736,47.53457],[9.55125,47.53629],[9.56312,47.49495],[9.58208,47.48344],[9.59482,47.46305],[9.60205,47.46165],[9.60484,47.46358],[9.60841,47.47178],[9.62158,47.45858],[9.62475,47.45685],[9.6423,47.45599],[9.65728,47.45383],[9.65863,47.44847],[9.64483,47.43842],[9.6446,47.43233],[9.65043,47.41937],[9.65136,47.40504],[9.6629,47.39591],[9.67334,47.39191],[9.67445,47.38429],[9.6711,47.37824],[9.66243,47.37136],[9.65427,47.36824],[9.62476,47.36639],[9.59978,47.34671],[9.58513,47.31334],[9.55857,47.29919],[9.54773,47.2809],[9.53116,47.27029],[9.56766,47.24281],[9.55176,47.22585],[9.56981,47.21926],[9.58264,47.20673],[9.56539,47.17124],[9.62623,47.14685],[9.63395,47.08443],[9.61216,47.07732],[9.60717,47.06091],[9.87935,47.01337],[9.88266,46.93343],[9.98058,46.91434],[10.10715,46.84296],[10.22675,46.86942],[10.24128,46.93147],[10.30031,46.92093],[10.36933,47.00212],[10.48376,46.93891],[10.47197,46.85698],[10.54783,46.84505],[10.66405,46.87614],[10.75753,46.82258],[10.72974,46.78972],[11.00764,46.76896],[11.10618,46.92966],[11.33355,46.99862],[11.50739,47.00644],[11.74789,46.98484],[12.19254,47.09331],[12.21781,47.03996],[12.11675,47.01241],[12.2006,46.88854],[12.27591,46.88651],[12.38708,46.71529],[12.59992,46.6595],[12.94445,46.60401],[13.27627,46.56059],[13.64088,46.53438],[13.7148,46.5222],[13.89837,46.52331],[14.00422,46.48474],[14.04002,46.49117],[14.12097,46.47724],[14.15989,46.43327],[14.28326,46.44315],[14.314,46.43327],[14.42608,46.44614],[14.45877,46.41717],[14.52176,46.42617],[14.56463,46.37208],[14.5942,46.43434],[14.66892,46.44936],[14.72185,46.49974],[14.81836,46.51046],[14.83549,46.56614],[14.86419,46.59411],[14.87129,46.61],[14.92283,46.60848],[14.96002,46.63459],[14.98024,46.6009],[15.01451,46.641],[15.14215,46.66131],[15.23711,46.63994],[15.41235,46.65556],[15.45514,46.63697],[15.46906,46.61321],[15.54431,46.6312],[15.55333,46.64988],[15.54533,46.66985],[15.59826,46.68908],[15.62317,46.67947],[15.63255,46.68069],[15.6365,46.6894],[15.6543,46.69228],[15.6543,46.70616],[15.67411,46.70735],[15.69523,46.69823],[15.72279,46.69548],[15.73823,46.70011],[15.76771,46.69863],[15.78518,46.70712],[15.8162,46.71897],[15.87691,46.7211],[15.94864,46.68769],[15.98512,46.68463],[15.99988,46.67947],[16.04036,46.6549],[16.04347,46.68694],[16.02808,46.71094],[15.99769,46.7266],[15.98432,46.74991],[15.99126,46.78199],[15.99054,46.82772],[16.05786,46.83927],[16.10983,46.867],[16.19904,46.94134],[16.22403,46.939],[16.27594,46.9643],[16.28202,47.00159],[16.51369,47.00084],[16.43936,47.03548],[16.52176,47.05747],[16.46134,47.09395],[16.52863,47.13974],[16.44932,47.14418],[16.46442,47.16845],[16.4523,47.18812],[16.42801,47.18422],[16.41739,47.20649],[16.43663,47.21127],[16.44142,47.25079],[16.47782,47.25918],[16.45104,47.41181],[16.49908,47.39416],[16.52414,47.41007],[16.57152,47.40868],[16.6718,47.46139],[16.64821,47.50155],[16.71059,47.52692],[16.64193,47.63114],[16.58699,47.61772],[16.4222,47.66537],[16.55129,47.72268],[16.53514,47.73837],[16.54779,47.75074],[16.61183,47.76171],[16.65679,47.74197],[16.72089,47.73469],[16.7511,47.67878],[16.82938,47.68432],[16.86509,47.72268],[16.87538,47.68895],[17.08893,47.70928],[17.05048,47.79377],[17.07039,47.81129],[17.00997,47.86245],[17.08275,47.87719],[17.11022,47.92461],[17.09786,47.97336],[17.16001,48.00636],[17.07039,48.0317],[17.09168,48.09366],[17.05735,48.14179],[17.02919,48.13996],[16.97701,48.17385],[16.89461,48.31332],[16.90903,48.32519],[16.84243,48.35258],[16.83317,48.38138],[16.83588,48.3844],[16.8497,48.38321],[16.85204,48.44968],[16.94611,48.53614],[16.93955,48.60371],[16.90354,48.71541],[16.79779,48.70998],[16.71883,48.73806],[16.68518,48.7281],[16.67008,48.77699],[16.46134,48.80865],[16.40915,48.74576],[16.37345,48.729],[16.06034,48.75436],[15.84404,48.86921],[15.78087,48.87644],[15.75341,48.8516],[15.6921,48.85973],[15.61622,48.89541],[15.51357,48.91549],[15.48027,48.94481],[15.34823,48.98444]]]]}},{type:"Feature",properties:{iso1A2:"AU",iso1A3:"AUS",iso1N3:"036",wikidata:"Q408",nameEn:"Australia",groups:["053","009"],driveSide:"left",callingCodes:["61"]},geometry:{type:"MultiPolygon",coordinates:[[[[156.55918,-21.85134],[158.60851,-15.7108],[144.30183,-9.48146],[142.81927,-9.31709],[142.5723,-9.35994],[142.31447,-9.24611],[142.23304,-9.19253],[142.1462,-9.19923],[142.0953,-9.23534],[142.0601,-9.56571],[140.88922,-9.34945],[127.55165,-9.05052],[96.7091,-25.20343],[159.69067,-56.28945],[165.46901,-28.32101],[156.55918,-21.85134]]]]}},{type:"Feature",properties:{iso1A2:"AW",iso1A3:"ABW",iso1N3:"533",wikidata:"Q21203",nameEn:"Aruba",country:"NL",groups:["029","003","419","019"],callingCodes:["297"]},geometry:{type:"MultiPolygon",coordinates:[[[[-70.00823,12.98375],[-70.35625,12.58277],[-69.60231,12.17],[-70.00823,12.98375]]]]}},{type:"Feature",properties:{iso1A2:"AX",iso1A3:"ALA",iso1N3:"248",wikidata:"Q5689",nameEn:"Åland Islands",country:"FI",groups:["EU","154","150"],callingCodes:["358 18","358 457"]},geometry:{type:"MultiPolygon",coordinates:[[[[19.08191,60.19152],[20.5104,59.15546],[21.35468,59.67511],[21.02509,60.12142],[21.08159,60.20167],[21.15143,60.54555],[20.96741,60.71528],[19.23413,60.61414],[19.08191,60.19152]]]]}},{type:"Feature",properties:{iso1A2:"AZ",iso1A3:"AZE",iso1N3:"031",wikidata:"Q227",nameEn:"Azerbaijan",groups:["145","142"],callingCodes:["994"]},geometry:{type:"MultiPolygon",coordinates:[[[[46.42738,41.91323],[46.3984,41.84399],[46.30863,41.79133],[46.23962,41.75811],[46.20538,41.77205],[46.17891,41.72094],[46.19759,41.62327],[46.24429,41.59883],[46.26531,41.63339],[46.28182,41.60089],[46.3253,41.60912],[46.34039,41.5947],[46.34126,41.57454],[46.29794,41.5724],[46.33925,41.4963],[46.40307,41.48464],[46.4669,41.43331],[46.63658,41.37727],[46.72375,41.28609],[46.66148,41.20533],[46.63969,41.09515],[46.55096,41.1104],[46.48558,41.0576],[46.456,41.09984],[46.37661,41.10805],[46.27698,41.19011],[46.13221,41.19479],[45.95786,41.17956],[45.80842,41.2229],[45.69946,41.29545],[45.75705,41.35157],[45.71035,41.36208],[45.68389,41.3539],[45.45973,41.45898],[45.4006,41.42402],[45.31352,41.47168],[45.26285,41.46433],[45.1797,41.42231],[45.09867,41.34065],[45.0133,41.29747],[45.05497,41.2464],[45.02932,41.2101],[45.05201,41.19211],[45.11811,41.19967],[45.1969,41.168],[45.19942,41.13299],[45.08028,41.10917],[45.06784,41.05379],[45.12923,41.06059],[45.1313,41.09369],[45.1634,41.08082],[45.16493,41.05068],[45.1994,41.04518],[45.25897,41.0027],[45.26162,41.0228],[45.28859,41.03757],[45.35677,40.99784],[45.39725,41.02603],[45.44083,41.01663],[45.40814,40.97904],[45.60584,40.87436],[45.55914,40.78366],[45.4206,40.7424],[45.35366,40.65979],[45.45484,40.57707],[45.42994,40.53804],[45.65098,40.37696],[45.95609,40.27846],[45.97944,40.181],[45.83779,39.98925],[45.78642,40.03218],[45.59806,40.0131],[45.60895,39.97733],[45.7833,39.9475],[45.82533,39.82925],[45.96543,39.78859],[46.18493,39.60533],[46.40286,39.63651],[46.42465,39.57534],[46.52117,39.58734],[46.57098,39.56694],[46.57721,39.54414],[46.51027,39.52373],[46.53051,39.47809],[46.4013,39.45405],[46.37795,39.42039],[46.43244,39.35181],[46.50093,39.33736],[46.56476,39.24942],[46.63481,39.23013],[46.58032,39.21204],[46.54141,39.15895],[46.52584,39.18912],[46.44022,39.19636],[46.54296,39.07078],[46.51805,38.94982],[46.53497,38.86548],[46.75752,39.03231],[46.83822,39.13143],[46.92539,39.16644],[46.95341,39.13505],[47.05771,39.20143],[47.05927,39.24846],[47.31301,39.37492],[47.38978,39.45999],[47.50099,39.49615],[47.84774,39.66285],[47.98977,39.70999],[48.34264,39.42935],[48.37385,39.37584],[48.15984,39.30028],[48.12404,39.25208],[48.15361,39.19419],[48.31239,39.09278],[48.33884,39.03022],[48.28437,38.97186],[48.08627,38.94434],[48.07734,38.91616],[48.01409,38.90333],[48.02581,38.82705],[48.24773,38.71883],[48.3146,38.59958],[48.45084,38.61013],[48.58793,38.45076],[48.62217,38.40198],[48.70001,38.40564],[48.78979,38.45026],[48.81072,38.44853],[48.84969,38.45015],[48.88288,38.43975],[52.39847,39.43556],[48.80971,41.95365],[48.5867,41.84306],[48.55078,41.77917],[48.42301,41.65444],[48.40277,41.60441],[48.2878,41.56221],[48.22064,41.51472],[48.07587,41.49957],[47.87973,41.21798],[47.75831,41.19455],[47.62288,41.22969],[47.54504,41.20275],[47.49004,41.26366],[47.34579,41.27884],[47.10762,41.59044],[47.03757,41.55434],[46.99554,41.59743],[47.00955,41.63583],[46.8134,41.76252],[46.75269,41.8623],[46.58924,41.80547],[46.5332,41.87389],[46.42738,41.91323]],[[45.50279,40.58424],[45.47927,40.65023],[45.51825,40.67382],[45.56071,40.64765],[45.50279,40.58424]]],[[[45.00864,41.03411],[45.03792,41.03938],[45.04517,41.06653],[45.03406,41.07931],[45.00864,41.09407],[44.97169,41.09176],[44.95383,41.07553],[44.96031,41.06345],[44.9903,41.05657],[45.00864,41.03411]]],[[[45.21324,40.9817],[45.23095,40.97828],[45.23487,41.00226],[45.20625,41.01484],[45.18382,41.0066],[45.19312,40.98998],[45.20518,40.99348],[45.21219,40.99001],[45.21324,40.9817]]],[[[45.46992,39.49888],[45.29606,39.57654],[45.30385,39.61373],[45.23535,39.61373],[45.21784,39.58074],[45.17464,39.58614],[45.18554,39.67846],[45.06604,39.79277],[44.92869,39.72157],[44.88354,39.74432],[44.75779,39.7148],[44.80977,39.65768],[44.81043,39.62677],[44.88916,39.59653],[44.96746,39.42998],[45.05932,39.36435],[45.08751,39.35052],[45.16168,39.21952],[45.30489,39.18333],[45.40148,39.09007],[45.40452,39.07224],[45.44811,39.04927],[45.44966,38.99243],[45.6131,38.964],[45.6155,38.94304],[45.65172,38.95199],[45.83883,38.90768],[45.90266,38.87739],[45.94624,38.89072],[46.00228,38.87376],[46.06766,38.87861],[46.14785,38.84206],[46.06973,39.0744],[46.02303,39.09978],[45.99774,39.28931],[45.79225,39.3695],[45.83,39.46487],[45.80804,39.56716],[45.70547,39.60174],[45.46992,39.49888]]]]}},{type:"Feature",properties:{iso1A2:"BA",iso1A3:"BIH",iso1N3:"070",wikidata:"Q225",nameEn:"Bosnia and Herzegovina",groups:["039","150"],callingCodes:["387"]},geometry:{type:"MultiPolygon",coordinates:[[[[17.84826,45.04489],[17.66571,45.13408],[17.59104,45.10816],[17.51469,45.10791],[17.47589,45.12656],[17.45615,45.12523],[17.4498,45.16119],[17.41229,45.13335],[17.33573,45.14521],[17.32092,45.16246],[17.26815,45.18444],[17.25131,45.14957],[17.24325,45.146],[17.18438,45.14764],[17.0415,45.20759],[16.9385,45.22742],[16.92405,45.27607],[16.83804,45.18951],[16.81137,45.18434],[16.78219,45.19002],[16.74845,45.20393],[16.64962,45.20714],[16.60194,45.23042],[16.56559,45.22307],[16.5501,45.2212],[16.52982,45.22713],[16.49155,45.21153],[16.4634,45.14522],[16.40023,45.1147],[16.38309,45.05955],[16.38219,45.05139],[16.3749,45.05206],[16.35863,45.03529],[16.35404,45.00241],[16.29036,44.99732],[16.12153,45.09616],[15.98412,45.23088],[15.83512,45.22459],[15.76371,45.16508],[15.78842,45.11519],[15.74585,45.0638],[15.78568,44.97401],[15.74723,44.96818],[15.76096,44.87045],[15.79472,44.8455],[15.72584,44.82334],[15.8255,44.71501],[15.89348,44.74964],[16.05828,44.61538],[16.00884,44.58605],[16.03012,44.55572],[16.10566,44.52586],[16.16814,44.40679],[16.12969,44.38275],[16.21346,44.35231],[16.18688,44.27012],[16.36864,44.08263],[16.43662,44.07523],[16.43629,44.02826],[16.50528,44.0244],[16.55472,43.95326],[16.70922,43.84887],[16.75316,43.77157],[16.80736,43.76011],[17.00585,43.58037],[17.15828,43.49376],[17.24411,43.49376],[17.29699,43.44542],[17.25579,43.40353],[17.286,43.33065],[17.46986,43.16559],[17.64268,43.08595],[17.70879,42.97223],[17.5392,42.92787],[17.6444,42.88641],[17.68151,42.92725],[17.7948,42.89556],[17.80854,42.9182],[17.88201,42.83668],[18.24318,42.6112],[18.36197,42.61423],[18.43735,42.55921],[18.49778,42.58409],[18.53751,42.57376],[18.55504,42.58409],[18.52232,42.62279],[18.57373,42.64429],[18.54841,42.68328],[18.54603,42.69171],[18.55221,42.69045],[18.56789,42.72074],[18.47324,42.74992],[18.45921,42.81682],[18.47633,42.85829],[18.4935,42.86433],[18.49661,42.89306],[18.49076,42.95553],[18.52232,43.01451],[18.66254,43.03928],[18.64735,43.14766],[18.66605,43.2056],[18.71747,43.2286],[18.6976,43.25243],[18.76538,43.29838],[18.85342,43.32426],[18.84794,43.33735],[18.83912,43.34795],[18.90911,43.36383],[18.95819,43.32899],[18.95001,43.29327],[19.00844,43.24988],[19.04233,43.30008],[19.08206,43.29668],[19.08673,43.31453],[19.04071,43.397],[19.01078,43.43854],[18.96053,43.45042],[18.95469,43.49367],[18.91379,43.50299],[19.01078,43.55806],[19.04934,43.50384],[19.13933,43.5282],[19.15685,43.53943],[19.22807,43.5264],[19.24774,43.53061],[19.2553,43.5938],[19.33426,43.58833],[19.36653,43.60921],[19.41941,43.54056],[19.42696,43.57987],[19.50455,43.58385],[19.5176,43.71403],[19.3986,43.79668],[19.23465,43.98764],[19.24363,44.01502],[19.38439,43.96611],[19.52515,43.95573],[19.56498,43.99922],[19.61836,44.01464],[19.61991,44.05254],[19.57467,44.04716],[19.55999,44.06894],[19.51167,44.08158],[19.47321,44.1193],[19.48386,44.14332],[19.47338,44.15034],[19.43905,44.13088],[19.40927,44.16722],[19.3588,44.18353],[19.34773,44.23244],[19.32464,44.27185],[19.26945,44.26957],[19.23306,44.26097],[19.20508,44.2917],[19.18328,44.28383],[19.16741,44.28648],[19.13332,44.31492],[19.13556,44.338],[19.11547,44.34218],[19.1083,44.3558],[19.11865,44.36712],[19.10298,44.36924],[19.10365,44.37795],[19.10704,44.38249],[19.10749,44.39421],[19.11785,44.40313],[19.14681,44.41463],[19.14837,44.45253],[19.12278,44.50132],[19.13369,44.52521],[19.16699,44.52197],[19.26388,44.65412],[19.32543,44.74058],[19.36722,44.88164],[19.18183,44.92055],[19.01994,44.85493],[18.8704,44.85097],[18.76347,44.90669],[18.76369,44.93707],[18.80661,44.93561],[18.78357,44.97741],[18.65723,45.07544],[18.47939,45.05871],[18.41896,45.11083],[18.32077,45.1021],[18.24387,45.13699],[18.1624,45.07654],[18.03121,45.12632],[18.01594,45.15163],[17.99479,45.14958],[17.97834,45.13831],[17.97336,45.12245],[17.93706,45.08016],[17.87148,45.04645],[17.84826,45.04489]]]]}},{type:"Feature",properties:{iso1A2:"BB",iso1A3:"BRB",iso1N3:"052",wikidata:"Q244",nameEn:"Barbados",groups:["029","003","419","019"],driveSide:"left",callingCodes:["1 246"]},geometry:{type:"MultiPolygon",coordinates:[[[[-58.56442,13.24471],[-59.80731,13.87556],[-60.19227,12.37597],[-58.56442,13.24471]]]]}},{type:"Feature",properties:{iso1A2:"BD",iso1A3:"BGD",iso1N3:"050",wikidata:"Q902",nameEn:"Bangladesh",groups:["034","142"],driveSide:"left",callingCodes:["880"]},geometry:{type:"MultiPolygon",coordinates:[[[[89.15869,26.13708],[89.08899,26.38845],[88.95612,26.4564],[88.92357,26.40711],[88.91321,26.37984],[89.05328,26.2469],[88.85004,26.23211],[88.78961,26.31093],[88.67837,26.26291],[88.69485,26.38353],[88.62144,26.46783],[88.4298,26.54489],[88.41196,26.63837],[88.33093,26.48929],[88.35153,26.45241],[88.36938,26.48683],[88.48749,26.45855],[88.51649,26.35923],[88.35153,26.29123],[88.34757,26.22216],[88.1844,26.14417],[88.16581,26.0238],[88.08804,25.91334],[88.13138,25.78773],[88.242,25.80811],[88.45103,25.66245],[88.4559,25.59227],[88.677,25.46959],[88.81296,25.51546],[88.85278,25.34679],[89.01105,25.30303],[89.00463,25.26583],[88.94067,25.18534],[88.44766,25.20149],[88.46277,25.07468],[88.33917,24.86803],[88.27325,24.88796],[88.21832,24.96642],[88.14004,24.93529],[88.15515,24.85806],[88.00683,24.66477],[88.08786,24.63232],[88.12296,24.51301],[88.50934,24.32474],[88.68801,24.31464],[88.74841,24.1959],[88.6976,24.14703],[88.73743,23.91751],[88.66189,23.87607],[88.58087,23.87105],[88.56507,23.64044],[88.74841,23.47361],[88.79351,23.50535],[88.79254,23.46028],[88.71133,23.2492],[88.99148,23.21134],[88.86377,23.08759],[88.88327,23.03885],[88.87063,22.95235],[88.96713,22.83346],[88.9151,22.75228],[88.94614,22.66941],[88.9367,22.58527],[89.07114,22.15335],[89.03553,21.77397],[89.13927,21.60785],[89.13606,21.42955],[92.39837,20.38919],[92.4302,20.5688],[92.31348,20.57137],[92.28464,20.63179],[92.37665,20.72172],[92.26071,21.05697],[92.17752,21.17445],[92.20087,21.337],[92.37939,21.47764],[92.43158,21.37025],[92.55105,21.3856],[92.60187,21.24615],[92.68152,21.28454],[92.59775,21.6092],[92.62187,21.87037],[92.60949,21.97638],[92.56616,22.13554],[92.60029,22.1522],[92.5181,22.71441],[92.37665,22.9435],[92.38214,23.28705],[92.26541,23.70392],[92.15417,23.73409],[92.04706,23.64229],[91.95093,23.73284],[91.95642,23.47361],[91.84789,23.42235],[91.76417,23.26619],[91.81634,23.08001],[91.7324,23.00043],[91.61571,22.93929],[91.54993,23.01051],[91.46615,23.2328],[91.4035,23.27522],[91.40848,23.07117],[91.36453,23.06612],[91.28293,23.37538],[91.15579,23.6599],[91.25192,23.83463],[91.22308,23.89616],[91.29587,24.0041],[91.35741,23.99072],[91.37414,24.10693],[91.55542,24.08687],[91.63782,24.1132],[91.65292,24.22095],[91.73257,24.14703],[91.76004,24.23848],[91.82596,24.22345],[91.89258,24.14674],[91.96603,24.3799],[92.11662,24.38997],[92.15796,24.54435],[92.25854,24.9191],[92.38626,24.86055],[92.49887,24.88796],[92.39147,25.01471],[92.33957,25.07593],[92.0316,25.1834],[91.63648,25.12846],[91.25517,25.20677],[90.87427,25.15799],[90.65042,25.17788],[90.40034,25.1534],[90.1155,25.22686],[89.90478,25.31038],[89.87629,25.28337],[89.83371,25.29548],[89.84086,25.31854],[89.81208,25.37244],[89.86129,25.61714],[89.84388,25.70042],[89.80585,25.82489],[89.86592,25.93115],[89.77728,26.04254],[89.77865,26.08387],[89.73581,26.15818],[89.70201,26.15138],[89.63968,26.22595],[89.57101,25.9682],[89.53515,26.00382],[89.35953,26.0077],[89.15869,26.13708]]]]}},{type:"Feature",properties:{iso1A2:"BE",iso1A3:"BEL",iso1N3:"056",wikidata:"Q31",nameEn:"Belgium",groups:["EU","155","150"],callingCodes:["32"]},geometry:{type:"MultiPolygon",coordinates:[[[[4.93295,51.44945],[4.93909,51.44632],[4.9524,51.45014],[4.95244,51.45207],[4.93295,51.44945]]],[[[4.91493,51.4353],[4.92652,51.43329],[4.92952,51.42984],[4.93986,51.43064],[4.94265,51.44003],[4.93471,51.43861],[4.93416,51.44185],[4.94025,51.44193],[4.93544,51.44634],[4.92879,51.44161],[4.92815,51.43856],[4.92566,51.44273],[4.92811,51.4437],[4.92287,51.44741],[4.91811,51.44621],[4.92227,51.44252],[4.91935,51.43634],[4.91493,51.4353]]],[[[4.82946,51.4213],[4.82409,51.44736],[4.84139,51.4799],[4.78803,51.50284],[4.77321,51.50529],[4.74578,51.48937],[4.72935,51.48424],[4.65442,51.42352],[4.57489,51.4324],[4.53521,51.4243],[4.52846,51.45002],[4.54675,51.47265],[4.5388,51.48184],[4.47736,51.4778],[4.38122,51.44905],[4.39747,51.43316],[4.38064,51.41965],[4.43777,51.36989],[4.39292,51.35547],[4.34086,51.35738],[4.33265,51.37687],[4.21923,51.37443],[4.24024,51.35371],[4.16721,51.29348],[4.05165,51.24171],[4.01957,51.24504],[3.97889,51.22537],[3.90125,51.20371],[3.78783,51.2151],[3.78999,51.25766],[3.58939,51.30064],[3.51502,51.28697],[3.52698,51.2458],[3.43488,51.24135],[3.41704,51.25933],[3.38289,51.27331],[3.35847,51.31572],[3.38696,51.33436],[3.36263,51.37112],[2.56575,51.85301],[2.18458,51.52087],[2.55904,51.07014],[2.57551,51.00326],[2.63074,50.94746],[2.59093,50.91751],[2.63331,50.81457],[2.71165,50.81295],[2.81056,50.71773],[2.8483,50.72276],[2.86985,50.7033],[2.87937,50.70298],[2.88504,50.70656],[2.90069,50.69263],[2.91036,50.6939],[2.90873,50.702],[2.95019,50.75138],[2.96778,50.75242],[3.00537,50.76588],[3.04314,50.77674],[3.09163,50.77717],[3.10614,50.78303],[3.11206,50.79416],[3.11987,50.79188],[3.1257,50.78603],[3.15017,50.79031],[3.16476,50.76843],[3.18339,50.74981],[3.18811,50.74025],[3.20064,50.73547],[3.19017,50.72569],[3.20845,50.71662],[3.22042,50.71019],[3.24593,50.71389],[3.26063,50.70086],[3.26141,50.69151],[3.2536,50.68977],[3.264,50.67668],[3.23951,50.6585],[3.2729,50.60718],[3.28575,50.52724],[3.37693,50.49538],[3.44629,50.51009],[3.47385,50.53397],[3.51564,50.5256],[3.49509,50.48885],[3.5683,50.50192],[3.58361,50.49049],[3.61014,50.49568],[3.64426,50.46275],[3.66153,50.45165],[3.67494,50.40239],[3.67262,50.38663],[3.65709,50.36873],[3.66976,50.34563],[3.71009,50.30305],[3.70987,50.3191],[3.73911,50.34809],[3.84314,50.35219],[3.90781,50.32814],[3.96771,50.34989],[4.0268,50.35793],[4.0689,50.3254],[4.10237,50.31247],[4.10957,50.30234],[4.11954,50.30425],[4.13665,50.25609],[4.16808,50.25786],[4.15524,50.2833],[4.17347,50.28838],[4.17861,50.27443],[4.20651,50.27333],[4.21945,50.25539],[4.15524,50.21103],[4.16014,50.19239],[4.13561,50.13078],[4.20147,50.13535],[4.23101,50.06945],[4.16294,50.04719],[4.13508,50.01976],[4.14239,49.98034],[4.20532,49.95803],[4.31963,49.97043],[4.35051,49.95315],[4.43488,49.94122],[4.51098,49.94659],[4.5414,49.96911],[4.68695,49.99685],[4.70064,50.09384],[4.75237,50.11314],[4.82438,50.16878],[4.83279,50.15331],[4.88602,50.15182],[4.8382,50.06738],[4.78827,49.95609],[4.88529,49.9236],[4.85134,49.86457],[4.86965,49.82271],[4.85464,49.78995],[4.96714,49.79872],[5.09249,49.76193],[5.14545,49.70287],[5.26232,49.69456],[5.31465,49.66846],[5.33039,49.6555],[5.30214,49.63055],[5.3137,49.61225],[5.33851,49.61599],[5.34837,49.62889],[5.3974,49.61596],[5.43713,49.5707],[5.46734,49.52648],[5.46541,49.49825],[5.55001,49.52729],[5.60909,49.51228],[5.64505,49.55146],[5.75649,49.54321],[5.7577,49.55915],[5.77435,49.56298],[5.79195,49.55228],[5.81838,49.54777],[5.84143,49.5533],[5.84692,49.55663],[5.8424,49.56082],[5.87256,49.57539],[5.86986,49.58756],[5.84971,49.58674],[5.84826,49.5969],[5.8762,49.60898],[5.87609,49.62047],[5.88393,49.62802],[5.88552,49.63507],[5.90599,49.63853],[5.90164,49.6511],[5.9069,49.66377],[5.86175,49.67862],[5.86527,49.69291],[5.88677,49.70951],[5.86503,49.72739],[5.84193,49.72161],[5.82562,49.72395],[5.83149,49.74729],[5.82245,49.75048],[5.78871,49.7962],[5.75409,49.79239],[5.74953,49.81428],[5.74364,49.82058],[5.74844,49.82435],[5.7404,49.83452],[5.74076,49.83823],[5.74975,49.83933],[5.74953,49.84709],[5.75884,49.84811],[5.74567,49.85368],[5.75861,49.85631],[5.75269,49.8711],[5.78415,49.87922],[5.73621,49.89796],[5.77314,49.93646],[5.77291,49.96056],[5.80833,49.96451],[5.81163,49.97142],[5.83467,49.97823],[5.83968,49.9892],[5.82331,49.99662],[5.81866,50.01286],[5.8551,50.02683],[5.86904,50.04614],[5.85474,50.06342],[5.8857,50.07824],[5.89488,50.11476],[5.95929,50.13295],[5.96453,50.17259],[6.02488,50.18283],[6.03093,50.16362],[6.06406,50.15344],[6.08577,50.17246],[6.12028,50.16374],[6.1137,50.13668],[6.1379,50.12964],[6.15298,50.14126],[6.14132,50.14971],[6.14588,50.17106],[6.18739,50.1822],[6.18364,50.20815],[6.16853,50.2234],[6.208,50.25179],[6.28797,50.27458],[6.29949,50.30887],[6.32488,50.32333],[6.35701,50.31139],[6.40641,50.32425],[6.40785,50.33557],[6.3688,50.35898],[6.34406,50.37994],[6.36852,50.40776],[6.37219,50.45397],[6.34005,50.46083],[6.3465,50.48833],[6.30809,50.50058],[6.26637,50.50272],[6.22335,50.49578],[6.20599,50.52089],[6.19193,50.5212],[6.18716,50.52653],[6.19579,50.5313],[6.19735,50.53576],[6.17802,50.54179],[6.17739,50.55875],[6.20281,50.56952],[6.22581,50.5907],[6.24005,50.58732],[6.24888,50.59869],[6.2476,50.60392],[6.26957,50.62444],[6.17852,50.6245],[6.11707,50.72231],[6.04428,50.72861],[6.0406,50.71848],[6.0326,50.72647],[6.03889,50.74618],[6.01976,50.75398],[5.97545,50.75441],[5.95942,50.7622],[5.89132,50.75124],[5.89129,50.75125],[5.88734,50.77092],[5.84888,50.75448],[5.84548,50.76542],[5.80673,50.7558],[5.77513,50.78308],[5.76533,50.78159],[5.74356,50.7691],[5.73904,50.75674],[5.72216,50.76398],[5.69469,50.75529],[5.68091,50.75804],[5.70107,50.7827],[5.68995,50.79641],[5.70118,50.80764],[5.65259,50.82309],[5.64009,50.84742],[5.64504,50.87107],[5.67886,50.88142],[5.69858,50.91046],[5.71626,50.90796],[5.72644,50.91167],[5.72545,50.92312],[5.74644,50.94723],[5.75927,50.95601],[5.74752,50.96202],[5.72875,50.95428],[5.71864,50.96092],[5.76242,50.99703],[5.77688,51.02483],[5.75961,51.03113],[5.77258,51.06196],[5.79835,51.05834],[5.79903,51.09371],[5.82921,51.09328],[5.83226,51.10585],[5.8109,51.10861],[5.80798,51.11661],[5.85508,51.14445],[5.82564,51.16753],[5.77697,51.1522],[5.77735,51.17845],[5.74617,51.18928],[5.70344,51.1829],[5.65528,51.18736],[5.65145,51.19788],[5.5603,51.22249],[5.5569,51.26544],[5.515,51.29462],[5.48476,51.30053],[5.46519,51.2849],[5.4407,51.28169],[5.41672,51.26248],[5.347,51.27502],[5.33886,51.26314],[5.29716,51.26104],[5.26461,51.26693],[5.23814,51.26064],[5.22542,51.26888],[5.24244,51.30495],[5.2002,51.32243],[5.16222,51.31035],[5.13377,51.31592],[5.13105,51.34791],[5.07102,51.39469],[5.10456,51.43163],[5.07891,51.4715],[5.04774,51.47022],[5.03281,51.48679],[5.0106,51.47167],[5.00393,51.44406],[4.92152,51.39487],[4.90016,51.41404],[4.84988,51.41502],[4.78941,51.41102],[4.77229,51.41337],[4.76577,51.43046],[4.78314,51.43319],[4.82946,51.4213]]]]}},{type:"Feature",properties:{iso1A2:"BF",iso1A3:"BFA",iso1N3:"854",wikidata:"Q965",nameEn:"Burkina Faso",groups:["011","202","002"],callingCodes:["226"]},geometry:{type:"MultiPolygon",coordinates:[[[[0.23859,15.00135],[0.06588,14.96961],[-0.24673,15.07805],[-0.72004,15.08655],[-1.05875,14.7921],[-1.32166,14.72774],[-1.68083,14.50023],[-1.97945,14.47709],[-1.9992,14.19011],[-2.10223,14.14878],[-2.47587,14.29671],[-2.66175,14.14713],[-2.84667,14.05532],[-2.90831,13.81174],[-2.88189,13.64921],[-3.26407,13.70699],[-3.28396,13.5422],[-3.23599,13.29035],[-3.43507,13.27272],[-3.4313,13.1588],[-3.54454,13.1781],[-3.7911,13.36665],[-3.96282,13.38164],[-3.90558,13.44375],[-3.96501,13.49778],[-4.34477,13.12927],[-4.21819,12.95722],[-4.238,12.71467],[-4.47356,12.71252],[-4.41412,12.31922],[-4.57703,12.19875],[-4.54841,12.1385],[-4.62546,12.13204],[-4.62987,12.06531],[-4.70692,12.06746],[-4.72893,12.01579],[-5.07897,11.97918],[-5.26389,11.84778],[-5.40258,11.8327],[-5.26389,11.75728],[-5.29251,11.61715],[-5.22867,11.60421],[-5.20665,11.43811],[-5.25509,11.36905],[-5.25949,11.24816],[-5.32553,11.21578],[-5.32994,11.13371],[-5.49284,11.07538],[-5.41579,10.84628],[-5.47083,10.75329],[-5.46643,10.56074],[-5.51058,10.43177],[-5.39602,10.2929],[-5.12465,10.29788],[-4.96453,9.99923],[-4.96621,9.89132],[-4.6426,9.70696],[-4.31392,9.60062],[-4.25999,9.76012],[-3.69703,9.94279],[-3.31779,9.91125],[-3.27228,9.84981],[-3.19306,9.93781],[-3.16609,9.85147],[-3.00765,9.74019],[-2.93012,9.57403],[-2.76494,9.40778],[-2.68802,9.49343],[-2.76534,9.56589],[-2.74174,9.83172],[-2.83108,10.40252],[-2.94232,10.64281],[-2.83373,11.0067],[-0.67143,10.99811],[-0.61937,10.91305],[-0.44298,11.04292],[-0.42391,11.11661],[-0.38219,11.12596],[-0.35955,11.07801],[-0.28566,11.12713],[-0.27374,11.17157],[-0.13493,11.14075],[0.50388,11.01011],[0.48852,10.98561],[0.50521,10.98035],[0.4958,10.93269],[0.66104,10.99964],[0.91245,10.99597],[0.9813,11.08876],[1.03409,11.04719],[1.42823,11.46822],[2.00988,11.42227],[2.29983,11.68254],[2.39723,11.89473],[2.05785,12.35539],[2.26349,12.41915],[0.99167,13.10727],[0.99253,13.37515],[1.18873,13.31771],[1.21217,13.37853],[1.24516,13.33968],[1.28509,13.35488],[1.24429,13.39373],[1.20088,13.38951],[1.02813,13.46635],[0.99514,13.5668],[0.77637,13.64442],[0.77377,13.6866],[0.61924,13.68491],[0.38051,14.05575],[0.16936,14.51654],[0.23859,15.00135]]]]}},{type:"Feature",properties:{iso1A2:"BG",iso1A3:"BGR",iso1N3:"100",wikidata:"Q219",nameEn:"Bulgaria",groups:["EU","151","150"],callingCodes:["359"]},geometry:{type:"MultiPolygon",coordinates:[[[[23.05288,43.79494],[22.85314,43.84452],[22.83753,43.88055],[22.87873,43.9844],[23.01674,44.01946],[23.04988,44.07694],[22.67173,44.21564],[22.61711,44.16938],[22.61688,44.06534],[22.41449,44.00514],[22.35558,43.81281],[22.41043,43.69566],[22.47582,43.6558],[22.53397,43.47225],[22.82036,43.33665],[22.89727,43.22417],[23.00806,43.19279],[22.98104,43.11199],[22.89521,43.03625],[22.78397,42.98253],[22.74826,42.88701],[22.54302,42.87774],[22.43309,42.82057],[22.4997,42.74144],[22.43983,42.56851],[22.55669,42.50144],[22.51961,42.3991],[22.47498,42.3915],[22.45919,42.33822],[22.34773,42.31725],[22.38136,42.30339],[22.47251,42.20393],[22.50289,42.19527],[22.51224,42.15457],[22.67701,42.06614],[22.86749,42.02275],[22.90254,41.87587],[22.96682,41.77137],[23.01239,41.76527],[23.03342,41.71034],[22.95513,41.63265],[22.96331,41.35782],[22.93334,41.34104],[23.1833,41.31755],[23.21953,41.33773],[23.22771,41.37106],[23.31301,41.40525],[23.33639,41.36317],[23.40416,41.39999],[23.52453,41.40262],[23.63203,41.37632],[23.67644,41.41139],[23.76525,41.40175],[23.80148,41.43943],[23.89613,41.45257],[23.91483,41.47971],[23.96975,41.44118],[24.06908,41.46132],[24.06323,41.53222],[24.10063,41.54796],[24.18126,41.51735],[24.27124,41.57682],[24.30513,41.51297],[24.52599,41.56808],[24.61129,41.42278],[24.71529,41.41928],[24.8041,41.34913],[24.82514,41.4035],[24.86136,41.39298],[24.90928,41.40876],[24.942,41.38685],[25.11611,41.34212],[25.28322,41.23411],[25.48187,41.28506],[25.52394,41.2798],[25.55082,41.31667],[25.61042,41.30614],[25.66183,41.31316],[25.70507,41.29209],[25.8266,41.34563],[25.87919,41.30526],[26.12926,41.35878],[26.16548,41.42278],[26.20288,41.43943],[26.14796,41.47533],[26.176,41.50072],[26.17951,41.55409],[26.14328,41.55496],[26.15146,41.60828],[26.07083,41.64584],[26.06148,41.70345],[26.16841,41.74858],[26.21325,41.73223],[26.22888,41.74139],[26.2654,41.71544],[26.30255,41.70925],[26.35957,41.71149],[26.32952,41.73637],[26.33589,41.76802],[26.36952,41.82265],[26.53968,41.82653],[26.57961,41.90024],[26.56051,41.92995],[26.62996,41.97644],[26.79143,41.97386],[26.95638,42.00741],[27.03277,42.0809],[27.08486,42.08735],[27.19251,42.06028],[27.22376,42.10152],[27.27411,42.10409],[27.45478,41.96591],[27.52379,41.93756],[27.55191,41.90928],[27.69949,41.97515],[27.81235,41.94803],[27.83492,41.99709],[27.91479,41.97902],[28.02971,41.98066],[28.32297,41.98371],[29.24336,43.70874],[28.23293,43.76],[27.99558,43.84193],[27.92008,44.00761],[27.73468,43.95326],[27.64542,44.04958],[27.60834,44.01206],[27.39757,44.0141],[27.26845,44.12602],[26.95141,44.13555],[26.62712,44.05698],[26.38764,44.04356],[26.10115,43.96908],[26.05584,43.90925],[25.94911,43.85745],[25.72792,43.69263],[25.39528,43.61866],[25.17144,43.70261],[25.10718,43.6831],[24.96682,43.72693],[24.73542,43.68523],[24.62281,43.74082],[24.50264,43.76314],[24.35364,43.70211],[24.18149,43.68218],[23.73978,43.80627],[23.61687,43.79289],[23.4507,43.84936],[23.26772,43.84843],[23.05288,43.79494]]]]}},{type:"Feature",properties:{iso1A2:"BH",iso1A3:"BHR",iso1N3:"048",wikidata:"Q398",nameEn:"Bahrain",groups:["145","142"],callingCodes:["973"]},geometry:{type:"MultiPolygon",coordinates:[[[[50.93865,26.30758],[50.71771,26.73086],[50.38162,26.53976],[50.26923,26.08243],[50.302,25.87592],[50.57069,25.57887],[50.80824,25.54641],[50.7801,25.595],[50.86149,25.6965],[50.81266,25.88946],[50.93865,26.30758]]]]}},{type:"Feature",properties:{iso1A2:"BI",iso1A3:"BDI",iso1N3:"108",wikidata:"Q967",nameEn:"Burundi",groups:["014","202","002"],callingCodes:["257"]},geometry:{type:"MultiPolygon",coordinates:[[[[30.54501,-2.41404],[30.42933,-2.31064],[30.14034,-2.43626],[29.95911,-2.33348],[29.88237,-2.75105],[29.36805,-2.82933],[29.32234,-2.6483],[29.0562,-2.58632],[29.04081,-2.7416],[29.00167,-2.78523],[29.00404,-2.81978],[29.0505,-2.81774],[29.09119,-2.87871],[29.09797,-2.91935],[29.16037,-2.95457],[29.17258,-2.99385],[29.25633,-3.05471],[29.21463,-3.3514],[29.23708,-3.75856],[29.43673,-4.44845],[29.63827,-4.44681],[29.75109,-4.45836],[29.77289,-4.41733],[29.82885,-4.36153],[29.88172,-4.35743],[30.03323,-4.26631],[30.22042,-4.01738],[30.45915,-3.56532],[30.84165,-3.25152],[30.83823,-2.97837],[30.6675,-2.98987],[30.57926,-2.89791],[30.4987,-2.9573],[30.40662,-2.86151],[30.52747,-2.65841],[30.41789,-2.66266],[30.54501,-2.41404]]]]}},{type:"Feature",properties:{iso1A2:"BJ",iso1A3:"BEN",iso1N3:"204",wikidata:"Q962",nameEn:"Benin",aliases:["DY"],groups:["011","202","002"],callingCodes:["229"]},geometry:{type:"MultiPolygon",coordinates:[[[[3.59375,11.70269],[3.48187,11.86092],[3.31613,11.88495],[3.25352,12.01467],[2.83978,12.40585],[2.6593,12.30631],[2.37783,12.24804],[2.39657,12.10952],[2.45824,11.98672],[2.39723,11.89473],[2.29983,11.68254],[2.00988,11.42227],[1.42823,11.46822],[1.03409,11.04719],[0.9813,11.08876],[0.91245,10.99597],[0.8804,10.803],[0.80358,10.71459],[0.77666,10.37665],[1.35507,9.99525],[1.36624,9.5951],[1.33675,9.54765],[1.41746,9.3226],[1.5649,9.16941],[1.61838,9.0527],[1.64249,6.99562],[1.55877,6.99737],[1.61812,6.74843],[1.58105,6.68619],[1.76906,6.43189],[1.79826,6.28221],[1.62913,6.24075],[1.67336,6.02702],[2.74181,6.13349],[2.70566,6.38038],[2.70464,6.50831],[2.74334,6.57291],[2.7325,6.64057],[2.78204,6.70514],[2.78823,6.76356],[2.73405,6.78508],[2.74024,6.92802],[2.71702,6.95722],[2.76965,7.13543],[2.74489,7.42565],[2.79442,7.43486],[2.78668,7.5116],[2.73405,7.5423],[2.73095,7.7755],[2.67523,7.87825],[2.77907,9.06924],[3.08017,9.10006],[3.14147,9.28375],[3.13928,9.47167],[3.25093,9.61632],[3.34726,9.70696],[3.32099,9.78032],[3.35383,9.83641],[3.54429,9.87739],[3.66908,10.18136],[3.57275,10.27185],[3.6844,10.46351],[3.78292,10.40538],[3.84243,10.59316],[3.71505,11.13015],[3.49175,11.29765],[3.59375,11.70269]]]]}},{type:"Feature",properties:{iso1A2:"BL",iso1A3:"BLM",iso1N3:"652",wikidata:"Q25362",nameEn:"Saint-Barthélemy",country:"FR",groups:["029","003","419","019"],callingCodes:["590"]},geometry:{type:"MultiPolygon",coordinates:[[[[-62.75637,18.13489],[-62.93924,18.02904],[-63.07669,17.79659],[-62.76692,17.64353],[-62.54836,17.8636],[-62.75637,18.13489]]]]}},{type:"Feature",properties:{iso1A2:"BM",iso1A3:"BMU",iso1N3:"060",wikidata:"Q23635",nameEn:"Bermuda",country:"GB",groups:["021","003","019"],driveSide:"left",callingCodes:["1 441"]},geometry:{type:"MultiPolygon",coordinates:[[[[-63.20987,32.6953],[-65.31453,32.68437],[-65.63955,31.43417],[-63.20987,32.6953]]]]}},{type:"Feature",properties:{iso1A2:"BN",iso1A3:"BRN",iso1N3:"096",wikidata:"Q921",nameEn:"Brunei",groups:["035","142"],driveSide:"left",callingCodes:["673"]},geometry:{type:"MultiPolygon",coordinates:[[[[115.16236,5.01011],[115.02521,5.35005],[114.08532,4.64632],[114.07448,4.58441],[114.15813,4.57],[114.26876,4.49878],[114.32176,4.34942],[114.32176,4.2552],[114.4416,4.27588],[114.49922,4.13108],[114.64211,4.00694],[114.78539,4.12205],[114.88039,4.4257],[114.83189,4.42387],[114.77303,4.72871],[114.8266,4.75062],[114.88841,4.81905],[114.96982,4.81146],[114.99417,4.88201],[115.05038,4.90275],[115.02955,4.82087],[115.02278,4.74137],[115.04064,4.63706],[115.07737,4.53418],[115.09978,4.39123],[115.31275,4.30806],[115.36346,4.33563],[115.2851,4.42295],[115.27819,4.63661],[115.20737,4.8256],[115.15092,4.87604],[115.16236,5.01011]]]]}},{type:"Feature",properties:{iso1A2:"BO",iso1A3:"BOL",iso1N3:"068",wikidata:"Q750",nameEn:"Bolivia",groups:["005","419","019"],callingCodes:["591"]},geometry:{type:"MultiPolygon",coordinates:[[[[-63.90248,-12.52544],[-64.22539,-12.45267],[-64.30708,-12.46398],[-64.99778,-11.98604],[-65.30027,-11.48749],[-65.28141,-10.86289],[-65.35402,-10.78685],[-65.37923,-10.35141],[-65.29019,-9.86253],[-65.40615,-9.63894],[-65.56244,-9.84266],[-65.68343,-9.75323],[-67.17784,-10.34016],[-68.71533,-11.14749],[-68.7651,-11.0496],[-68.75179,-11.03688],[-68.75265,-11.02383],[-68.74802,-11.00891],[-69.42792,-10.93451],[-69.47839,-10.95254],[-69.57156,-10.94555],[-68.98115,-11.8979],[-68.65044,-12.50689],[-68.85615,-12.87769],[-68.8864,-13.40792],[-69.05265,-13.68546],[-68.88135,-14.18639],[-69.36254,-14.94634],[-69.14856,-15.23478],[-69.40336,-15.61358],[-69.20291,-16.16668],[-69.09986,-16.22693],[-68.96238,-16.194],[-68.79464,-16.33272],[-68.98358,-16.42165],[-69.04027,-16.57214],[-69.00853,-16.66769],[-69.16896,-16.72233],[-69.62883,-17.28142],[-69.46863,-17.37466],[-69.46897,-17.4988],[-69.46623,-17.60518],[-69.34126,-17.72753],[-69.28671,-17.94844],[-69.07496,-18.03715],[-69.14807,-18.16893],[-69.07432,-18.28259],[-68.94987,-18.93302],[-68.87082,-19.06003],[-68.80602,-19.08355],[-68.61989,-19.27584],[-68.41218,-19.40499],[-68.66761,-19.72118],[-68.54611,-19.84651],[-68.57132,-20.03134],[-68.74273,-20.08817],[-68.7276,-20.46178],[-68.44023,-20.62701],[-68.55383,-20.7355],[-68.53957,-20.91542],[-68.40403,-20.94562],[-68.18816,-21.28614],[-67.85114,-22.87076],[-67.54284,-22.89771],[-67.18382,-22.81525],[-66.7298,-22.23644],[-66.29714,-22.08741],[-66.24077,-21.77837],[-66.03836,-21.84829],[-66.04832,-21.9187],[-65.9261,-21.93335],[-65.7467,-22.10105],[-65.61166,-22.09504],[-65.58694,-22.09794],[-65.57743,-22.07675],[-65.47435,-22.08908],[-64.99524,-22.08255],[-64.90014,-22.12136],[-64.67174,-22.18957],[-64.58888,-22.25035],[-64.4176,-22.67692],[-64.35108,-22.73282],[-64.31489,-22.88824],[-64.22918,-22.55807],[-63.93287,-21.99934],[-63.70963,-21.99934],[-63.68113,-22.0544],[-63.66482,-21.99918],[-62.81124,-21.9987],[-62.8078,-22.12534],[-62.64455,-22.25091],[-62.2757,-21.06657],[-62.26883,-20.55311],[-61.93912,-20.10053],[-61.73723,-19.63958],[-60.00638,-19.2981],[-59.06965,-19.29148],[-58.23216,-19.80058],[-58.16225,-20.16193],[-57.8496,-19.98346],[-58.14215,-19.76276],[-57.78463,-19.03259],[-57.71113,-19.03161],[-57.69134,-19.00544],[-57.71995,-18.97546],[-57.71995,-18.89573],[-57.76764,-18.90087],[-57.56807,-18.25655],[-57.48237,-18.24219],[-57.69877,-17.8431],[-57.73949,-17.56095],[-57.90082,-17.44555],[-57.99661,-17.5273],[-58.32935,-17.28195],[-58.5058,-16.80958],[-58.30918,-16.3699],[-58.32431,-16.25861],[-58.41506,-16.32636],[-60.16069,-16.26479],[-60.23797,-15.50267],[-60.58224,-15.09887],[-60.23968,-15.09515],[-60.27887,-14.63021],[-60.46037,-14.22496],[-60.48053,-13.77981],[-61.05527,-13.50054],[-61.81151,-13.49564],[-63.76259,-12.42952],[-63.90248,-12.52544]]]]}},{type:"Feature",properties:{iso1A2:"BQ",iso1A3:"BES",iso1N3:"535",wikidata:"Q27561",nameEn:"Caribbean Netherlands",country:"NL",groups:["029","003","419","019"],callingCodes:["599 3","599 4","599 7"]},geometry:{type:"MultiPolygon",coordinates:[[[[-63.07669,17.79659],[-63.22932,17.32592],[-63.11114,17.23125],[-62.76692,17.64353],[-63.07669,17.79659]]],[[[-63.29212,17.90532],[-63.58819,17.61311],[-63.22932,17.32592],[-63.07669,17.79659],[-63.29212,17.90532]]],[[[-67.89186,12.4116],[-68.90012,12.62309],[-68.33524,11.78151],[-68.01417,11.77722],[-67.89186,12.4116]]]]}},{type:"Feature",properties:{iso1A2:"BR",iso1A3:"BRA",iso1N3:"076",wikidata:"Q155",nameEn:"Brazil",groups:["005","419","019"],callingCodes:["55"]},geometry:{type:"MultiPolygon",coordinates:[[[[-59.69361,4.34069],[-59.78878,4.45637],[-60.15953,4.53456],[-60.04189,4.69801],[-59.98129,5.07097],[-60.20944,5.28754],[-60.32352,5.21299],[-60.73204,5.20931],[-60.5802,4.94312],[-60.86539,4.70512],[-60.98303,4.54167],[-61.15703,4.49839],[-61.31457,4.54167],[-61.29675,4.44216],[-61.48569,4.43149],[-61.54629,4.2822],[-62.13094,4.08309],[-62.44822,4.18621],[-62.57656,4.04754],[-62.74411,4.03331],[-62.7655,3.73099],[-62.98296,3.59935],[-63.21111,3.96219],[-63.4464,3.9693],[-63.42233,3.89995],[-63.50611,3.83592],[-63.67099,4.01731],[-63.70218,3.91417],[-63.86082,3.94796],[-63.99183,3.90172],[-64.14512,4.12932],[-64.57648,4.12576],[-64.72977,4.28931],[-64.84028,4.24665],[-64.48379,3.7879],[-64.02908,2.79797],[-64.0257,2.48156],[-63.39114,2.4317],[-63.39827,2.16098],[-64.06135,1.94722],[-64.08274,1.64792],[-64.34654,1.35569],[-64.38932,1.5125],[-65.11657,1.12046],[-65.57288,0.62856],[-65.50158,0.92086],[-65.6727,1.01353],[-66.28507,0.74585],[-66.85795,1.22998],[-67.08222,1.17441],[-67.15784,1.80439],[-67.299,1.87494],[-67.40488,2.22258],[-67.9292,1.82455],[-68.18632,2.00091],[-68.26699,1.83463],[-68.18128,1.72881],[-69.38621,1.70865],[-69.53746,1.76408],[-69.83491,1.69353],[-69.82987,1.07864],[-69.26017,1.06856],[-69.14422,0.84172],[-69.20976,0.57958],[-69.47696,0.71065],[-70.04162,0.55437],[-70.03658,-0.19681],[-69.603,-0.51947],[-69.59796,-0.75136],[-69.4215,-1.01853],[-69.43395,-1.42219],[-69.94708,-4.2431],[-70.00888,-4.37833],[-70.11305,-4.27281],[-70.19582,-4.3607],[-70.33236,-4.15214],[-70.77601,-4.15717],[-70.96814,-4.36915],[-71.87003,-4.51661],[-72.64391,-5.0391],[-72.83973,-5.14765],[-73.24579,-6.05764],[-73.12983,-6.43852],[-73.73986,-6.87919],[-73.77011,-7.28944],[-73.96938,-7.58465],[-73.65485,-7.77897],[-73.76576,-7.89884],[-72.92886,-9.04074],[-73.21498,-9.40904],[-72.72216,-9.41397],[-72.31883,-9.5184],[-72.14742,-9.98049],[-71.23394,-9.9668],[-70.53373,-9.42628],[-70.58453,-9.58303],[-70.55429,-9.76692],[-70.62487,-9.80666],[-70.64134,-11.0108],[-70.51395,-10.92249],[-70.38791,-11.07096],[-69.90896,-10.92744],[-69.57835,-10.94051],[-69.57156,-10.94555],[-69.47839,-10.95254],[-69.42792,-10.93451],[-68.74802,-11.00891],[-68.75265,-11.02383],[-68.75179,-11.03688],[-68.7651,-11.0496],[-68.71533,-11.14749],[-67.17784,-10.34016],[-65.68343,-9.75323],[-65.56244,-9.84266],[-65.40615,-9.63894],[-65.29019,-9.86253],[-65.37923,-10.35141],[-65.35402,-10.78685],[-65.28141,-10.86289],[-65.30027,-11.48749],[-64.99778,-11.98604],[-64.30708,-12.46398],[-64.22539,-12.45267],[-63.90248,-12.52544],[-63.76259,-12.42952],[-61.81151,-13.49564],[-61.05527,-13.50054],[-60.48053,-13.77981],[-60.46037,-14.22496],[-60.27887,-14.63021],[-60.23968,-15.09515],[-60.58224,-15.09887],[-60.23797,-15.50267],[-60.16069,-16.26479],[-58.41506,-16.32636],[-58.32431,-16.25861],[-58.30918,-16.3699],[-58.5058,-16.80958],[-58.32935,-17.28195],[-57.99661,-17.5273],[-57.90082,-17.44555],[-57.73949,-17.56095],[-57.69877,-17.8431],[-57.48237,-18.24219],[-57.56807,-18.25655],[-57.76764,-18.90087],[-57.71995,-18.89573],[-57.71995,-18.97546],[-57.69134,-19.00544],[-57.71113,-19.03161],[-57.78463,-19.03259],[-58.14215,-19.76276],[-57.8496,-19.98346],[-58.16225,-20.16193],[-57.84536,-20.93155],[-57.93492,-21.65505],[-57.88239,-21.6868],[-57.94642,-21.73799],[-57.98625,-22.09157],[-56.6508,-22.28387],[-56.5212,-22.11556],[-56.45893,-22.08072],[-56.23206,-22.25347],[-55.8331,-22.29008],[-55.74941,-22.46436],[-55.741,-22.52018],[-55.72366,-22.5519],[-55.6986,-22.56268],[-55.68742,-22.58407],[-55.62493,-22.62765],[-55.63849,-22.95122],[-55.5446,-23.22811],[-55.52288,-23.2595],[-55.5555,-23.28237],[-55.43585,-23.87157],[-55.44117,-23.9185],[-55.41784,-23.9657],[-55.12292,-23.99669],[-55.0518,-23.98666],[-55.02691,-23.97317],[-54.6238,-23.83078],[-54.32807,-24.01865],[-54.28207,-24.07305],[-54.4423,-25.13381],[-54.62033,-25.46026],[-54.60196,-25.48397],[-54.59509,-25.53696],[-54.59398,-25.59224],[-54.5502,-25.58915],[-54.52926,-25.62846],[-53.90831,-25.55513],[-53.83691,-25.94849],[-53.73511,-26.04211],[-53.73086,-26.05842],[-53.7264,-26.0664],[-53.73391,-26.07006],[-53.73968,-26.10012],[-53.65018,-26.19501],[-53.65237,-26.23289],[-53.63739,-26.2496],[-53.63881,-26.25075],[-53.64632,-26.24798],[-53.64186,-26.25976],[-53.64505,-26.28089],[-53.68269,-26.33359],[-53.73372,-26.6131],[-53.80144,-27.09844],[-54.15978,-27.2889],[-54.19062,-27.27639],[-54.19268,-27.30751],[-54.41888,-27.40882],[-54.50416,-27.48232],[-54.67657,-27.57214],[-54.90159,-27.63132],[-54.90805,-27.73149],[-55.1349,-27.89759],[-55.16872,-27.86224],[-55.33303,-27.94661],[-55.6262,-28.17124],[-55.65418,-28.18304],[-56.01729,-28.51223],[-56.00458,-28.60421],[-56.05265,-28.62651],[-56.54171,-29.11447],[-56.57295,-29.11357],[-56.62789,-29.18073],[-56.81251,-29.48154],[-57.09386,-29.74211],[-57.65132,-30.19229],[-57.22502,-30.26121],[-56.90236,-30.02578],[-56.49267,-30.39471],[-56.4795,-30.3899],[-56.4619,-30.38457],[-55.87388,-31.05053],[-55.58866,-30.84117],[-55.5634,-30.8686],[-55.55373,-30.8732],[-55.55218,-30.88193],[-55.54572,-30.89051],[-55.53431,-30.89714],[-55.53276,-30.90218],[-55.52712,-30.89997],[-55.51862,-30.89828],[-55.50841,-30.9027],[-55.50821,-30.91349],[-54.17384,-31.86168],[-53.76024,-32.0751],[-53.39572,-32.58596],[-53.37671,-32.57005],[-53.1111,-32.71147],[-53.53459,-33.16843],[-53.52794,-33.68908],[-53.44031,-33.69344],[-53.39593,-33.75169],[-53.37138,-33.74313],[-52.83257,-34.01481],[-28.34015,-20.99094],[-28.99601,1.86593],[-51.35485,4.8383],[-51.63798,4.51394],[-51.61983,4.14596],[-51.79599,3.89336],[-51.82312,3.85825],[-51.85573,3.83427],[-52.31787,3.17896],[-52.6906,2.37298],[-52.96539,2.1881],[-53.78743,2.34412],[-54.16286,2.10779],[-54.6084,2.32856],[-55.01919,2.564],[-55.71493,2.40342],[-55.96292,2.53188],[-56.13054,2.27723],[-55.92159,2.05236],[-55.89863,1.89861],[-55.99278,1.83137],[-56.47045,1.95135],[-56.7659,1.89509],[-57.07092,1.95304],[-57.09109,2.01854],[-57.23981,1.95808],[-57.35073,1.98327],[-57.55743,1.69605],[-57.77281,1.73344],[-57.97336,1.64566],[-58.01873,1.51966],[-58.33887,1.58014],[-58.4858,1.48399],[-58.53571,1.29154],[-58.84229,1.17749],[-58.92072,1.31293],[-59.25583,1.40559],[-59.74066,1.87596],[-59.7264,2.27497],[-59.91177,2.36759],[-59.99733,2.92312],[-59.79769,3.37162],[-59.86899,3.57089],[-59.51963,3.91951],[-59.73353,4.20399],[-59.69361,4.34069]]]]}},{type:"Feature",properties:{iso1A2:"BS",iso1A3:"BHS",iso1N3:"044",wikidata:"Q778",nameEn:"The Bahamas",groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 242"]},geometry:{type:"MultiPolygon",coordinates:[[[[-72.98446,20.4801],[-71.70065,25.7637],[-79.14818,27.83105],[-79.89631,24.6597],[-80.88924,23.80416],[-72.98446,20.4801]]]]}},{type:"Feature",properties:{iso1A2:"BT",iso1A3:"BTN",iso1N3:"064",wikidata:"Q917",nameEn:"Bhutan",groups:["034","142"],driveSide:"left",callingCodes:["975"]},geometry:{type:"MultiPolygon",coordinates:[[[[91.6469,27.76358],[91.5629,27.84823],[91.48973,27.93903],[91.46327,28.0064],[91.25779,28.07509],[91.20019,27.98715],[90.69894,28.07784],[90.58842,28.02838],[90.13387,28.19178],[89.79762,28.23979],[89.59525,28.16433],[89.12825,27.62502],[89.0582,27.60985],[88.97213,27.51671],[88.95355,27.4106],[89.00216,27.32532],[88.96947,27.30319],[88.93678,27.33777],[88.91901,27.32483],[88.74219,27.144],[88.86984,27.10937],[88.8714,26.97488],[88.92301,26.99286],[88.95807,26.92668],[89.09554,26.89089],[89.12825,26.81661],[89.1926,26.81329],[89.37913,26.86224],[89.38319,26.85963],[89.3901,26.84225],[89.42349,26.83727],[89.63369,26.74402],[89.86124,26.73307],[90.04535,26.72422],[90.30402,26.85098],[90.39271,26.90704],[90.48504,26.8594],[90.67715,26.77215],[91.50067,26.79223],[91.83181,26.87318],[92.05523,26.8692],[92.11863,26.893],[92.03457,27.07334],[92.04702,27.26861],[92.12019,27.27829],[92.01132,27.47352],[91.65007,27.48287],[91.55819,27.6144],[91.6469,27.76358]]]]}},{type:"Feature",properties:{iso1A2:"BV",iso1A3:"BVT",iso1N3:"074",wikidata:"Q23408",nameEn:"Bouvet Island",country:"NO",groups:["005","419","019"]},geometry:{type:"MultiPolygon",coordinates:[[[[4.54042,-54.0949],[2.28941,-54.13089],[3.35353,-55.17558],[4.54042,-54.0949]]]]}},{type:"Feature",properties:{iso1A2:"BW",iso1A3:"BWA",iso1N3:"072",wikidata:"Q963",nameEn:"Botswana",groups:["018","202","002"],driveSide:"left",callingCodes:["267"]},geometry:{type:"MultiPolygon",coordinates:[[[[25.26433,-17.79571],[25.16882,-17.78253],[25.05895,-17.84452],[24.95586,-17.79674],[24.73364,-17.89338],[24.71887,-17.9218],[24.6303,-17.9863],[24.57485,-18.07151],[24.40577,-17.95726],[24.19416,-18.01919],[23.61088,-18.4881],[23.29618,-17.99855],[23.0996,-18.00075],[21.45556,-18.31795],[20.99904,-18.31743],[20.99751,-22.00026],[19.99912,-21.99991],[19.99817,-24.76768],[20.02809,-24.78725],[20.03678,-24.81004],[20.29826,-24.94869],[20.64795,-25.47827],[20.86081,-26.14892],[20.61754,-26.4692],[20.63275,-26.78181],[20.68596,-26.9039],[20.87031,-26.80047],[21.13353,-26.86661],[21.37869,-26.82083],[21.69322,-26.86152],[21.7854,-26.79199],[21.77114,-26.69015],[21.83291,-26.65959],[21.90703,-26.66808],[22.06192,-26.61882],[22.21206,-26.3773],[22.41921,-26.23078],[22.56365,-26.19668],[22.70808,-25.99186],[22.86012,-25.50572],[23.03497,-25.29971],[23.47588,-25.29971],[23.9244,-25.64286],[24.18287,-25.62916],[24.36531,-25.773],[24.44703,-25.73021],[24.67319,-25.81749],[24.8946,-25.80723],[25.01718,-25.72507],[25.12266,-25.75931],[25.33076,-25.76616],[25.58543,-25.6343],[25.6643,-25.4491],[25.69661,-25.29284],[25.72702,-25.25503],[25.88571,-24.87802],[25.84295,-24.78661],[25.8515,-24.75727],[26.39409,-24.63468],[26.46346,-24.60358],[26.51667,-24.47219],[26.84165,-24.24885],[26.99749,-23.65486],[27.33768,-23.40917],[27.52393,-23.37952],[27.6066,-23.21894],[27.74154,-23.2137],[27.93539,-23.04941],[27.93729,-22.96194],[28.04752,-22.90243],[28.04562,-22.8394],[28.34874,-22.5694],[28.63287,-22.55887],[28.91889,-22.44299],[29.0151,-22.22907],[29.10881,-22.21202],[29.15268,-22.21399],[29.18974,-22.18599],[29.21955,-22.17771],[29.37703,-22.19581],[29.3533,-22.18363],[29.24648,-22.05967],[29.1974,-22.07472],[29.14501,-22.07275],[29.08495,-22.04867],[29.04108,-22.00563],[29.02191,-21.95665],[29.02191,-21.90647],[29.04023,-21.85864],[29.07763,-21.81877],[28.58114,-21.63455],[28.49942,-21.66634],[28.29416,-21.59037],[28.01669,-21.57624],[27.91407,-21.31621],[27.69171,-21.08409],[27.72972,-20.51735],[27.69361,-20.48531],[27.28865,-20.49873],[27.29831,-20.28935],[27.21278,-20.08244],[26.72246,-19.92707],[26.17227,-19.53709],[25.96226,-19.08152],[25.99837,-19.02943],[25.94326,-18.90362],[25.82353,-18.82808],[25.79217,-18.6355],[25.68859,-18.56165],[25.53465,-18.39041],[25.39972,-18.12691],[25.31799,-18.07091],[25.23909,-17.90832],[25.26433,-17.79571]]]]}},{type:"Feature",properties:{iso1A2:"BY",iso1A3:"BLR",iso1N3:"112",wikidata:"Q184",nameEn:"Belarus",groups:["151","150"],callingCodes:["375"]},geometry:{type:"MultiPolygon",coordinates:[[[[28.15217,56.16964],[27.97865,56.11849],[27.63065,55.89687],[27.61683,55.78558],[27.3541,55.8089],[27.27804,55.78299],[27.1559,55.85032],[26.97153,55.8102],[26.87448,55.7172],[26.76872,55.67658],[26.71802,55.70645],[26.64888,55.70515],[26.63231,55.67968],[26.63167,55.57887],[26.55094,55.5093],[26.5522,55.40277],[26.44937,55.34832],[26.5709,55.32572],[26.6714,55.33902],[26.80929,55.31642],[26.83266,55.30444],[26.835,55.28182],[26.73017,55.24226],[26.72983,55.21788],[26.68075,55.19787],[26.69243,55.16718],[26.54753,55.14181],[26.51481,55.16051],[26.46249,55.12814],[26.35121,55.1525],[26.30628,55.12536],[26.23202,55.10439],[26.26941,55.08032],[26.20397,54.99729],[26.13386,54.98924],[26.05907,54.94631],[25.99129,54.95705],[25.89462,54.93438],[25.74122,54.80108],[25.75977,54.57252],[25.68045,54.5321],[25.64813,54.48704],[25.62203,54.4656],[25.63371,54.42075],[25.5376,54.33158],[25.55425,54.31591],[25.68513,54.31727],[25.78553,54.23327],[25.78563,54.15747],[25.71084,54.16704],[25.64875,54.1259],[25.54724,54.14925],[25.51452,54.17799],[25.56823,54.25212],[25.509,54.30267],[25.35559,54.26544],[25.22705,54.26271],[25.19199,54.219],[25.0728,54.13419],[24.991,54.14241],[24.96894,54.17589],[24.77131,54.11091],[24.85311,54.02862],[24.74279,53.96663],[24.69185,53.96543],[24.69652,54.01901],[24.62275,54.00217],[24.44411,53.90076],[24.34128,53.90076],[24.19638,53.96405],[23.98837,53.92554],[23.95098,53.9613],[23.81309,53.94205],[23.80543,53.89558],[23.71726,53.93379],[23.61677,53.92691],[23.51284,53.95052],[23.62004,53.60942],[23.81995,53.24131],[23.85657,53.22923],[23.91393,53.16469],[23.87548,53.0831],[23.92184,53.02079],[23.94689,52.95919],[23.91805,52.94016],[23.93763,52.71332],[23.73615,52.6149],[23.58296,52.59868],[23.45112,52.53774],[23.34141,52.44845],[23.18196,52.28812],[23.20071,52.22848],[23.47859,52.18215],[23.54314,52.12148],[23.61,52.11264],[23.64066,52.07626],[23.68733,51.9906],[23.61523,51.92066],[23.62691,51.78208],[23.53198,51.74298],[23.57053,51.55938],[23.56236,51.53673],[23.62751,51.50512],[23.6736,51.50255],[23.60906,51.62122],[23.7766,51.66809],[23.91118,51.63316],[23.8741,51.59734],[23.99907,51.58369],[24.13075,51.66979],[24.3163,51.75063],[24.29021,51.80841],[24.37123,51.88222],[24.98784,51.91273],[25.20228,51.97143],[25.46163,51.92205],[25.73673,51.91973],[25.80574,51.94556],[25.83217,51.92587],[26.00408,51.92967],[26.19084,51.86781],[26.39367,51.87315],[26.46962,51.80501],[26.69759,51.82284],[26.80043,51.75777],[26.9489,51.73788],[26.99422,51.76933],[27.20602,51.77291],[27.20948,51.66713],[27.26613,51.65957],[27.24828,51.60161],[27.47212,51.61184],[27.51058,51.5854],[27.55727,51.63486],[27.71932,51.60672],[27.67125,51.50854],[27.76052,51.47604],[27.85253,51.62293],[27.91844,51.61952],[27.95827,51.56065],[28.10658,51.57857],[28.23452,51.66988],[28.37592,51.54505],[28.47051,51.59734],[28.64429,51.5664],[28.69161,51.44695],[28.73143,51.46236],[28.75615,51.41442],[28.78224,51.45294],[28.76027,51.48802],[28.81795,51.55552],[28.95528,51.59222],[28.99098,51.56833],[29.1187,51.65872],[29.16402,51.64679],[29.20659,51.56918],[29.25603,51.57089],[29.25191,51.49828],[29.32881,51.37843],[29.42357,51.4187],[29.49773,51.39814],[29.54372,51.48372],[29.7408,51.53417],[29.77376,51.4461],[30.17888,51.51025],[30.34642,51.42555],[30.36153,51.33984],[30.56203,51.25655],[30.64992,51.35014],[30.51946,51.59649],[30.68804,51.82806],[30.76443,51.89739],[30.90897,52.00699],[30.95589,52.07775],[31.13332,52.1004],[31.25142,52.04131],[31.38326,52.12991],[31.7822,52.11406],[31.77877,52.18636],[31.6895,52.1973],[31.70735,52.26711],[31.57971,52.32146],[31.62084,52.33849],[31.61397,52.48843],[31.56316,52.51518],[31.63869,52.55361],[31.50406,52.69707],[31.57277,52.71613],[31.592,52.79011],[31.35667,52.97854],[31.24147,53.031],[31.32283,53.04101],[31.33519,53.08805],[31.3915,53.09712],[31.36403,53.13504],[31.40523,53.21406],[31.56316,53.19432],[31.62496,53.22886],[31.787,53.18033],[31.82373,53.10042],[32.15368,53.07594],[32.40773,53.18856],[32.51725,53.28431],[32.73257,53.33494],[32.74968,53.45597],[32.47777,53.5548],[32.40499,53.6656],[32.50112,53.68594],[32.45717,53.74039],[32.36663,53.7166],[32.12621,53.81586],[31.89137,53.78099],[31.77028,53.80015],[31.85019,53.91801],[31.88744,54.03653],[31.89599,54.0837],[31.57002,54.14535],[31.30791,54.25315],[31.3177,54.34067],[31.22945,54.46585],[31.08543,54.50361],[31.21399,54.63113],[31.19339,54.66947],[30.99187,54.67046],[30.98226,54.68872],[31.0262,54.70698],[30.97127,54.71967],[30.95479,54.74346],[30.75165,54.80699],[30.8264,54.90062],[30.81759,54.94064],[30.93144,54.9585],[30.95754,54.98609],[30.9081,55.02232],[30.94243,55.03964],[31.00972,55.02783],[31.02071,55.06167],[30.97369,55.17134],[30.87944,55.28223],[30.81946,55.27931],[30.8257,55.3313],[30.93144,55.3914],[30.90123,55.46621],[30.95204,55.50667],[30.93419,55.6185],[30.86003,55.63169],[30.7845,55.58514],[30.72957,55.66268],[30.67464,55.64176],[30.63344,55.73079],[30.51037,55.76568],[30.51346,55.78982],[30.48257,55.81066],[30.30987,55.83592],[30.27776,55.86819],[30.12136,55.8358],[29.97975,55.87281],[29.80672,55.79569],[29.61446,55.77716],[29.51283,55.70294],[29.3604,55.75862],[29.44692,55.95978],[29.21717,55.98971],[29.08299,56.03427],[28.73418,55.97131],[28.63668,56.07262],[28.68337,56.10173],[28.5529,56.11705],[28.43068,56.09407],[28.37987,56.11399],[28.36888,56.05805],[28.30571,56.06035],[28.15217,56.16964]]]]}},{type:"Feature",properties:{iso1A2:"BZ",iso1A3:"BLZ",iso1N3:"084",wikidata:"Q242",nameEn:"Belize",groups:["013","003","419","019"],roadSpeedUnit:"mph",callingCodes:["501"]},geometry:{type:"MultiPolygon",coordinates:[[[[-88.3268,18.49048],[-88.48242,18.49164],[-88.71505,18.0707],[-88.8716,17.89535],[-89.03839,18.0067],[-89.15105,17.95104],[-89.14985,17.81563],[-89.15025,17.04813],[-89.22683,15.88619],[-89.17418,15.90898],[-89.02415,15.9063],[-88.95358,15.88698],[-88.40779,16.09624],[-86.92368,17.61462],[-87.84815,18.18511],[-87.85693,18.18266],[-87.86657,18.19971],[-87.87604,18.18313],[-87.90671,18.15213],[-88.03165,18.16657],[-88.03238,18.41778],[-88.26593,18.47617],[-88.29909,18.47591],[-88.3268,18.49048]]]]}},{type:"Feature",properties:{iso1A2:"CA",iso1A3:"CAN",iso1N3:"124",wikidata:"Q16",nameEn:"Canada",groups:["021","003","019"],callingCodes:["1"]},geometry:{type:"MultiPolygon",coordinates:[[[[-67.20349,45.1722],[-67.19603,45.16771],[-67.15965,45.16179],[-67.11316,45.11176],[-67.0216,44.95333],[-66.96824,44.90965],[-66.98249,44.87071],[-66.96824,44.83078],[-66.93432,44.82597],[-67.16117,44.20069],[-61.98255,37.34815],[-56.27503,47.39728],[-53.12387,41.40385],[-46.37635,57.3249],[-76.75614,76.72014],[-68.21821,80.48551],[-45.47832,84.58738],[-140.97446,84.39275],[-141.00116,60.30648],[-140.5227,60.22077],[-140.45648,60.30919],[-139.98024,60.18027],[-139.68991,60.33693],[-139.05831,60.35205],[-139.20603,60.08896],[-139.05365,59.99655],[-138.71149,59.90728],[-138.62145,59.76431],[-137.60623,59.24465],[-137.4925,58.89415],[-136.82619,59.16198],[-136.52365,59.16752],[-136.47323,59.46617],[-136.33727,59.44466],[-136.22381,59.55526],[-136.31566,59.59083],[-135.48007,59.79937],[-135.03069,59.56208],[-135.00267,59.28745],[-134.7047,59.2458],[-134.55699,59.1297],[-134.48059,59.13231],[-134.27175,58.8634],[-133.84645,58.73543],[-133.38523,58.42773],[-131.8271,56.62247],[-130.77769,56.36185],[-130.33965,56.10849],[-130.10173,56.12178],[-130.00093,56.00325],[-130.00857,55.91344],[-130.15373,55.74895],[-129.97513,55.28029],[-130.08035,55.21556],[-130.18765,55.07744],[-130.27203,54.97174],[-130.44184,54.85377],[-130.64499,54.76912],[-130.61931,54.70835],[-133.92876,54.62289],[-133.36909,48.51151],[-125.03842,48.53282],[-123.50039,48.21223],[-123.15614,48.35395],[-123.26565,48.6959],[-123.0093,48.76586],[-123.0093,48.83186],[-123.32163,49.00419],[-117.03266,49.00056],[-116.04938,48.99999],[-114.0683,48.99885],[-110.0051,48.99901],[-104.05004,48.99925],[-101.36198,48.99935],[-97.24024,48.99952],[-95.15355,48.9996],[-95.15357,49.384],[-95.12903,49.37056],[-95.05825,49.35311],[-95.01419,49.35647],[-94.99532,49.36579],[-94.95681,49.37035],[-94.85381,49.32492],[-94.8159,49.32299],[-94.82487,49.29483],[-94.77355,49.11998],[-94.75017,49.09931],[-94.687,48.84077],[-94.70087,48.8339],[-94.70486,48.82365],[-94.69669,48.80918],[-94.69335,48.77883],[-94.58903,48.71803],[-94.54885,48.71543],[-94.53826,48.70216],[-94.44258,48.69223],[-94.4174,48.71049],[-94.27153,48.70232],[-94.25172,48.68404],[-94.25104,48.65729],[-94.23215,48.65202],[-93.85769,48.63284],[-93.83288,48.62745],[-93.80676,48.58232],[-93.80939,48.52439],[-93.79267,48.51631],[-93.66382,48.51845],[-93.47022,48.54357],[-93.44472,48.59147],[-93.40693,48.60948],[-93.39758,48.60364],[-93.3712,48.60599],[-93.33946,48.62787],[-93.25391,48.64266],[-92.94973,48.60866],[-92.7287,48.54005],[-92.6342,48.54133],[-92.62747,48.50278],[-92.69927,48.49573],[-92.71323,48.46081],[-92.65606,48.43471],[-92.50712,48.44921],[-92.45588,48.40624],[-92.48147,48.36609],[-92.37185,48.22259],[-92.27167,48.25046],[-92.30939,48.31251],[-92.26662,48.35651],[-92.202,48.35252],[-92.14732,48.36578],[-92.05339,48.35958],[-91.98929,48.25409],[-91.86125,48.21278],[-91.71231,48.19875],[-91.70451,48.11805],[-91.55649,48.10611],[-91.58025,48.04339],[-91.45829,48.07454],[-91.43248,48.04912],[-91.25025,48.08522],[-91.08016,48.18096],[-90.87588,48.2484],[-90.75045,48.09143],[-90.56444,48.12184],[-90.56312,48.09488],[-90.07418,48.11043],[-89.89974,47.98109],[-89.77248,48.02607],[-89.57972,48.00023],[-89.48837,48.01412],[-88.37033,48.30586],[-84.85871,46.88881],[-84.55635,46.45974],[-84.47607,46.45225],[-84.4481,46.48972],[-84.42101,46.49853],[-84.34174,46.50683],[-84.29893,46.49127],[-84.26351,46.49508],[-84.2264,46.53337],[-84.1945,46.54061],[-84.17723,46.52753],[-84.12885,46.53068],[-84.11196,46.50248],[-84.13451,46.39218],[-84.11254,46.32329],[-84.11615,46.2681],[-84.09756,46.25512],[-84.1096,46.23987],[-83.95399,46.05634],[-83.90453,46.05922],[-83.83329,46.12169],[-83.57017,46.105],[-83.43746,45.99749],[-83.59589,45.82131],[-82.48419,45.30225],[-82.42469,42.992],[-82.4146,42.97626],[-82.4253,42.95423],[-82.45331,42.93139],[-82.4826,42.8068],[-82.46613,42.76615],[-82.51063,42.66025],[-82.51858,42.611],[-82.57583,42.5718],[-82.58873,42.54984],[-82.64242,42.55594],[-82.82964,42.37355],[-83.02253,42.33045],[-83.07837,42.30978],[-83.09837,42.28877],[-83.12724,42.2376],[-83.14962,42.04089],[-83.11184,41.95671],[-82.67862,41.67615],[-78.93684,42.82887],[-78.90712,42.89733],[-78.90905,42.93022],[-78.93224,42.95229],[-78.96312,42.95509],[-78.98126,42.97],[-79.02074,42.98444],[-79.02424,43.01983],[-78.99941,43.05612],[-79.01055,43.06659],[-79.07486,43.07845],[-79.05671,43.10937],[-79.06881,43.12029],[-79.0427,43.13934],[-79.04652,43.16396],[-79.05384,43.17418],[-79.05002,43.20133],[-79.05544,43.21224],[-79.05512,43.25375],[-79.06921,43.26183],[-79.25796,43.54052],[-76.79706,43.63099],[-76.43859,44.09393],[-76.35324,44.13493],[-76.31222,44.19894],[-76.244,44.19643],[-76.1664,44.23051],[-76.16285,44.28262],[-76.00018,44.34896],[-75.95947,44.34463],[-75.8217,44.43176],[-75.76813,44.51537],[-75.41441,44.76614],[-75.2193,44.87821],[-75.01363,44.95608],[-74.99101,44.98051],[-74.8447,45.00606],[-74.66689,45.00646],[-74.32699,44.99029],[-73.35025,45.00942],[-71.50067,45.01357],[-71.48735,45.07784],[-71.42778,45.12624],[-71.40364,45.21382],[-71.44252,45.2361],[-71.37133,45.24624],[-71.29371,45.29996],[-71.22338,45.25184],[-71.19723,45.25438],[-71.14568,45.24128],[-71.08364,45.30623],[-71.01866,45.31573],[-71.0107,45.34819],[-70.95193,45.33895],[-70.91169,45.29849],[-70.89864,45.2398],[-70.84816,45.22698],[-70.80236,45.37444],[-70.82638,45.39828],[-70.78372,45.43269],[-70.65383,45.37592],[-70.62518,45.42286],[-70.72651,45.49771],[-70.68516,45.56964],[-70.54019,45.67291],[-70.38934,45.73215],[-70.41523,45.79497],[-70.25976,45.89675],[-70.24694,45.95138],[-70.31025,45.96424],[-70.23855,46.1453],[-70.29078,46.18832],[-70.18547,46.35357],[-70.05812,46.41768],[-69.99966,46.69543],[-69.22119,47.46461],[-69.05148,47.42012],[-69.05073,47.30076],[-69.05039,47.2456],[-68.89222,47.1807],[-68.70125,47.24399],[-68.60575,47.24659],[-68.57914,47.28431],[-68.38332,47.28723],[-68.37458,47.35851],[-68.23244,47.35712],[-67.94843,47.1925],[-67.87993,47.10377],[-67.78578,47.06473],[-67.78111,45.9392],[-67.75196,45.91814],[-67.80961,45.87531],[-67.75654,45.82324],[-67.80653,45.80022],[-67.80705,45.69528],[-67.6049,45.60725],[-67.43815,45.59162],[-67.42144,45.50584],[-67.50578,45.48971],[-67.42394,45.37969],[-67.48201,45.27351],[-67.34927,45.122],[-67.29754,45.14865],[-67.29748,45.18173],[-67.27039,45.1934],[-67.22751,45.16344],[-67.20349,45.1722]]]]}},{type:"Feature",properties:{iso1A2:"CC",iso1A3:"CCK",iso1N3:"166",wikidata:"Q36004",nameEn:"Cocos (Keeling) Islands",country:"AU",groups:["053","009"],driveSide:"left",callingCodes:["61"]},geometry:{type:"MultiPolygon",coordinates:[[[[96.61846,-10.82438],[96.02343,-12.68334],[97.93979,-12.33309],[96.61846,-10.82438]]]]}},{type:"Feature",properties:{iso1A2:"CD",iso1A3:"COD",iso1N3:"180",wikidata:"Q974",nameEn:"Democratic Republic of the Congo",aliases:["ZR"],groups:["017","202","002"],callingCodes:["243"]},geometry:{type:"MultiPolygon",coordinates:[[[[27.44012,5.07349],[27.09575,5.22305],[26.93064,5.13535],[26.85579,5.03887],[26.74572,5.10685],[26.48595,5.04984],[26.13371,5.25594],[25.86073,5.19455],[25.53271,5.37431],[25.34558,5.29101],[25.31256,5.03668],[24.71816,4.90509],[24.46719,5.0915],[23.38847,4.60013],[22.94817,4.82392],[22.89094,4.79321],[22.84691,4.69887],[22.78526,4.71423],[22.6928,4.47285],[22.60915,4.48821],[22.5431,4.22041],[22.45504,4.13039],[22.27682,4.11347],[22.10721,4.20723],[21.6405,4.317],[21.55904,4.25553],[21.25744,4.33676],[21.21341,4.29285],[21.11214,4.33895],[21.08793,4.39603],[20.90383,4.44877],[20.60184,4.42394],[18.62755,3.47564],[18.63857,3.19342],[18.10683,2.26876],[18.08034,1.58553],[17.85887,1.04327],[17.86989,0.58873],[17.95255,0.48128],[17.93877,0.32424],[17.81204,0.23884],[17.66051,-0.26535],[17.72112,-0.52707],[17.32438,-0.99265],[16.97999,-1.12762],[16.70724,-1.45815],[16.50336,-1.8795],[16.16173,-2.16586],[16.22785,-2.59528],[16.1755,-3.25014],[16.21407,-3.2969],[15.89448,-3.9513],[15.53081,-4.042],[15.48121,-4.22062],[15.41785,-4.28381],[15.32693,-4.27282],[15.25411,-4.31121],[15.1978,-4.32388],[14.83101,-4.80838],[14.67948,-4.92093],[14.5059,-4.84956],[14.41499,-4.8825],[14.37366,-4.56125],[14.47284,-4.42941],[14.3957,-4.36623],[14.40672,-4.28381],[13.9108,-4.50906],[13.81162,-4.41842],[13.71794,-4.44864],[13.70417,-4.72601],[13.50305,-4.77818],[13.41764,-4.89897],[13.11182,-4.5942],[13.09648,-4.63739],[13.11195,-4.67745],[12.8733,-4.74346],[12.70868,-4.95505],[12.63465,-4.94632],[12.60251,-5.01715],[12.46297,-5.09408],[12.49815,-5.14058],[12.51589,-5.1332],[12.53586,-5.14658],[12.53599,-5.1618],[12.52301,-5.17481],[12.52318,-5.74353],[12.26557,-5.74031],[12.20376,-5.76338],[11.95767,-5.94705],[12.42245,-6.07585],[13.04371,-5.87078],[16.55507,-5.85631],[16.96282,-7.21787],[17.5828,-8.13784],[18.33635,-8.00126],[19.33698,-7.99743],[19.5469,-7.00195],[20.30218,-6.98955],[20.31846,-6.91953],[20.61689,-6.90876],[20.56263,-7.28566],[21.79824,-7.29628],[21.84856,-9.59871],[22.19039,-9.94628],[22.32604,-10.76291],[22.17954,-10.85884],[22.25951,-11.24911],[22.54205,-11.05784],[23.16602,-11.10577],[23.45631,-10.946],[23.86868,-11.02856],[24.00027,-10.89356],[24.34528,-11.06816],[24.42612,-11.44975],[25.34069,-11.19707],[25.33058,-11.65767],[26.01777,-11.91488],[26.88687,-12.01868],[27.04351,-11.61312],[27.22541,-11.60323],[27.21025,-11.76157],[27.59932,-12.22123],[28.33199,-12.41375],[29.01918,-13.41353],[29.60531,-13.21685],[29.65078,-13.41844],[29.81551,-13.44683],[29.8139,-12.14898],[29.48404,-12.23604],[29.4992,-12.43843],[29.18592,-12.37921],[28.48357,-11.87532],[28.37241,-11.57848],[28.65032,-10.65133],[28.62795,-9.92942],[28.68532,-9.78],[28.56208,-9.49122],[28.51627,-9.44726],[28.52636,-9.35379],[28.36562,-9.30091],[28.38526,-9.23393],[28.9711,-8.66935],[28.88917,-8.4831],[30.79243,-8.27382],[30.2567,-7.14121],[29.52552,-6.2731],[29.43673,-4.44845],[29.23708,-3.75856],[29.21463,-3.3514],[29.25633,-3.05471],[29.17258,-2.99385],[29.16037,-2.95457],[29.09797,-2.91935],[29.09119,-2.87871],[29.0505,-2.81774],[29.00404,-2.81978],[29.00167,-2.78523],[29.04081,-2.7416],[29.00357,-2.70596],[28.94346,-2.69124],[28.89793,-2.66111],[28.90226,-2.62385],[28.89288,-2.55848],[28.87943,-2.55165],[28.86193,-2.53185],[28.86209,-2.5231],[28.87497,-2.50887],[28.88846,-2.50493],[28.89342,-2.49017],[28.89132,-2.47557],[28.86846,-2.44866],[28.86826,-2.41888],[28.89601,-2.37321],[28.95642,-2.37321],[29.00051,-2.29001],[29.105,-2.27043],[29.17562,-2.12278],[29.11847,-1.90576],[29.24458,-1.69663],[29.24323,-1.66826],[29.36322,-1.50887],[29.45038,-1.5054],[29.53062,-1.40499],[29.59061,-1.39016],[29.58388,-0.89821],[29.63006,-0.8997],[29.62708,-0.71055],[29.67176,-0.55714],[29.67474,-0.47969],[29.65091,-0.46777],[29.72687,-0.08051],[29.7224,0.07291],[29.77454,0.16675],[29.81922,0.16824],[29.87284,0.39166],[29.97413,0.52124],[29.95477,0.64486],[29.98307,0.84295],[30.1484,0.89805],[30.22139,0.99635],[30.24671,1.14974],[30.48503,1.21675],[31.30127,2.11006],[31.28042,2.17853],[31.20148,2.2217],[31.1985,2.29462],[31.12104,2.27676],[31.07934,2.30207],[31.06593,2.35862],[30.96911,2.41071],[30.91102,2.33332],[30.83059,2.42559],[30.74271,2.43601],[30.75612,2.5863],[30.8857,2.83923],[30.8574,2.9508],[30.77101,3.04897],[30.84251,3.26908],[30.93486,3.40737],[30.94081,3.50847],[30.85153,3.48867],[30.85997,3.5743],[30.80713,3.60506],[30.78512,3.67097],[30.56277,3.62703],[30.57378,3.74567],[30.55396,3.84451],[30.47691,3.83353],[30.27658,3.95653],[30.22374,3.93896],[30.1621,4.10586],[30.06964,4.13221],[29.79666,4.37809],[29.82087,4.56246],[29.49726,4.7007],[29.43341,4.50101],[29.22207,4.34297],[29.03054,4.48784],[28.8126,4.48784],[28.6651,4.42638],[28.20719,4.35614],[27.79551,4.59976],[27.76469,4.79284],[27.65462,4.89375],[27.56656,4.89375],[27.44012,5.07349]]]]}},{type:"Feature",properties:{iso1A2:"CF",iso1A3:"CAF",iso1N3:"140",wikidata:"Q929",nameEn:"Central African Republic",groups:["017","202","002"],callingCodes:["236"]},geometry:{type:"MultiPolygon",coordinates:[[[[22.87758,10.91915],[22.45889,11.00246],[21.72139,10.64136],[21.71479,10.29932],[21.63553,10.217],[21.52766,10.2105],[21.34934,9.95907],[21.26348,9.97642],[20.82979,9.44696],[20.36748,9.11019],[19.06421,9.00367],[18.86388,8.87971],[19.11044,8.68172],[18.79783,8.25929],[18.67455,8.22226],[18.62612,8.14163],[18.64153,8.08714],[18.6085,8.05009],[18.02731,8.01085],[17.93926,7.95853],[17.67288,7.98905],[16.8143,7.53971],[16.6668,7.67281],[16.658,7.75353],[16.59415,7.76444],[16.58315,7.88657],[16.41583,7.77971],[16.40703,7.68809],[15.79942,7.44149],[15.73118,7.52006],[15.49743,7.52179],[15.23397,7.25135],[15.04717,6.77085],[14.96311,6.75693],[14.79966,6.39043],[14.80122,6.34866],[14.74206,6.26356],[14.56149,6.18928],[14.43073,6.08867],[14.42917,6.00508],[14.49455,5.91683],[14.60974,5.91838],[14.62375,5.70466],[14.58951,5.59777],[14.62531,5.51411],[14.52724,5.28319],[14.57083,5.23979],[14.65489,5.21343],[14.73383,4.6135],[15.00825,4.41458],[15.08609,4.30282],[15.10644,4.1362],[15.17482,4.05131],[15.07686,4.01805],[15.73522,3.24348],[15.77725,3.26835],[16.05449,3.02306],[16.08252,2.45708],[16.19357,2.21537],[16.50126,2.84739],[16.46701,2.92512],[16.57598,3.47999],[16.68283,3.54257],[17.01746,3.55136],[17.35649,3.63045],[17.46876,3.70515],[17.60966,3.63705],[17.83421,3.61068],[17.85842,3.53378],[18.05656,3.56893],[18.14902,3.54476],[18.17323,3.47665],[18.24148,3.50302],[18.2723,3.57992],[18.39558,3.58212],[18.49245,3.63924],[18.58711,3.49423],[18.62755,3.47564],[20.60184,4.42394],[20.90383,4.44877],[21.08793,4.39603],[21.11214,4.33895],[21.21341,4.29285],[21.25744,4.33676],[21.55904,4.25553],[21.6405,4.317],[22.10721,4.20723],[22.27682,4.11347],[22.45504,4.13039],[22.5431,4.22041],[22.60915,4.48821],[22.6928,4.47285],[22.78526,4.71423],[22.84691,4.69887],[22.89094,4.79321],[22.94817,4.82392],[23.38847,4.60013],[24.46719,5.0915],[24.71816,4.90509],[25.31256,5.03668],[25.34558,5.29101],[25.53271,5.37431],[25.86073,5.19455],[26.13371,5.25594],[26.48595,5.04984],[26.74572,5.10685],[26.85579,5.03887],[26.93064,5.13535],[27.09575,5.22305],[27.44012,5.07349],[27.26886,5.25876],[27.23017,5.37167],[27.28621,5.56382],[27.22705,5.62889],[27.22705,5.71254],[26.51721,6.09655],[26.58259,6.1987],[26.32729,6.36272],[26.38022,6.63493],[25.90076,7.09549],[25.37461,7.33024],[25.35281,7.42595],[25.20337,7.50312],[25.20649,7.61115],[25.29214,7.66675],[25.25319,7.8487],[24.98855,7.96588],[24.85156,8.16933],[24.35965,8.26177],[24.13238,8.36959],[24.25691,8.69288],[23.51905,8.71749],[23.59065,8.99743],[23.44744,8.99128],[23.4848,9.16959],[23.56263,9.19418],[23.64358,9.28637],[23.64981,9.44303],[23.62179,9.53823],[23.69155,9.67566],[23.67164,9.86923],[23.3128,10.45214],[23.02221,10.69235],[22.87758,10.91915]]]]}},{type:"Feature",properties:{iso1A2:"CG",iso1A3:"COG",iso1N3:"178",wikidata:"Q971",nameEn:"Republic of the Congo",groups:["017","202","002"],callingCodes:["242"]},geometry:{type:"MultiPolygon",coordinates:[[[[18.62755,3.47564],[18.58711,3.49423],[18.49245,3.63924],[18.39558,3.58212],[18.2723,3.57992],[18.24148,3.50302],[18.17323,3.47665],[18.14902,3.54476],[18.05656,3.56893],[17.85842,3.53378],[17.83421,3.61068],[17.60966,3.63705],[17.46876,3.70515],[17.35649,3.63045],[17.01746,3.55136],[16.68283,3.54257],[16.57598,3.47999],[16.46701,2.92512],[16.50126,2.84739],[16.19357,2.21537],[16.15568,2.18955],[16.08563,2.19733],[16.05294,1.9811],[16.14634,1.70259],[16.02647,1.65591],[16.02959,1.76483],[15.48942,1.98265],[15.34776,1.91264],[15.22634,2.03243],[15.00996,1.98887],[14.61145,2.17866],[13.29457,2.16106],[13.13461,1.57238],[13.25447,1.32339],[13.15519,1.23368],[13.89582,1.4261],[14.25186,1.39842],[14.48179,0.9152],[14.26066,0.57255],[14.10909,0.58563],[13.88648,0.26652],[13.90632,-0.2287],[14.06862,-0.20826],[14.2165,-0.38261],[14.41887,-0.44799],[14.52569,-0.57818],[14.41838,-1.89412],[14.25932,-1.97624],[14.23518,-2.15671],[14.16202,-2.23916],[14.23829,-2.33715],[14.10442,-2.49268],[13.85846,-2.46935],[13.92073,-2.35581],[13.75884,-2.09293],[13.47977,-2.43224],[13.02759,-2.33098],[12.82172,-1.91091],[12.61312,-1.8129],[12.44656,-1.92025],[12.47925,-2.32626],[12.04895,-2.41704],[11.96866,-2.33559],[11.74605,-2.39936],[11.57637,-2.33379],[11.64487,-2.61865],[11.5359,-2.85654],[11.64798,-2.81146],[11.80365,-3.00424],[11.70558,-3.0773],[11.70227,-3.17465],[11.96554,-3.30267],[11.8318,-3.5812],[11.92719,-3.62768],[11.87083,-3.71571],[11.68608,-3.68942],[11.57949,-3.52798],[11.48764,-3.51089],[11.22301,-3.69888],[11.12647,-3.94169],[10.75913,-4.39519],[11.50888,-5.33417],[12.00924,-5.02627],[12.16068,-4.90089],[12.20901,-4.75642],[12.25587,-4.79437],[12.32324,-4.78415],[12.40964,-4.60609],[12.64835,-4.55937],[12.76844,-4.38709],[12.87096,-4.40315],[12.91489,-4.47907],[13.09648,-4.63739],[13.11182,-4.5942],[13.41764,-4.89897],[13.50305,-4.77818],[13.70417,-4.72601],[13.71794,-4.44864],[13.81162,-4.41842],[13.9108,-4.50906],[14.40672,-4.28381],[14.3957,-4.36623],[14.47284,-4.42941],[14.37366,-4.56125],[14.41499,-4.8825],[14.5059,-4.84956],[14.67948,-4.92093],[14.83101,-4.80838],[15.1978,-4.32388],[15.25411,-4.31121],[15.32693,-4.27282],[15.41785,-4.28381],[15.48121,-4.22062],[15.53081,-4.042],[15.89448,-3.9513],[16.21407,-3.2969],[16.1755,-3.25014],[16.22785,-2.59528],[16.16173,-2.16586],[16.50336,-1.8795],[16.70724,-1.45815],[16.97999,-1.12762],[17.32438,-0.99265],[17.72112,-0.52707],[17.66051,-0.26535],[17.81204,0.23884],[17.93877,0.32424],[17.95255,0.48128],[17.86989,0.58873],[17.85887,1.04327],[18.08034,1.58553],[18.10683,2.26876],[18.63857,3.19342],[18.62755,3.47564]]]]}},{type:"Feature",properties:{iso1A2:"CH",iso1A3:"CHE",iso1N3:"756",wikidata:"Q39",nameEn:"Switzerland",groups:["155","150"],callingCodes:["41"]},geometry:{type:"MultiPolygon",coordinates:[[[[8.72809,47.69282],[8.72617,47.69651],[8.73671,47.7169],[8.70543,47.73121],[8.74251,47.75168],[8.71778,47.76571],[8.68985,47.75686],[8.68022,47.78599],[8.65292,47.80066],[8.64425,47.76398],[8.62408,47.7626],[8.61657,47.79998],[8.56415,47.80633],[8.56814,47.78001],[8.48868,47.77215],[8.45771,47.7493],[8.44807,47.72426],[8.40569,47.69855],[8.4211,47.68407],[8.40473,47.67499],[8.41346,47.66676],[8.42264,47.66667],[8.44711,47.65379],[8.4667,47.65747],[8.46605,47.64103],[8.49656,47.64709],[8.5322,47.64687],[8.52801,47.66059],[8.56141,47.67088],[8.57683,47.66158],[8.6052,47.67258],[8.61113,47.66332],[8.62884,47.65098],[8.62049,47.63757],[8.60412,47.63735],[8.61471,47.64514],[8.60701,47.65271],[8.59545,47.64298],[8.60348,47.61204],[8.57586,47.59537],[8.55756,47.62394],[8.51686,47.63476],[8.50747,47.61897],[8.45578,47.60121],[8.46637,47.58389],[8.48949,47.588],[8.49431,47.58107],[8.43235,47.56617],[8.39477,47.57826],[8.38273,47.56608],[8.32735,47.57133],[8.30277,47.58607],[8.29524,47.5919],[8.29722,47.60603],[8.2824,47.61225],[8.26313,47.6103],[8.25863,47.61571],[8.23809,47.61204],[8.22577,47.60385],[8.22011,47.6181],[8.20617,47.62141],[8.19378,47.61636],[8.1652,47.5945],[8.14947,47.59558],[8.13823,47.59147],[8.13662,47.58432],[8.11543,47.5841],[8.10395,47.57918],[8.10002,47.56504],[8.08557,47.55768],[8.06663,47.56374],[8.04383,47.55443],[8.02136,47.55096],[8.00113,47.55616],[7.97581,47.55493],[7.95682,47.55789],[7.94494,47.54511],[7.91251,47.55031],[7.90673,47.57674],[7.88664,47.58854],[7.84412,47.5841],[7.81901,47.58798],[7.79486,47.55691],[7.75261,47.54599],[7.71961,47.54219],[7.69642,47.53297],[7.68101,47.53232],[7.6656,47.53752],[7.66174,47.54554],[7.65083,47.54662],[7.63338,47.56256],[7.67655,47.56435],[7.68904,47.57133],[7.67115,47.5871],[7.68486,47.59601],[7.69385,47.60099],[7.68229,47.59905],[7.67395,47.59212],[7.64599,47.59695],[7.64213,47.5944],[7.64309,47.59151],[7.61929,47.57683],[7.60459,47.57869],[7.60523,47.58519],[7.58945,47.59017],[7.58386,47.57536],[7.56684,47.57785],[7.56548,47.57617],[7.55689,47.57232],[7.55652,47.56779],[7.53634,47.55553],[7.52831,47.55347],[7.51723,47.54578],[7.50873,47.54546],[7.49691,47.53821],[7.50588,47.52856],[7.51904,47.53515],[7.53199,47.5284],[7.5229,47.51644],[7.49804,47.51798],[7.51076,47.49651],[7.47534,47.47932],[7.43356,47.49712],[7.42923,47.48628],[7.4583,47.47216],[7.4462,47.46264],[7.43088,47.45846],[7.40308,47.43638],[7.35603,47.43432],[7.33526,47.44186],[7.24669,47.4205],[7.17026,47.44312],[7.19583,47.49455],[7.16249,47.49025],[7.12781,47.50371],[7.07425,47.48863],[7.0231,47.50522],[6.98425,47.49432],[7.0024,47.45264],[6.93953,47.43388],[6.93744,47.40714],[6.88542,47.37262],[6.87959,47.35335],[7.03125,47.36996],[7.0564,47.35134],[7.05305,47.33304],[6.94316,47.28747],[6.95108,47.26428],[6.9508,47.24338],[6.8489,47.15933],[6.76788,47.1208],[6.68823,47.06616],[6.71531,47.0494],[6.43341,46.92703],[6.46456,46.88865],[6.43216,46.80336],[6.45209,46.77502],[6.38351,46.73171],[6.27135,46.68251],[6.11084,46.57649],[6.1567,46.54402],[6.07269,46.46244],[6.08427,46.44305],[6.06407,46.41676],[6.09926,46.40768],[6.15016,46.3778],[6.15985,46.37721],[6.16987,46.36759],[6.15738,46.3491],[6.13876,46.33844],[6.1198,46.31157],[6.11697,46.29547],[6.1013,46.28512],[6.11926,46.2634],[6.12446,46.25059],[6.10071,46.23772],[6.08563,46.24651],[6.07072,46.24085],[6.0633,46.24583],[6.05029,46.23518],[6.04602,46.23127],[6.03342,46.2383],[6.02461,46.23313],[5.97542,46.21525],[5.96515,46.19638],[5.99573,46.18587],[5.98846,46.17046],[5.98188,46.17392],[5.97508,46.15863],[5.9641,46.14412],[5.95781,46.12925],[5.97893,46.13303],[5.9871,46.14499],[6.01791,46.14228],[6.03614,46.13712],[6.04564,46.14031],[6.05203,46.15191],[6.07491,46.14879],[6.09199,46.15191],[6.09926,46.14373],[6.13397,46.1406],[6.15305,46.15194],[6.18116,46.16187],[6.18871,46.16644],[6.18707,46.17999],[6.19552,46.18401],[6.19807,46.18369],[6.20539,46.19163],[6.21114,46.1927],[6.21273,46.19409],[6.21603,46.19507],[6.21844,46.19837],[6.22222,46.19888],[6.22175,46.20045],[6.23544,46.20714],[6.23913,46.20511],[6.24821,46.20531],[6.26007,46.21165],[6.27694,46.21566],[6.29663,46.22688],[6.31041,46.24417],[6.29474,46.26221],[6.26749,46.24745],[6.24952,46.26255],[6.23775,46.27822],[6.25137,46.29014],[6.24826,46.30175],[6.21981,46.31304],[6.25432,46.3632],[6.53358,46.45431],[6.82312,46.42661],[6.8024,46.39171],[6.77152,46.34784],[6.86052,46.28512],[6.78968,46.14058],[6.89321,46.12548],[6.87868,46.03855],[6.93862,46.06502],[7.00946,45.9944],[7.04151,45.92435],[7.10685,45.85653],[7.56343,45.97421],[7.85949,45.91485],[7.9049,45.99945],[7.98881,45.99867],[8.02906,46.10331],[8.11383,46.11577],[8.16866,46.17817],[8.08814,46.26692],[8.31162,46.38044],[8.30648,46.41587],[8.42464,46.46367],[8.46317,46.43712],[8.45032,46.26869],[8.62242,46.12112],[8.75697,46.10395],[8.80778,46.10085],[8.85617,46.0748],[8.79414,46.00913],[8.78585,45.98973],[8.79362,45.99207],[8.8319,45.9879],[8.85121,45.97239],[8.86688,45.96135],[8.88904,45.95465],[8.93649,45.86775],[8.94372,45.86587],[8.93504,45.86245],[8.91129,45.8388],[8.94737,45.84285],[8.9621,45.83707],[8.99663,45.83466],[9.00324,45.82055],[9.0298,45.82127],[9.03279,45.82865],[9.03793,45.83548],[9.03505,45.83976],[9.04059,45.8464],[9.04546,45.84968],[9.06642,45.8761],[9.09065,45.89906],[8.99257,45.9698],[9.01618,46.04928],[9.24503,46.23616],[9.29226,46.32717],[9.25502,46.43743],[9.28136,46.49685],[9.36128,46.5081],[9.40487,46.46621],[9.45936,46.50873],[9.46117,46.37481],[9.57015,46.2958],[9.71273,46.29266],[9.73086,46.35071],[9.95249,46.38045],[10.07055,46.21668],[10.14439,46.22992],[10.17862,46.25626],[10.10506,46.3372],[10.165,46.41051],[10.03715,46.44479],[10.10307,46.61003],[10.23674,46.63484],[10.25309,46.57432],[10.46136,46.53164],[10.49375,46.62049],[10.44686,46.64162],[10.40475,46.63671],[10.38659,46.67847],[10.47197,46.85698],[10.48376,46.93891],[10.36933,47.00212],[10.30031,46.92093],[10.24128,46.93147],[10.22675,46.86942],[10.10715,46.84296],[9.98058,46.91434],[9.88266,46.93343],[9.87935,47.01337],[9.60717,47.06091],[9.55721,47.04762],[9.54041,47.06495],[9.47548,47.05257],[9.47139,47.06402],[9.51362,47.08505],[9.52089,47.10019],[9.51044,47.13727],[9.48774,47.17402],[9.4891,47.19346],[9.50318,47.22153],[9.52406,47.24959],[9.53116,47.27029],[9.54773,47.2809],[9.55857,47.29919],[9.58513,47.31334],[9.59978,47.34671],[9.62476,47.36639],[9.65427,47.36824],[9.66243,47.37136],[9.6711,47.37824],[9.67445,47.38429],[9.67334,47.39191],[9.6629,47.39591],[9.65136,47.40504],[9.65043,47.41937],[9.6446,47.43233],[9.64483,47.43842],[9.65863,47.44847],[9.65728,47.45383],[9.6423,47.45599],[9.62475,47.45685],[9.62158,47.45858],[9.60841,47.47178],[9.60484,47.46358],[9.60205,47.46165],[9.59482,47.46305],[9.58208,47.48344],[9.56312,47.49495],[9.55125,47.53629],[9.25619,47.65939],[9.18203,47.65598],[9.17593,47.65399],[9.1755,47.65584],[9.1705,47.65513],[9.15181,47.66904],[9.13845,47.66389],[9.09891,47.67801],[9.02093,47.6868],[8.94093,47.65596],[8.89946,47.64769],[8.87625,47.65441],[8.87383,47.67045],[8.85065,47.68209],[8.86989,47.70504],[8.82002,47.71458],[8.80663,47.73821],[8.77309,47.72059],[8.76965,47.7075],[8.79966,47.70222],[8.79511,47.67462],[8.75856,47.68969],[8.72809,47.69282]],[[8.95861,45.96485],[8.96668,45.98436],[8.97741,45.98317],[8.97604,45.96151],[8.95861,45.96485]],[[8.70847,47.68904],[8.68985,47.69552],[8.66837,47.68437],[8.65769,47.68928],[8.67508,47.6979],[8.66416,47.71367],[8.70237,47.71453],[8.71773,47.69088],[8.70847,47.68904]]]]}},{type:"Feature",properties:{iso1A2:"CI",iso1A3:"CIV",iso1N3:"384",wikidata:"Q1008",nameEn:"Côte d'Ivoire",groups:["011","202","002"],callingCodes:["225"]},geometry:{type:"MultiPolygon",coordinates:[[[[-7.52774,3.7105],[-3.34019,4.17519],[-3.10675,5.08515],[-3.11073,5.12675],[-3.063,5.13665],[-2.96554,5.10397],[-2.95261,5.12477],[-2.75502,5.10657],[-2.73074,5.1364],[-2.77625,5.34621],[-2.72737,5.34789],[-2.76614,5.60963],[-2.85378,5.65156],[-2.93132,5.62137],[-2.96671,5.6415],[-2.95323,5.71865],[-3.01896,5.71697],[-3.25999,6.62521],[-3.21954,6.74407],[-3.23327,6.81744],[-2.95438,7.23737],[-2.97822,7.27165],[-2.92339,7.60847],[-2.79467,7.86002],[-2.78395,7.94974],[-2.74819,7.92613],[-2.67787,8.02055],[-2.61232,8.02645],[-2.62901,8.11495],[-2.49037,8.20872],[-2.58243,8.7789],[-2.66357,9.01771],[-2.77799,9.04949],[-2.69814,9.22717],[-2.68802,9.49343],[-2.76494,9.40778],[-2.93012,9.57403],[-3.00765,9.74019],[-3.16609,9.85147],[-3.19306,9.93781],[-3.27228,9.84981],[-3.31779,9.91125],[-3.69703,9.94279],[-4.25999,9.76012],[-4.31392,9.60062],[-4.6426,9.70696],[-4.96621,9.89132],[-4.96453,9.99923],[-5.12465,10.29788],[-5.39602,10.2929],[-5.51058,10.43177],[-5.65135,10.46767],[-5.78124,10.43952],[-5.99478,10.19694],[-6.18851,10.24244],[-6.1731,10.46983],[-6.24795,10.74248],[-6.325,10.68624],[-6.40646,10.69922],[-6.42847,10.5694],[-6.52974,10.59104],[-6.63541,10.66893],[-6.68164,10.35074],[-6.93921,10.35291],[-7.01186,10.25111],[-6.97444,10.21644],[-7.00966,10.15794],[-7.0603,10.14711],[-7.13331,10.24877],[-7.3707,10.24677],[-7.44555,10.44602],[-7.52261,10.4655],[-7.54462,10.40921],[-7.63048,10.46334],[-7.92107,10.15577],[-7.97971,10.17117],[-8.01225,10.1021],[-8.11921,10.04577],[-8.15652,9.94288],[-8.09434,9.86936],[-8.14657,9.55062],[-8.03463,9.39604],[-7.85056,9.41812],[-7.90777,9.20456],[-7.73862,9.08422],[-7.92518,8.99332],[-7.95503,8.81146],[-7.69882,8.66148],[-7.65653,8.36873],[-7.92518,8.50652],[-8.22991,8.48438],[-8.2411,8.24196],[-8.062,8.16071],[-7.98675,8.20134],[-7.99919,8.11023],[-7.94695,8.00925],[-8.06449,8.04989],[-8.13414,7.87991],[-8.09931,7.78626],[-8.21374,7.54466],[-8.4003,7.6285],[-8.47114,7.55676],[-8.41935,7.51203],[-8.37458,7.25794],[-8.29249,7.1691],[-8.31736,6.82837],[-8.59456,6.50612],[-8.48652,6.43797],[-8.45666,6.49977],[-8.38453,6.35887],[-8.3298,6.36381],[-8.17557,6.28222],[-8.00642,6.31684],[-7.90692,6.27728],[-7.83478,6.20309],[-7.8497,6.08932],[-7.79747,6.07696],[-7.78254,5.99037],[-7.70294,5.90625],[-7.67309,5.94337],[-7.48155,5.80974],[-7.46165,5.84934],[-7.43677,5.84687],[-7.43926,5.74787],[-7.37209,5.61173],[-7.43428,5.42355],[-7.36463,5.32944],[-7.46165,5.26256],[-7.48901,5.14118],[-7.55369,5.08667],[-7.53876,4.94294],[-7.59349,4.8909],[-7.53259,4.35145],[-7.52774,3.7105]]]]}},{type:"Feature",properties:{iso1A2:"CK",iso1A3:"COK",iso1N3:"184",wikidata:"Q26988",nameEn:"Cook Islands",country:"NZ",groups:["061","009"],driveSide:"left",callingCodes:["682"]},geometry:{type:"MultiPolygon",coordinates:[[[[-167.73854,-14.92809],[-167.73129,-23.22266],[-156.46451,-23.21255],[-156.4957,-12.32002],[-156.50903,-7.4975],[-167.75329,-7.52784],[-167.75195,-10.12005],[-167.73854,-14.92809]]]]}},{type:"Feature",properties:{iso1A2:"CL",iso1A3:"CHL",iso1N3:"152",wikidata:"Q298",nameEn:"Chile",groups:["005","419","019"],callingCodes:["56"]},geometry:{type:"MultiPolygon",coordinates:[[[[-68.60702,-52.65781],[-68.41683,-52.33516],[-69.97824,-52.00845],[-71.99889,-51.98018],[-72.33873,-51.59954],[-72.31343,-50.58411],[-73.15765,-50.78337],[-73.55259,-49.92488],[-73.45156,-49.79461],[-73.09655,-49.14342],[-72.56894,-48.81116],[-72.54042,-48.52392],[-72.27662,-48.28727],[-72.50478,-47.80586],[-71.94152,-47.13595],[-71.68577,-46.55385],[-71.75614,-45.61611],[-71.35687,-45.22075],[-72.06985,-44.81756],[-71.26418,-44.75684],[-71.16436,-44.46244],[-71.81318,-44.38097],[-71.64206,-43.64774],[-72.14828,-42.85321],[-72.15541,-42.15941],[-71.74901,-42.11711],[-71.92726,-40.72714],[-71.37826,-38.91474],[-70.89532,-38.6923],[-71.24279,-37.20264],[-70.95047,-36.4321],[-70.38008,-36.02375],[-70.49416,-35.24145],[-69.87386,-34.13344],[-69.88099,-33.34489],[-70.55832,-31.51559],[-70.14479,-30.36595],[-69.8596,-30.26131],[-69.99507,-29.28351],[-69.80969,-29.07185],[-69.66709,-28.44055],[-69.22504,-27.95042],[-68.77586,-27.16029],[-68.43363,-27.08414],[-68.27677,-26.90626],[-68.59048,-26.49861],[-68.56909,-26.28146],[-68.38372,-26.15353],[-68.57622,-25.32505],[-68.38372,-25.08636],[-68.56909,-24.69831],[-68.24825,-24.42596],[-67.33563,-24.04237],[-66.99632,-22.99839],[-67.18382,-22.81525],[-67.54284,-22.89771],[-67.85114,-22.87076],[-68.18816,-21.28614],[-68.40403,-20.94562],[-68.53957,-20.91542],[-68.55383,-20.7355],[-68.44023,-20.62701],[-68.7276,-20.46178],[-68.74273,-20.08817],[-68.57132,-20.03134],[-68.54611,-19.84651],[-68.66761,-19.72118],[-68.41218,-19.40499],[-68.61989,-19.27584],[-68.80602,-19.08355],[-68.87082,-19.06003],[-68.94987,-18.93302],[-69.07432,-18.28259],[-69.14807,-18.16893],[-69.07496,-18.03715],[-69.28671,-17.94844],[-69.34126,-17.72753],[-69.46623,-17.60518],[-69.46897,-17.4988],[-69.66483,-17.65083],[-69.79087,-17.65563],[-69.82868,-17.72048],[-69.75305,-17.94605],[-69.81607,-18.12582],[-69.96732,-18.25992],[-70.16394,-18.31737],[-70.31267,-18.31258],[-70.378,-18.3495],[-70.59118,-18.35072],[-113.52687,-26.52828],[-68.11646,-58.14883],[-66.07313,-55.19618],[-67.11046,-54.94199],[-67.46182,-54.92205],[-68.01394,-54.8753],[-68.60733,-54.9125],[-68.60702,-52.65781]]]]}},{type:"Feature",properties:{iso1A2:"CM",iso1A3:"CMR",iso1N3:"120",wikidata:"Q1009",nameEn:"Cameroon",groups:["017","202","002"],callingCodes:["237"]},geometry:{type:"MultiPolygon",coordinates:[[[[14.83314,12.62963],[14.55058,12.78256],[14.56101,12.91036],[14.46881,13.08259],[14.08251,13.0797],[14.20204,12.53405],[14.17523,12.41916],[14.22215,12.36533],[14.4843,12.35223],[14.6474,12.17466],[14.61612,11.7798],[14.55207,11.72001],[14.64591,11.66166],[14.6124,11.51283],[14.17821,11.23831],[13.97489,11.30258],[13.78945,11.00154],[13.7403,11.00593],[13.70753,10.94451],[13.73434,10.9255],[13.54964,10.61236],[13.5705,10.53183],[13.43644,10.13326],[13.34111,10.12299],[13.25025,10.03647],[13.25323,10.00127],[13.286,9.9822],[13.27409,9.93232],[13.24132,9.91031],[13.25025,9.86042],[13.29941,9.8296],[13.25472,9.76795],[13.22642,9.57266],[13.02385,9.49334],[12.85628,9.36698],[12.91958,9.33905],[12.90022,9.11411],[12.81085,8.91992],[12.79,8.75361],[12.71701,8.7595],[12.68722,8.65938],[12.44146,8.6152],[12.4489,8.52536],[12.26123,8.43696],[12.24782,8.17904],[12.19271,8.10826],[12.20909,7.97553],[11.99908,7.67302],[12.01844,7.52981],[11.93205,7.47812],[11.84864,7.26098],[11.87396,7.09398],[11.63117,6.9905],[11.55818,6.86186],[11.57755,6.74059],[11.51499,6.60892],[11.42264,6.5882],[11.42041,6.53789],[11.09495,6.51717],[11.09644,6.68437],[10.94302,6.69325],[10.8179,6.83377],[10.83727,6.9358],[10.60789,7.06885],[10.59746,7.14719],[10.57214,7.16345],[10.53639,6.93432],[10.21466,6.88996],[10.15135,7.03781],[9.86314,6.77756],[9.77824,6.79088],[9.70674,6.51717],[9.51757,6.43874],[8.84209,5.82562],[8.88156,5.78857],[8.83687,5.68483],[8.92029,5.58403],[8.78027,5.1243],[8.60302,4.87353],[8.34397,4.30689],[9.22018,3.72052],[9.81162,2.33797],[9.82123,2.35097],[9.83754,2.32428],[9.83238,2.29079],[9.84716,2.24676],[9.89012,2.20457],[9.90749,2.20049],[9.991,2.16561],[11.3561,2.17217],[11.37116,2.29975],[13.28534,2.25716],[13.29457,2.16106],[14.61145,2.17866],[15.00996,1.98887],[15.22634,2.03243],[15.34776,1.91264],[15.48942,1.98265],[16.02959,1.76483],[16.02647,1.65591],[16.14634,1.70259],[16.05294,1.9811],[16.08563,2.19733],[16.15568,2.18955],[16.19357,2.21537],[16.08252,2.45708],[16.05449,3.02306],[15.77725,3.26835],[15.73522,3.24348],[15.07686,4.01805],[15.17482,4.05131],[15.10644,4.1362],[15.08609,4.30282],[15.00825,4.41458],[14.73383,4.6135],[14.65489,5.21343],[14.57083,5.23979],[14.52724,5.28319],[14.62531,5.51411],[14.58951,5.59777],[14.62375,5.70466],[14.60974,5.91838],[14.49455,5.91683],[14.42917,6.00508],[14.43073,6.08867],[14.56149,6.18928],[14.74206,6.26356],[14.80122,6.34866],[14.79966,6.39043],[14.96311,6.75693],[15.04717,6.77085],[15.23397,7.25135],[15.49743,7.52179],[15.56964,7.58936],[15.59272,7.7696],[15.50743,7.79302],[15.20426,8.50892],[15.09484,8.65982],[14.83566,8.80557],[14.35707,9.19611],[14.37094,9.2954],[13.97544,9.6365],[14.01793,9.73169],[14.1317,9.82413],[14.20411,10.00055],[14.4673,10.00264],[14.80082,9.93818],[14.95722,9.97926],[15.05999,9.94845],[15.14043,9.99246],[15.24618,9.99246],[15.41408,9.92876],[15.68761,9.99344],[15.50535,10.1098],[15.30874,10.31063],[15.23724,10.47764],[15.14936,10.53915],[15.15532,10.62846],[15.06737,10.80921],[15.09127,10.87431],[15.04957,11.02347],[15.10021,11.04101],[15.0585,11.40481],[15.13149,11.5537],[15.06595,11.71126],[15.11579,11.79313],[15.04808,11.8731],[15.05786,12.0608],[15.0349,12.10698],[15.00146,12.1223],[14.96952,12.0925],[14.89019,12.16593],[14.90827,12.3269],[14.83314,12.62963]]]]}},{type:"Feature",properties:{iso1A2:"CN",iso1A3:"CHN",iso1N3:"156",wikidata:"Q148",nameEn:"China",aliases:["RC"],groups:["030","142"],callingCodes:["86"]},geometry:{type:"MultiPolygon",coordinates:[[[[125.6131,53.07229],[125.17522,53.20225],[124.46078,53.21881],[123.86158,53.49391],[123.26989,53.54843],[122.85966,53.47395],[122.35063,53.49565],[121.39213,53.31888],[120.85633,53.28499],[120.0451,52.7359],[120.04049,52.58773],[120.46454,52.63811],[120.71673,52.54099],[120.61346,52.32447],[120.77337,52.20805],[120.65907,51.93544],[120.10963,51.671],[119.13553,50.37412],[119.38598,50.35162],[119.27996,50.13348],[119.11003,50.00276],[118.61623,49.93809],[117.82343,49.52696],[117.48208,49.62324],[117.27597,49.62544],[117.07142,49.68482],[116.71193,49.83813],[116.03781,48.87014],[116.06565,48.81716],[115.78876,48.51781],[115.811,48.25699],[115.52082,48.15367],[115.57128,47.91988],[115.94296,47.67741],[116.08431,47.80693],[116.2527,47.87766],[116.4465,47.83662],[116.67405,47.89039],[116.87527,47.88836],[117.08918,47.82242],[117.37875,47.63627],[117.50181,47.77216],[117.80196,48.01661],[118.03676,48.00982],[118.11009,48.04],[118.22677,48.03853],[118.29654,48.00246],[118.55766,47.99277],[118.7564,47.76947],[119.12343,47.66458],[119.13995,47.53997],[119.35892,47.48104],[119.31964,47.42617],[119.54918,47.29505],[119.56019,47.24874],[119.62403,47.24575],[119.71209,47.19192],[119.85518,46.92196],[119.91242,46.90091],[119.89261,46.66423],[119.80455,46.67631],[119.77373,46.62947],[119.68127,46.59015],[119.65265,46.62342],[119.42827,46.63783],[119.37306,46.61132],[119.30261,46.6083],[119.24978,46.64761],[119.10448,46.65516],[119.00541,46.74273],[118.92616,46.72765],[118.89974,46.77139],[118.8337,46.77742],[118.78747,46.68689],[118.30534,46.73519],[117.69554,46.50991],[117.60748,46.59771],[117.41782,46.57862],[117.36609,46.36335],[117.07252,46.35818],[116.83166,46.38637],[116.75551,46.33083],[116.58612,46.30211],[116.26678,45.96479],[116.24012,45.8778],[116.27366,45.78637],[116.16989,45.68603],[115.91898,45.6227],[115.69688,45.45761],[115.35757,45.39106],[114.94546,45.37377],[114.74612,45.43585],[114.54801,45.38337],[114.5166,45.27189],[114.08071,44.92847],[113.909,44.91444],[113.63821,44.74326],[112.74662,44.86297],[112.4164,45.06858],[111.98695,45.09074],[111.76275,44.98032],[111.40498,44.3461],[111.96289,43.81596],[111.93776,43.68709],[111.79758,43.6637],[111.59087,43.51207],[111.0149,43.3289],[110.4327,42.78293],[110.08401,42.6411],[109.89402,42.63111],[109.452,42.44842],[109.00679,42.45302],[108.84489,42.40246],[108.23156,42.45532],[107.57258,42.40898],[107.49681,42.46221],[107.29755,42.41395],[107.24774,42.36107],[106.76517,42.28741],[105.24708,41.7442],[105.01119,41.58382],[104.91272,41.64619],[104.51667,41.66113],[104.52258,41.8706],[103.92804,41.78246],[103.3685,41.89696],[102.72403,42.14675],[102.42826,42.15137],[102.07645,42.22519],[101.80515,42.50074],[101.28833,42.58524],[100.84979,42.67087],[100.33297,42.68231],[99.50671,42.56535],[97.1777,42.7964],[96.37926,42.72055],[96.35658,42.90363],[95.89543,43.2528],[95.52594,43.99353],[95.32891,44.02407],[95.39772,44.2805],[95.01191,44.25274],[94.71959,44.35284],[94.10003,44.71016],[93.51161,44.95964],[91.64048,45.07408],[90.89169,45.19667],[90.65114,45.49314],[90.70907,45.73437],[91.03026,46.04194],[90.99672,46.14207],[90.89639,46.30711],[91.07696,46.57315],[91.0147,46.58171],[91.03649,46.72916],[90.84035,46.99525],[90.76108,46.99399],[90.48542,47.30438],[90.48854,47.41826],[90.33598,47.68303],[90.10871,47.7375],[90.06512,47.88177],[89.76624,47.82745],[89.55453,48.0423],[89.0711,47.98528],[88.93186,48.10263],[88.8011,48.11302],[88.58316,48.21893],[88.58939,48.34531],[87.96361,48.58478],[88.0788,48.71436],[87.73822,48.89582],[87.88171,48.95853],[87.81333,49.17354],[87.48983,49.13794],[87.478,49.07403],[87.28386,49.11626],[86.87238,49.12432],[86.73568,48.99918],[86.75343,48.70331],[86.38069,48.46064],[85.73581,48.3939],[85.5169,48.05493],[85.61067,47.49753],[85.69696,47.2898],[85.54294,47.06171],[85.22443,47.04816],[84.93995,46.87399],[84.73077,47.01394],[83.92184,46.98912],[83.04622,47.19053],[82.21792,45.56619],[82.58474,45.40027],[82.51374,45.1755],[81.73278,45.3504],[80.11169,45.03352],[79.8987,44.89957],[80.38384,44.63073],[80.40229,44.23319],[80.40031,44.10986],[80.75156,43.44948],[80.69718,43.32589],[80.77771,43.30065],[80.78817,43.14235],[80.62913,43.141],[80.3735,43.01557],[80.58999,42.9011],[80.38169,42.83142],[80.26886,42.8366],[80.16892,42.61137],[80.26841,42.23797],[80.17807,42.21166],[80.17842,42.03211],[79.92977,42.04113],[78.3732,41.39603],[78.15757,41.38565],[78.12873,41.23091],[77.81287,41.14307],[77.76206,41.01574],[77.52723,41.00227],[77.3693,41.0375],[77.28004,41.0033],[76.99302,41.0696],[76.75681,40.95354],[76.5261,40.46114],[76.33659,40.3482],[75.96168,40.38064],[75.91361,40.2948],[75.69663,40.28642],[75.5854,40.66874],[75.22834,40.45382],[75.08243,40.43945],[74.82013,40.52197],[74.78168,40.44886],[74.85996,40.32857],[74.69875,40.34668],[74.35063,40.09742],[74.25533,40.13191],[73.97049,40.04378],[73.83006,39.76136],[73.9051,39.75073],[73.92354,39.69565],[73.94683,39.60733],[73.87018,39.47879],[73.59831,39.46425],[73.59241,39.40843],[73.5004,39.38402],[73.55396,39.3543],[73.54572,39.27567],[73.60638,39.24534],[73.75823,39.023],[73.81728,39.04007],[73.82964,38.91517],[73.7445,38.93867],[73.7033,38.84782],[73.80656,38.66449],[73.79806,38.61106],[73.97933,38.52945],[74.17022,38.65504],[74.51217,38.47034],[74.69619,38.42947],[74.69894,38.22155],[74.80331,38.19889],[74.82665,38.07359],[74.9063,38.03033],[74.92416,37.83428],[75.00935,37.77486],[74.8912,37.67576],[74.94338,37.55501],[75.06011,37.52779],[75.15899,37.41443],[75.09719,37.37297],[75.12328,37.31839],[74.88887,37.23275],[74.80605,37.21565],[74.49981,37.24518],[74.56453,37.03023],[75.13839,37.02622],[75.40481,36.95382],[75.45562,36.71971],[75.72737,36.7529],[75.92391,36.56986],[76.0324,36.41198],[76.00906,36.17511],[75.93028,36.13136],[76.15325,35.9264],[76.14913,35.82848],[76.33453,35.84296],[76.50961,35.8908],[76.77323,35.66062],[76.84539,35.67356],[76.96624,35.5932],[77.44277,35.46132],[77.70232,35.46244],[77.80532,35.52058],[78.11664,35.48022],[78.03466,35.3785],[78.00033,35.23954],[78.22692,34.88771],[78.18435,34.7998],[78.27781,34.61484],[78.54964,34.57283],[78.56475,34.50835],[78.74465,34.45174],[79.05364,34.32482],[78.99802,34.3027],[78.91769,34.15452],[78.66225,34.08858],[78.65657,34.03195],[78.73367,34.01121],[78.77349,33.73871],[78.67599,33.66445],[78.73636,33.56521],[79.15252,33.17156],[79.14016,33.02545],[79.46562,32.69668],[79.26768,32.53277],[79.13174,32.47766],[79.0979,32.38051],[78.99322,32.37948],[78.96713,32.33655],[78.7831,32.46873],[78.73916,32.69438],[78.38897,32.53938],[78.4645,32.45367],[78.49609,32.2762],[78.68754,32.10256],[78.74404,32.00384],[78.78036,31.99478],[78.69933,31.78723],[78.84516,31.60631],[78.71032,31.50197],[78.77898,31.31209],[79.01931,31.42817],[79.14016,31.43403],[79.22805,31.34963],[79.59884,30.93943],[79.93255,30.88288],[80.20721,30.58541],[80.54504,30.44936],[80.83343,30.32023],[81.03953,30.20059],[81.12842,30.01395],[81.24362,30.0126],[81.29032,30.08806],[81.2623,30.14596],[81.33355,30.15303],[81.39928,30.21862],[81.41018,30.42153],[81.62033,30.44703],[81.99082,30.33423],[82.10135,30.35439],[82.10757,30.23745],[82.19475,30.16884],[82.16984,30.0692],[82.38622,30.02608],[82.5341,29.9735],[82.73024,29.81695],[83.07116,29.61957],[83.28131,29.56813],[83.44787,29.30513],[83.63156,29.16249],[83.82303,29.30513],[83.97559,29.33091],[84.18107,29.23451],[84.24801,29.02783],[84.2231,28.89571],[84.47528,28.74023],[84.62317,28.73887],[84.85511,28.58041],[85.06059,28.68562],[85.19135,28.62825],[85.18668,28.54076],[85.10729,28.34092],[85.38127,28.28336],[85.4233,28.32996],[85.59765,28.30529],[85.60854,28.25045],[85.69105,28.38475],[85.71907,28.38064],[85.74864,28.23126],[85.84672,28.18187],[85.90743,28.05144],[85.97813,27.99023],[85.94946,27.9401],[86.06309,27.90021],[86.12069,27.93047],[86.08333,28.02121],[86.088,28.09264],[86.18607,28.17364],[86.22966,27.9786],[86.42736,27.91122],[86.51609,27.96623],[86.56265,28.09569],[86.74181,28.10638],[86.75582,28.04182],[87.03757,27.94835],[87.11696,27.84104],[87.56996,27.84517],[87.72718,27.80938],[87.82681,27.95248],[88.13378,27.88015],[88.1278,27.95417],[88.25332,27.9478],[88.54858,28.06057],[88.63235,28.12356],[88.83559,28.01936],[88.88091,27.85192],[88.77517,27.45415],[88.82981,27.38814],[88.91901,27.32483],[88.93678,27.33777],[88.96947,27.30319],[89.00216,27.32532],[88.95355,27.4106],[88.97213,27.51671],[89.0582,27.60985],[89.12825,27.62502],[89.59525,28.16433],[89.79762,28.23979],[90.13387,28.19178],[90.58842,28.02838],[90.69894,28.07784],[91.20019,27.98715],[91.25779,28.07509],[91.46327,28.0064],[91.48973,27.93903],[91.5629,27.84823],[91.6469,27.76358],[91.84722,27.76325],[91.87057,27.7195],[92.27432,27.89077],[92.32101,27.79363],[92.42538,27.80092],[92.7275,27.98662],[92.73025,28.05814],[92.65472,28.07632],[92.67486,28.15018],[92.93075,28.25671],[93.14635,28.37035],[93.18069,28.50319],[93.44621,28.67189],[93.72797,28.68821],[94.35897,29.01965],[94.2752,29.11687],[94.69318,29.31739],[94.81353,29.17804],[95.0978,29.14446],[95.11291,29.09527],[95.2214,29.10727],[95.26122,29.07727],[95.3038,29.13847],[95.41091,29.13007],[95.50842,29.13487],[95.72086,29.20797],[95.75149,29.32063],[95.84899,29.31464],[96.05361,29.38167],[96.31316,29.18643],[96.18682,29.11087],[96.20467,29.02325],[96.3626,29.10607],[96.61391,28.72742],[96.40929,28.51526],[96.48895,28.42955],[96.6455,28.61657],[96.85561,28.4875],[96.88445,28.39452],[96.98882,28.32564],[97.1289,28.3619],[97.34547,28.21385],[97.41729,28.29783],[97.47085,28.2688],[97.50518,28.49716],[97.56835,28.55628],[97.70705,28.5056],[97.79632,28.33168],[97.90069,28.3776],[98.15337,28.12114],[98.13964,27.9478],[98.32641,27.51385],[98.42529,27.55404],[98.43353,27.67086],[98.69582,27.56499],[98.7333,26.85615],[98.77547,26.61994],[98.72741,26.36183],[98.67797,26.24487],[98.7329,26.17218],[98.66884,26.09165],[98.63128,26.15492],[98.57085,26.11547],[98.60763,26.01512],[98.70818,25.86241],[98.63128,25.79937],[98.54064,25.85129],[98.40606,25.61129],[98.31268,25.55307],[98.25774,25.6051],[98.16848,25.62739],[98.18084,25.56298],[98.12591,25.50722],[98.14925,25.41547],[97.92541,25.20815],[97.83614,25.2715],[97.77023,25.11492],[97.72216,25.08508],[97.72903,24.91332],[97.79949,24.85655],[97.76481,24.8289],[97.73127,24.83015],[97.70181,24.84557],[97.64354,24.79171],[97.56648,24.76475],[97.56383,24.75535],[97.5542,24.74943],[97.54675,24.74202],[97.56525,24.72838],[97.56286,24.54535],[97.52757,24.43748],[97.60029,24.4401],[97.66998,24.45288],[97.7098,24.35658],[97.65624,24.33781],[97.66723,24.30027],[97.71941,24.29652],[97.76799,24.26365],[97.72998,24.2302],[97.72799,24.18883],[97.75305,24.16902],[97.72903,24.12606],[97.62363,24.00506],[97.5247,23.94032],[97.64667,23.84574],[97.72302,23.89288],[97.79456,23.94836],[97.79416,23.95663],[97.84328,23.97603],[97.86545,23.97723],[97.88811,23.97446],[97.8955,23.97758],[97.89676,23.97931],[97.89683,23.98389],[97.88814,23.98605],[97.88414,23.99405],[97.88616,24.00463],[97.90998,24.02094],[97.93951,24.01953],[97.98691,24.03897],[97.99583,24.04932],[98.04709,24.07616],[98.05302,24.07408],[98.05671,24.07961],[98.0607,24.07812],[98.06703,24.08028],[98.07806,24.07988],[98.20666,24.11406],[98.54476,24.13119],[98.59256,24.08371],[98.85319,24.13042],[98.87998,24.15624],[98.89632,24.10612],[98.67797,23.9644],[98.68209,23.80492],[98.79607,23.77947],[98.82933,23.72921],[98.81775,23.694],[98.88396,23.59555],[98.80294,23.5345],[98.82877,23.47908],[98.87683,23.48995],[98.92104,23.36946],[98.87573,23.33038],[98.93958,23.31414],[98.92515,23.29535],[98.88597,23.18656],[99.05975,23.16382],[99.04601,23.12215],[99.25741,23.09025],[99.34127,23.13099],[99.52214,23.08218],[99.54218,22.90014],[99.43537,22.94086],[99.45654,22.85726],[99.31243,22.73893],[99.38247,22.57544],[99.37972,22.50188],[99.28771,22.4105],[99.17318,22.18025],[99.19176,22.16983],[99.1552,22.15874],[99.33166,22.09656],[99.47585,22.13345],[99.85351,22.04183],[99.96612,22.05965],[99.99084,21.97053],[99.94003,21.82782],[99.98654,21.71064],[100.04956,21.66843],[100.12679,21.70539],[100.17486,21.65306],[100.10757,21.59945],[100.12542,21.50365],[100.1625,21.48704],[100.18447,21.51898],[100.25863,21.47043],[100.35201,21.53176],[100.42892,21.54325],[100.4811,21.46148],[100.57861,21.45637],[100.72143,21.51898],[100.87265,21.67396],[101.11744,21.77659],[101.15156,21.56129],[101.2124,21.56422],[101.19349,21.41959],[101.26912,21.36482],[101.2229,21.23271],[101.29326,21.17254],[101.54563,21.25668],[101.6068,21.23329],[101.59491,21.18621],[101.60886,21.17947],[101.66977,21.20004],[101.70548,21.14911],[101.7622,21.14813],[101.79266,21.19025],[101.76745,21.21571],[101.83887,21.20983],[101.84412,21.25291],[101.74014,21.30967],[101.74224,21.48276],[101.7727,21.51794],[101.7475,21.5873],[101.80001,21.57461],[101.83257,21.61562],[101.74555,21.72852],[101.7791,21.83019],[101.62566,21.96574],[101.57525,22.13026],[101.60675,22.13513],[101.53638,22.24794],[101.56789,22.28876],[101.61306,22.27515],[101.68973,22.46843],[101.7685,22.50337],[101.86828,22.38397],[101.90714,22.38688],[101.91344,22.44417],[101.98487,22.42766],[102.03633,22.46164],[102.1245,22.43372],[102.14099,22.40092],[102.16621,22.43336],[102.26428,22.41321],[102.25339,22.4607],[102.41061,22.64184],[102.38415,22.67919],[102.42618,22.69212],[102.46665,22.77108],[102.51802,22.77969],[102.57095,22.7036],[102.60675,22.73376],[102.8636,22.60735],[102.9321,22.48659],[103.0722,22.44775],[103.07843,22.50097],[103.17961,22.55705],[103.15782,22.59873],[103.18895,22.64471],[103.28079,22.68063],[103.32282,22.8127],[103.43179,22.75816],[103.43646,22.70648],[103.52675,22.59155],[103.57812,22.65764],[103.56255,22.69499],[103.64506,22.79979],[103.87904,22.56683],[103.93286,22.52703],[103.94513,22.52553],[103.95191,22.5134],[103.96352,22.50584],[103.96783,22.51173],[103.97384,22.50634],[103.99247,22.51958],[104.01088,22.51823],[104.03734,22.72945],[104.11384,22.80363],[104.27084,22.8457],[104.25683,22.76534],[104.35593,22.69353],[104.47225,22.75813],[104.58122,22.85571],[104.60457,22.81841],[104.65283,22.83419],[104.72755,22.81984],[104.77114,22.90017],[104.84942,22.93631],[104.86765,22.95178],[104.8334,23.01484],[104.79478,23.12934],[104.87382,23.12854],[104.87992,23.17141],[104.91435,23.18666],[104.9486,23.17235],[104.96532,23.20463],[104.98712,23.19176],[105.07002,23.26248],[105.11672,23.25247],[105.17276,23.28679],[105.22569,23.27249],[105.32376,23.39684],[105.40782,23.28107],[105.42805,23.30824],[105.49966,23.20669],[105.56037,23.16806],[105.57594,23.075],[105.72382,23.06641],[105.8726,22.92756],[105.90119,22.94168],[105.99568,22.94178],[106.00179,22.99049],[106.19705,22.98475],[106.27022,22.87722],[106.34961,22.86718],[106.49749,22.91164],[106.51306,22.94891],[106.55976,22.92311],[106.60179,22.92884],[106.6516,22.86862],[106.6734,22.89587],[106.71387,22.88296],[106.71128,22.85982],[106.78422,22.81532],[106.81271,22.8226],[106.83685,22.8098],[106.82404,22.7881],[106.76293,22.73491],[106.72321,22.63606],[106.71698,22.58432],[106.65316,22.5757],[106.61269,22.60301],[106.58395,22.474],[106.55665,22.46498],[106.57221,22.37],[106.55976,22.34841],[106.6516,22.33977],[106.69986,22.22309],[106.67495,22.1885],[106.6983,22.15102],[106.70142,22.02409],[106.68274,21.99811],[106.69276,21.96013],[106.72551,21.97923],[106.74345,22.00965],[106.81038,21.97934],[106.9178,21.97357],[106.92714,21.93459],[106.97228,21.92592],[106.99252,21.95191],[107.05634,21.92303],[107.06101,21.88982],[107.00964,21.85948],[107.02615,21.81981],[107.10771,21.79879],[107.20734,21.71493],[107.24625,21.7077],[107.29296,21.74674],[107.35834,21.6672],[107.35989,21.60063],[107.38636,21.59774],[107.41593,21.64839],[107.47197,21.6672],[107.49532,21.62958],[107.49065,21.59774],[107.54047,21.5934],[107.56537,21.61945],[107.66967,21.60787],[107.80355,21.66141],[107.86114,21.65128],[107.90006,21.5905],[107.92652,21.58906],[107.95232,21.5388],[107.96774,21.53601],[107.97074,21.54072],[107.97383,21.53961],[107.97932,21.54503],[108.02926,21.54997],[108.0569,21.53604],[108.10003,21.47338],[108.00365,17.98159],[111.60491,13.57105],[118.41371,24.06775],[118.11703,24.39734],[118.28244,24.51231],[118.35291,24.51645],[118.42453,24.54644],[118.56434,24.49266],[120.49232,25.22863],[121.03532,26.8787],[123.5458,31.01942],[122.29378,31.76513],[122.80525,33.30571],[123.85601,37.49093],[123.90497,38.79949],[124.17532,39.8232],[124.23201,39.9248],[124.35029,39.95639],[124.37089,40.03004],[124.3322,40.05573],[124.38556,40.11047],[124.40719,40.13655],[124.86913,40.45387],[125.71172,40.85223],[125.76869,40.87908],[126.00335,40.92835],[126.242,41.15454],[126.53189,41.35206],[126.60631,41.65565],[126.90729,41.79955],[127.17841,41.59714],[127.29712,41.49473],[127.92943,41.44291],[128.02633,41.42103],[128.03311,41.39232],[128.12967,41.37931],[128.18546,41.41279],[128.20061,41.40895],[128.30716,41.60322],[128.15119,41.74568],[128.04487,42.01769],[128.94007,42.03537],[128.96068,42.06657],[129.15178,42.17224],[129.22285,42.26491],[129.22423,42.3553],[129.28541,42.41574],[129.42882,42.44702],[129.54701,42.37254],[129.60482,42.44461],[129.72541,42.43739],[129.75294,42.59409],[129.77183,42.69435],[129.7835,42.76521],[129.80719,42.79218],[129.83277,42.86746],[129.85261,42.96494],[129.8865,43.00395],[129.95082,43.01051],[129.96409,42.97306],[130.12957,42.98361],[130.09764,42.91425],[130.26095,42.9027],[130.23068,42.80125],[130.2385,42.71127],[130.41826,42.6011],[130.44361,42.54849],[130.50123,42.61636],[130.55143,42.52158],[130.62107,42.58413],[130.56576,42.68925],[130.40213,42.70788],[130.44361,42.76205],[130.66524,42.84753],[131.02438,42.86518],[131.02668,42.91246],[131.135,42.94114],[131.10274,43.04734],[131.20414,43.13654],[131.19031,43.21385],[131.30324,43.39498],[131.29402,43.46695],[131.19492,43.53047],[131.21105,43.82383],[131.26176,43.94011],[131.23583,43.96085],[131.25484,44.03131],[131.30365,44.04262],[131.1108,44.70266],[130.95639,44.85154],[131.48415,44.99513],[131.68466,45.12374],[131.66852,45.2196],[131.76532,45.22609],[131.86903,45.33636],[131.99417,45.2567],[132.83978,45.05916],[132.96373,45.0212],[133.12293,45.1332],[133.09279,45.25693],[133.19419,45.51913],[133.41083,45.57723],[133.48457,45.86203],[133.60442,45.90053],[133.67569,45.9759],[133.72695,46.05576],[133.68047,46.14697],[133.88097,46.25066],[133.91496,46.4274],[133.84104,46.46681],[134.03538,46.75668],[134.20016,47.33458],[134.50898,47.4812],[134.7671,47.72051],[134.55508,47.98651],[134.67098,48.1564],[134.75328,48.36763],[134.49516,48.42884],[132.66989,47.96491],[132.57309,47.71741],[131.90448,47.68011],[131.2635,47.73325],[131.09871,47.6852],[130.95985,47.6957],[130.90915,47.90623],[130.65103,48.10052],[130.84462,48.30942],[130.52147,48.61745],[130.66946,48.88251],[130.43232,48.90844],[130.2355,48.86741],[129.85416,49.11067],[129.67598,49.29596],[129.50685,49.42398],[129.40398,49.44194],[129.35317,49.3481],[129.23232,49.40353],[129.11153,49.36813],[128.72896,49.58676],[127.83476,49.5748],[127.53516,49.84306],[127.49299,50.01251],[127.60515,50.23503],[127.37384,50.28393],[127.36009,50.43787],[127.28765,50.46585],[127.36335,50.58306],[127.28165,50.72075],[127.14586,50.91152],[126.93135,51.0841],[126.90369,51.3238],[126.68349,51.70607],[126.44606,51.98254],[126.558,52.13738],[125.6131,53.07229]],[[113.56865,22.20973],[113.57123,22.20416],[113.60504,22.20464],[113.63011,22.10782],[113.57191,22.07696],[113.54839,22.10909],[113.54942,22.14519],[113.54093,22.15497],[113.52659,22.18271],[113.53552,22.20607],[113.53301,22.21235],[113.53591,22.21369],[113.54093,22.21314],[113.54333,22.21688],[113.5508,22.21672],[113.56865,22.20973]],[[114.50148,22.15017],[113.92195,22.13873],[113.83338,22.1826],[113.81621,22.2163],[113.86771,22.42972],[114.03113,22.5065],[114.05438,22.5026],[114.05729,22.51104],[114.06272,22.51617],[114.07267,22.51855],[114.07817,22.52997],[114.08606,22.53276],[114.09048,22.53716],[114.09692,22.53435],[114.1034,22.5352],[114.11181,22.52878],[114.11656,22.53415],[114.12665,22.54003],[114.13823,22.54319],[114.1482,22.54091],[114.15123,22.55163],[114.1597,22.56041],[114.17247,22.55944],[114.18338,22.55444],[114.20655,22.55706],[114.22185,22.55343],[114.22888,22.5436],[114.25154,22.55977],[114.44998,22.55977],[114.50148,22.15017]]]]}},{type:"Feature",properties:{iso1A2:"CO",iso1A3:"COL",iso1N3:"170",wikidata:"Q739",nameEn:"Colombia",groups:["005","419","019"],callingCodes:["57"]},geometry:{type:"MultiPolygon",coordinates:[[[[-71.19849,12.65801],[-81.58685,18.0025],[-82.06974,14.49418],[-82.56142,11.91792],[-78.79327,9.93766],[-77.58292,9.22278],[-77.32389,8.81247],[-77.45064,8.49991],[-77.17257,7.97422],[-77.57185,7.51147],[-77.72514,7.72348],[-77.72157,7.47612],[-77.81426,7.48319],[-77.89178,7.22681],[-78.06168,7.07793],[-82.12561,4.00341],[-78.87137,1.47457],[-78.42749,1.15389],[-77.85677,0.80197],[-77.7148,0.85003],[-77.68613,0.83029],[-77.66416,0.81604],[-77.67815,0.73863],[-77.49984,0.64476],[-77.52001,0.40782],[-76.89177,0.24736],[-76.4094,0.24015],[-76.41215,0.38228],[-76.23441,0.42294],[-75.82927,0.09578],[-75.25764,-0.11943],[-75.18513,-0.0308],[-74.42701,-0.50218],[-74.26675,-0.97229],[-73.65312,-1.26222],[-72.92587,-2.44514],[-71.75223,-2.15058],[-70.94377,-2.23142],[-70.04609,-2.73906],[-70.71396,-3.7921],[-70.52393,-3.87553],[-70.3374,-3.79505],[-69.94708,-4.2431],[-69.43395,-1.42219],[-69.4215,-1.01853],[-69.59796,-0.75136],[-69.603,-0.51947],[-70.03658,-0.19681],[-70.04162,0.55437],[-69.47696,0.71065],[-69.20976,0.57958],[-69.14422,0.84172],[-69.26017,1.06856],[-69.82987,1.07864],[-69.83491,1.69353],[-69.53746,1.76408],[-69.38621,1.70865],[-68.18128,1.72881],[-68.26699,1.83463],[-68.18632,2.00091],[-67.9292,1.82455],[-67.40488,2.22258],[-67.299,1.87494],[-67.15784,1.80439],[-67.08222,1.17441],[-66.85795,1.22998],[-67.21967,2.35778],[-67.65696,2.81691],[-67.85862,2.79173],[-67.85862,2.86727],[-67.30945,3.38393],[-67.50067,3.75812],[-67.62671,3.74303],[-67.85358,4.53249],[-67.83341,5.31104],[-67.59141,5.5369],[-67.63914,5.64963],[-67.58558,5.84537],[-67.43513,5.98835],[-67.4625,6.20625],[-67.60654,6.2891],[-69.41843,6.1072],[-70.10716,6.96516],[-70.7596,7.09799],[-71.03941,6.98163],[-71.37234,7.01588],[-71.42212,7.03854],[-71.44118,7.02116],[-71.82441,7.04314],[-72.04895,7.03837],[-72.19437,7.37034],[-72.43132,7.40034],[-72.47415,7.48928],[-72.45321,7.57232],[-72.47827,7.65604],[-72.46763,7.79518],[-72.44454,7.86031],[-72.46183,7.90682],[-72.45806,7.91141],[-72.47042,7.92306],[-72.48183,7.92909],[-72.48801,7.94329],[-72.47213,7.96106],[-72.39137,8.03534],[-72.35163,8.01163],[-72.36987,8.19976],[-72.4042,8.36513],[-72.65474,8.61428],[-72.77415,9.10165],[-72.94052,9.10663],[-73.02119,9.27584],[-73.36905,9.16636],[-72.98085,9.85253],[-72.88002,10.44309],[-72.4767,11.1117],[-72.24983,11.14138],[-71.9675,11.65536],[-71.3275,11.85],[-70.92579,11.96275],[-71.19849,12.65801]]]]}},{type:"Feature",properties:{iso1A2:"CP",iso1A3:"CPT",wikidata:"Q161258",nameEn:"Clipperton Island",country:"FR",isoStatus:"excRes"},geometry:{type:"MultiPolygon",coordinates:[[[[-110.36279,9.79626],[-108.755,9.84085],[-109.04145,11.13245],[-110.36279,9.79626]]]]}},{type:"Feature",properties:{iso1A2:"CR",iso1A3:"CRI",iso1N3:"188",wikidata:"Q800",nameEn:"Costa Rica",groups:["013","003","419","019"],callingCodes:["506"]},geometry:{type:"MultiPolygon",coordinates:[[[[-83.68276,11.01562],[-83.66597,10.79916],[-83.90838,10.71161],[-84.68197,11.07568],[-84.92439,10.9497],[-85.60529,11.22607],[-85.71223,11.06868],[-86.14524,11.09059],[-87.41779,5.02401],[-82.94503,7.93865],[-82.89978,8.04083],[-82.89137,8.05755],[-82.88641,8.10219],[-82.9388,8.26634],[-83.05209,8.33394],[-82.93056,8.43465],[-82.8679,8.44042],[-82.8382,8.48117],[-82.83322,8.52464],[-82.83975,8.54755],[-82.82739,8.60153],[-82.8794,8.6981],[-82.92068,8.74832],[-82.91377,8.774],[-82.88253,8.83331],[-82.72126,8.97125],[-82.93516,9.07687],[-82.93516,9.46741],[-82.84871,9.4973],[-82.87919,9.62645],[-82.77206,9.59573],[-82.66667,9.49746],[-82.61345,9.49881],[-82.56507,9.57279],[-82.51044,9.65379],[-83.54024,10.96805],[-83.68276,11.01562]]]]}},{type:"Feature",properties:{iso1A2:"CU",iso1A3:"CUB",iso1N3:"192",wikidata:"Q241",nameEn:"Cuba",groups:["029","003","419","019"],callingCodes:["53"]},geometry:{type:"MultiPolygon",coordinates:[[[[-73.62304,20.6935],[-82.02215,24.23074],[-85.77883,21.92705],[-74.81171,18.82201],[-73.62304,20.6935]]]]}},{type:"Feature",properties:{iso1A2:"CV",iso1A3:"CPV",iso1N3:"132",wikidata:"Q1011",nameEn:"Cape Verde",groups:["011","202","002"],callingCodes:["238"]},geometry:{type:"MultiPolygon",coordinates:[[[[-28.81604,14.57305],[-20.39702,14.12816],[-23.37101,19.134],[-28.81604,14.57305]]]]}},{type:"Feature",properties:{iso1A2:"CW",iso1A3:"CUW",iso1N3:"531",wikidata:"Q25279",nameEn:"Curaçao",country:"NL",groups:["029","003","419","019"],callingCodes:["599"]},geometry:{type:"MultiPolygon",coordinates:[[[[-68.90012,12.62309],[-69.59009,12.46019],[-68.99639,11.79035],[-68.33524,11.78151],[-68.90012,12.62309]]]]}},{type:"Feature",properties:{iso1A2:"CX",iso1A3:"CXR",iso1N3:"162",wikidata:"Q31063",nameEn:"Christmas Island",country:"AU",groups:["053","009"],driveSide:"left",callingCodes:["61"]},geometry:{type:"MultiPolygon",coordinates:[[[[105.66835,-9.31927],[104.67494,-11.2566],[106.66176,-11.14349],[105.66835,-9.31927]]]]}},{type:"Feature",properties:{iso1A2:"CY",iso1A3:"CYP",iso1N3:"196",wikidata:"Q229",nameEn:"Cyprus",groups:["EU","145","142"],driveSide:"left",callingCodes:["357"]},geometry:{type:"MultiPolygon",coordinates:[[[[33.70639,34.99303],[33.71514,35.00294],[33.69731,35.01754],[33.69938,35.03123],[33.67678,35.03866],[33.67742,35.05963],[33.68474,35.06602],[33.69095,35.06237],[33.70861,35.07644],[33.7161,35.07279],[33.70209,35.04882],[33.71482,35.03722],[33.73824,35.05321],[33.76106,35.04253],[33.78581,35.05104],[33.82067,35.07826],[33.84168,35.06823],[33.8541,35.07201],[33.87479,35.08881],[33.87097,35.09389],[33.87622,35.10457],[33.87224,35.12293],[33.88561,35.12449],[33.88943,35.12007],[33.88737,35.11408],[33.89853,35.11377],[33.91789,35.08688],[33.91299,35.07579],[33.90247,35.07686],[33.89485,35.06873],[33.88367,35.07877],[33.85261,35.0574],[33.8355,35.05777],[33.82051,35.0667],[33.8012,35.04786],[33.81524,35.04192],[33.83055,35.02865],[33.82875,35.01685],[33.84045,35.00616],[33.85216,35.00579],[33.85891,35.001],[33.85621,34.98956],[33.83505,34.98108],[33.84811,34.97075],[33.86432,34.97592],[33.90075,34.96623],[33.98684,34.76642],[35.48515,34.70851],[35.51152,36.10954],[32.82353,35.70297],[30.15137,34.08517],[32.74412,34.43926],[32.75515,34.64985],[32.76136,34.68318],[32.79433,34.67883],[32.82717,34.70622],[32.86014,34.70585],[32.86167,34.68734],[32.9068,34.66102],[32.91398,34.67343],[32.93043,34.67091],[32.92807,34.66736],[32.93449,34.66241],[32.93693,34.67027],[32.94379,34.67111],[32.94683,34.67907],[32.95539,34.68471],[32.99135,34.68061],[32.98668,34.67268],[32.99014,34.65518],[32.97736,34.65277],[32.97079,34.66112],[32.95325,34.66462],[32.94796,34.6587],[32.94976,34.65204],[32.95471,34.64528],[32.95323,34.64075],[32.95891,34.62919],[32.96718,34.63446],[32.96968,34.64046],[33.0138,34.64424],[33.26744,34.49942],[33.83531,34.73974],[33.70575,34.97947],[33.70639,34.99303]]],[[[33.74144,35.01053],[33.7492,35.01319],[33.74983,35.02274],[33.74265,35.02329],[33.73781,35.02181],[33.7343,35.01178],[33.74144,35.01053]]],[[[33.77312,34.9976],[33.75994,35.00113],[33.75682,34.99916],[33.76605,34.99543],[33.76738,34.99188],[33.7778,34.98981],[33.77843,34.988],[33.78149,34.98854],[33.78318,34.98699],[33.78571,34.98951],[33.78917,34.98854],[33.79191,34.98914],[33.78516,34.99582],[33.77553,34.99518],[33.77312,34.9976]]]]}},{type:"Feature",properties:{iso1A2:"CZ",iso1A3:"CZE",iso1N3:"203",wikidata:"Q213",nameEn:"Czechia",groups:["EU","151","150"],callingCodes:["420"]},geometry:{type:"MultiPolygon",coordinates:[[[[14.82803,50.86966],[14.79139,50.81438],[14.70661,50.84096],[14.61993,50.86049],[14.63434,50.8883],[14.65259,50.90513],[14.64802,50.93241],[14.58024,50.91443],[14.56374,50.922],[14.59702,50.96148],[14.59908,50.98685],[14.58215,50.99306],[14.56432,51.01008],[14.53438,51.00374],[14.53321,51.01679],[14.49873,51.02242],[14.50809,51.0427],[14.49991,51.04692],[14.49154,51.04382],[14.49202,51.02286],[14.45827,51.03712],[14.41335,51.02086],[14.30098,51.05515],[14.25665,50.98935],[14.28776,50.97718],[14.32353,50.98556],[14.32793,50.97379],[14.30251,50.96606],[14.31422,50.95243],[14.39848,50.93866],[14.38691,50.89907],[14.30098,50.88448],[14.27123,50.89386],[14.24314,50.88761],[14.22331,50.86049],[14.02982,50.80662],[13.98864,50.8177],[13.89113,50.78533],[13.89444,50.74142],[13.82942,50.7251],[13.76316,50.73487],[13.70204,50.71771],[13.65977,50.73096],[13.52474,50.70394],[13.53748,50.67654],[13.5226,50.64721],[13.49742,50.63133],[13.46413,50.60102],[13.42189,50.61243],[13.37485,50.64931],[13.37805,50.627],[13.32264,50.60317],[13.32594,50.58009],[13.29454,50.57904],[13.25158,50.59268],[13.19043,50.50237],[13.13424,50.51709],[13.08301,50.50132],[13.0312,50.50944],[13.02038,50.4734],[13.02147,50.44763],[12.98433,50.42016],[12.94058,50.40944],[12.82465,50.45738],[12.73476,50.43237],[12.73044,50.42268],[12.70731,50.39948],[12.67261,50.41949],[12.51356,50.39694],[12.48747,50.37278],[12.49214,50.35228],[12.48256,50.34784],[12.46643,50.35527],[12.43722,50.33774],[12.43371,50.32506],[12.39924,50.32302],[12.40158,50.29521],[12.36594,50.28289],[12.35425,50.23993],[12.33263,50.24367],[12.32445,50.20442],[12.33847,50.19432],[12.32596,50.17146],[12.29232,50.17524],[12.28063,50.19544],[12.28755,50.22429],[12.23943,50.24594],[12.24791,50.25525],[12.26953,50.25189],[12.25119,50.27079],[12.20823,50.2729],[12.18013,50.32146],[12.10907,50.32041],[12.13716,50.27396],[12.09287,50.25032],[12.19335,50.19997],[12.21484,50.16399],[12.1917,50.13434],[12.2073,50.10315],[12.23709,50.10213],[12.27433,50.0771],[12.26111,50.06331],[12.30798,50.05719],[12.49908,49.97305],[12.47264,49.94222],[12.55197,49.92094],[12.48256,49.83575],[12.46603,49.78882],[12.40489,49.76321],[12.4462,49.70233],[12.52553,49.68415],[12.53544,49.61888],[12.56188,49.6146],[12.60155,49.52887],[12.64782,49.52565],[12.64121,49.47628],[12.669,49.42935],[12.71227,49.42363],[12.75854,49.3989],[12.78168,49.34618],[12.88414,49.33541],[12.88249,49.35479],[12.94859,49.34079],[13.03618,49.30417],[13.02957,49.27399],[13.05883,49.26259],[13.17665,49.16713],[13.17019,49.14339],[13.20405,49.12303],[13.23689,49.11412],[13.28242,49.1228],[13.39479,49.04812],[13.40802,48.98851],[13.50221,48.93752],[13.50552,48.97441],[13.58319,48.96899],[13.61624,48.9462],[13.67739,48.87886],[13.73854,48.88538],[13.76994,48.83537],[13.78977,48.83319],[13.8096,48.77877],[13.84023,48.76988],[14.06151,48.66873],[14.01482,48.63788],[14.09104,48.5943],[14.20691,48.5898],[14.33909,48.55852],[14.43076,48.58855],[14.4587,48.64695],[14.56139,48.60429],[14.60808,48.62881],[14.66762,48.58215],[14.71794,48.59794],[14.72756,48.69502],[14.80584,48.73489],[14.80821,48.77711],[14.81545,48.7874],[14.94773,48.76268],[14.95641,48.75915],[14.9758,48.76857],[14.98112,48.77524],[14.9782,48.7766],[14.98032,48.77959],[14.95072,48.79101],[14.98917,48.90082],[14.97612,48.96983],[14.99878,49.01444],[15.15534,48.99056],[15.16358,48.94278],[15.26177,48.95766],[15.28305,48.98831],[15.34823,48.98444],[15.48027,48.94481],[15.51357,48.91549],[15.61622,48.89541],[15.6921,48.85973],[15.75341,48.8516],[15.78087,48.87644],[15.84404,48.86921],[16.06034,48.75436],[16.37345,48.729],[16.40915,48.74576],[16.46134,48.80865],[16.67008,48.77699],[16.68518,48.7281],[16.71883,48.73806],[16.79779,48.70998],[16.90354,48.71541],[16.93955,48.60371],[17.00215,48.70887],[17.11202,48.82925],[17.19355,48.87602],[17.29054,48.85546],[17.3853,48.80936],[17.45671,48.85004],[17.5295,48.81117],[17.7094,48.86721],[17.73126,48.87885],[17.77944,48.92318],[17.87831,48.92679],[17.91814,49.01784],[18.06885,49.03157],[18.1104,49.08624],[18.15022,49.24518],[18.18456,49.28909],[18.36446,49.3267],[18.4139,49.36517],[18.4084,49.40003],[18.44686,49.39467],[18.54848,49.47059],[18.53063,49.49022],[18.57183,49.51162],[18.6144,49.49824],[18.67757,49.50895],[18.74761,49.492],[18.84521,49.51672],[18.84786,49.5446],[18.80479,49.6815],[18.72838,49.68163],[18.69817,49.70473],[18.62676,49.71983],[18.62943,49.74603],[18.62645,49.75002],[18.61368,49.75426],[18.61278,49.7618],[18.57183,49.83334],[18.60341,49.86256],[18.57045,49.87849],[18.57697,49.91565],[18.54299,49.92537],[18.54495,49.9079],[18.53423,49.89906],[18.41604,49.93498],[18.33562,49.94747],[18.33278,49.92415],[18.31914,49.91565],[18.27794,49.93863],[18.27107,49.96779],[18.21752,49.97309],[18.20241,49.99958],[18.10628,50.00223],[18.07898,50.04535],[18.03212,50.06574],[18.00396,50.04954],[18.04585,50.03311],[18.04585,50.01194],[18.00191,50.01723],[17.86886,49.97452],[17.77669,50.02253],[17.7506,50.07896],[17.6888,50.12037],[17.66683,50.10275],[17.59404,50.16437],[17.70528,50.18812],[17.76296,50.23382],[17.72176,50.25665],[17.74648,50.29966],[17.69292,50.32859],[17.67764,50.28977],[17.58889,50.27837],[17.3702,50.28123],[17.34548,50.2628],[17.34273,50.32947],[17.27681,50.32246],[17.19991,50.3654],[17.19579,50.38817],[17.14498,50.38117],[17.1224,50.39494],[16.89229,50.45117],[16.85933,50.41093],[16.90877,50.38642],[16.94448,50.31281],[16.99803,50.30316],[17.02138,50.27772],[16.99803,50.25753],[17.02825,50.23118],[17.00353,50.21449],[16.98018,50.24172],[16.8456,50.20834],[16.7014,50.09659],[16.63137,50.1142],[16.55446,50.16613],[16.56407,50.21009],[16.42674,50.32509],[16.39379,50.3207],[16.3622,50.34875],[16.36495,50.37679],[16.30289,50.38292],[16.28118,50.36891],[16.22821,50.41054],[16.21585,50.40627],[16.19526,50.43291],[16.31413,50.50274],[16.34572,50.49575],[16.44597,50.58041],[16.33611,50.66579],[16.23174,50.67101],[16.20839,50.63096],[16.10265,50.66405],[16.02437,50.60046],[15.98317,50.61528],[16.0175,50.63009],[15.97219,50.69799],[15.87331,50.67188],[15.81683,50.75666],[15.73186,50.73885],[15.43798,50.80833],[15.3803,50.77187],[15.36656,50.83956],[15.2773,50.8907],[15.27043,50.97724],[15.2361,50.99886],[15.1743,50.9833],[15.16744,51.01959],[15.11937,50.99021],[15.10152,51.01095],[15.06218,51.02269],[15.03895,51.0123],[15.02433,51.0242],[14.96419,50.99108],[15.01088,50.97984],[14.99852,50.86817],[14.82803,50.86966]]]]}},{type:"Feature",properties:{iso1A2:"DE",iso1A3:"DEU",iso1N3:"276",wikidata:"Q183",nameEn:"Germany",groups:["EU","155","150"],callingCodes:["49"]},geometry:{type:"MultiPolygon",coordinates:[[[[8.70847,47.68904],[8.71773,47.69088],[8.70237,47.71453],[8.66416,47.71367],[8.67508,47.6979],[8.65769,47.68928],[8.66837,47.68437],[8.68985,47.69552],[8.70847,47.68904]]],[[[8.72617,47.69651],[8.72809,47.69282],[8.75856,47.68969],[8.79511,47.67462],[8.79966,47.70222],[8.76965,47.7075],[8.77309,47.72059],[8.80663,47.73821],[8.82002,47.71458],[8.86989,47.70504],[8.85065,47.68209],[8.87383,47.67045],[8.87625,47.65441],[8.89946,47.64769],[8.94093,47.65596],[9.02093,47.6868],[9.09891,47.67801],[9.13845,47.66389],[9.15181,47.66904],[9.1705,47.65513],[9.1755,47.65584],[9.17593,47.65399],[9.18203,47.65598],[9.25619,47.65939],[9.55125,47.53629],[9.72736,47.53457],[9.76748,47.5934],[9.80254,47.59419],[9.82591,47.58158],[9.8189,47.54688],[9.87499,47.52953],[9.87733,47.54688],[9.92407,47.53111],[9.96029,47.53899],[10.00003,47.48216],[10.03859,47.48927],[10.07131,47.45531],[10.09001,47.46005],[10.1052,47.4316],[10.06897,47.40709],[10.09819,47.35724],[10.11805,47.37228],[10.16362,47.36674],[10.17648,47.38889],[10.2127,47.38019],[10.22774,47.38904],[10.23757,47.37609],[10.19998,47.32832],[10.2147,47.31014],[10.17648,47.29149],[10.17531,47.27167],[10.23257,47.27088],[10.33424,47.30813],[10.39851,47.37623],[10.4324,47.38494],[10.4359,47.41183],[10.47446,47.43318],[10.46278,47.47901],[10.44291,47.48453],[10.4324,47.50111],[10.44992,47.5524],[10.43473,47.58394],[10.47329,47.58552],[10.48849,47.54057],[10.56912,47.53584],[10.60337,47.56755],[10.63456,47.5591],[10.68832,47.55752],[10.6965,47.54253],[10.7596,47.53228],[10.77596,47.51729],[10.88814,47.53701],[10.91268,47.51334],[10.86945,47.5015],[10.87061,47.4786],[10.90918,47.48571],[10.93839,47.48018],[10.92437,47.46991],[10.98513,47.42882],[10.97111,47.41617],[10.97111,47.39561],[11.11835,47.39719],[11.12536,47.41222],[11.20482,47.43198],[11.25157,47.43277],[11.22002,47.3964],[11.27844,47.39956],[11.29597,47.42566],[11.33804,47.44937],[11.4175,47.44621],[11.38128,47.47465],[11.4362,47.51413],[11.52618,47.50939],[11.58578,47.52281],[11.58811,47.55515],[11.60681,47.57881],[11.63934,47.59202],[11.84052,47.58354],[11.85572,47.60166],[12.0088,47.62451],[12.02282,47.61033],[12.05788,47.61742],[12.13734,47.60639],[12.17824,47.61506],[12.18145,47.61019],[12.17737,47.60121],[12.18568,47.6049],[12.20398,47.60667],[12.20801,47.61082],[12.19895,47.64085],[12.18507,47.65984],[12.18347,47.66663],[12.16769,47.68167],[12.16217,47.70105],[12.18303,47.70065],[12.22571,47.71776],[12.2542,47.7433],[12.26238,47.73544],[12.24017,47.69534],[12.26004,47.67725],[12.27991,47.68827],[12.336,47.69534],[12.37222,47.68433],[12.43883,47.6977],[12.44117,47.6741],[12.50076,47.62293],[12.53816,47.63553],[12.57438,47.63238],[12.6071,47.6741],[12.7357,47.6787],[12.77777,47.66689],[12.76492,47.64485],[12.82101,47.61493],[12.77427,47.58025],[12.80699,47.54477],[12.84672,47.54556],[12.85256,47.52741],[12.9624,47.47452],[12.98344,47.48716],[12.9998,47.46267],[13.04537,47.49426],[13.03252,47.53373],[13.05355,47.56291],[13.04537,47.58183],[13.06641,47.58577],[13.06407,47.60075],[13.09562,47.63304],[13.07692,47.68814],[13.01382,47.72116],[12.98578,47.7078],[12.92969,47.71094],[12.91333,47.7178],[12.90274,47.72513],[12.91711,47.74026],[12.9353,47.74788],[12.94371,47.76281],[12.93202,47.77302],[12.96311,47.79957],[12.98543,47.82896],[13.00588,47.84374],[12.94163,47.92927],[12.93886,47.94046],[12.93642,47.94436],[12.93419,47.94063],[12.92668,47.93879],[12.91985,47.94069],[12.9211,47.95135],[12.91683,47.95647],[12.87476,47.96195],[12.8549,48.01122],[12.76141,48.07373],[12.74973,48.10885],[12.7617,48.12796],[12.78595,48.12445],[12.80676,48.14979],[12.82673,48.15245],[12.8362,48.15876],[12.836,48.1647],[12.84475,48.16556],[12.87126,48.20318],[12.95306,48.20629],[13.02083,48.25689],[13.0851,48.27711],[13.126,48.27867],[13.18093,48.29577],[13.26039,48.29422],[13.30897,48.31575],[13.40709,48.37292],[13.43929,48.43386],[13.42527,48.45711],[13.45727,48.51092],[13.43695,48.55776],[13.45214,48.56472],[13.46967,48.55157],[13.50663,48.57506],[13.50131,48.58091],[13.51291,48.59023],[13.57535,48.55912],[13.59705,48.57013],[13.62508,48.55501],[13.65186,48.55092],[13.66113,48.53558],[13.72802,48.51208],[13.74816,48.53058],[13.7513,48.5624],[13.76921,48.55324],[13.80519,48.58026],[13.80038,48.59487],[13.82609,48.62345],[13.81901,48.6761],[13.81283,48.68426],[13.81791,48.69832],[13.79337,48.71375],[13.81863,48.73257],[13.82266,48.75544],[13.84023,48.76988],[13.8096,48.77877],[13.78977,48.83319],[13.76994,48.83537],[13.73854,48.88538],[13.67739,48.87886],[13.61624,48.9462],[13.58319,48.96899],[13.50552,48.97441],[13.50221,48.93752],[13.40802,48.98851],[13.39479,49.04812],[13.28242,49.1228],[13.23689,49.11412],[13.20405,49.12303],[13.17019,49.14339],[13.17665,49.16713],[13.05883,49.26259],[13.02957,49.27399],[13.03618,49.30417],[12.94859,49.34079],[12.88249,49.35479],[12.88414,49.33541],[12.78168,49.34618],[12.75854,49.3989],[12.71227,49.42363],[12.669,49.42935],[12.64121,49.47628],[12.64782,49.52565],[12.60155,49.52887],[12.56188,49.6146],[12.53544,49.61888],[12.52553,49.68415],[12.4462,49.70233],[12.40489,49.76321],[12.46603,49.78882],[12.48256,49.83575],[12.55197,49.92094],[12.47264,49.94222],[12.49908,49.97305],[12.30798,50.05719],[12.26111,50.06331],[12.27433,50.0771],[12.23709,50.10213],[12.2073,50.10315],[12.1917,50.13434],[12.21484,50.16399],[12.19335,50.19997],[12.09287,50.25032],[12.13716,50.27396],[12.10907,50.32041],[12.18013,50.32146],[12.20823,50.2729],[12.25119,50.27079],[12.26953,50.25189],[12.24791,50.25525],[12.23943,50.24594],[12.28755,50.22429],[12.28063,50.19544],[12.29232,50.17524],[12.32596,50.17146],[12.33847,50.19432],[12.32445,50.20442],[12.33263,50.24367],[12.35425,50.23993],[12.36594,50.28289],[12.40158,50.29521],[12.39924,50.32302],[12.43371,50.32506],[12.43722,50.33774],[12.46643,50.35527],[12.48256,50.34784],[12.49214,50.35228],[12.48747,50.37278],[12.51356,50.39694],[12.67261,50.41949],[12.70731,50.39948],[12.73044,50.42268],[12.73476,50.43237],[12.82465,50.45738],[12.94058,50.40944],[12.98433,50.42016],[13.02147,50.44763],[13.02038,50.4734],[13.0312,50.50944],[13.08301,50.50132],[13.13424,50.51709],[13.19043,50.50237],[13.25158,50.59268],[13.29454,50.57904],[13.32594,50.58009],[13.32264,50.60317],[13.37805,50.627],[13.37485,50.64931],[13.42189,50.61243],[13.46413,50.60102],[13.49742,50.63133],[13.5226,50.64721],[13.53748,50.67654],[13.52474,50.70394],[13.65977,50.73096],[13.70204,50.71771],[13.76316,50.73487],[13.82942,50.7251],[13.89444,50.74142],[13.89113,50.78533],[13.98864,50.8177],[14.02982,50.80662],[14.22331,50.86049],[14.24314,50.88761],[14.27123,50.89386],[14.30098,50.88448],[14.38691,50.89907],[14.39848,50.93866],[14.31422,50.95243],[14.30251,50.96606],[14.32793,50.97379],[14.32353,50.98556],[14.28776,50.97718],[14.25665,50.98935],[14.30098,51.05515],[14.41335,51.02086],[14.45827,51.03712],[14.49202,51.02286],[14.49154,51.04382],[14.49991,51.04692],[14.50809,51.0427],[14.49873,51.02242],[14.53321,51.01679],[14.53438,51.00374],[14.56432,51.01008],[14.58215,50.99306],[14.59908,50.98685],[14.59702,50.96148],[14.56374,50.922],[14.58024,50.91443],[14.64802,50.93241],[14.65259,50.90513],[14.63434,50.8883],[14.61993,50.86049],[14.70661,50.84096],[14.79139,50.81438],[14.82803,50.86966],[14.81664,50.88148],[14.89681,50.9422],[14.89252,50.94999],[14.92942,50.99744],[14.95529,51.04552],[14.97938,51.07742],[14.98229,51.11354],[14.99689,51.12205],[14.99079,51.14284],[14.99646,51.14365],[15.00083,51.14974],[14.99414,51.15813],[14.99311,51.16249],[15.0047,51.16874],[15.01242,51.21285],[15.04288,51.28387],[14.98008,51.33449],[14.96899,51.38367],[14.9652,51.44793],[14.94749,51.47155],[14.73219,51.52922],[14.72652,51.53902],[14.73047,51.54606],[14.71125,51.56209],[14.7727,51.61263],[14.75759,51.62318],[14.75392,51.67445],[14.69065,51.70842],[14.66386,51.73282],[14.64625,51.79472],[14.60493,51.80473],[14.59089,51.83302],[14.6588,51.88359],[14.6933,51.9044],[14.70601,51.92944],[14.7177,51.94048],[14.72163,51.95188],[14.71836,51.95606],[14.7139,51.95643],[14.70488,51.97679],[14.71339,52.00337],[14.76026,52.06624],[14.72971,52.09167],[14.6917,52.10283],[14.67683,52.13936],[14.70616,52.16927],[14.68344,52.19612],[14.71319,52.22144],[14.70139,52.25038],[14.58149,52.28007],[14.56378,52.33838],[14.55228,52.35264],[14.54423,52.42568],[14.63056,52.48993],[14.60081,52.53116],[14.6289,52.57136],[14.61073,52.59847],[14.22071,52.81175],[14.13806,52.82392],[14.12256,52.84311],[14.15873,52.87715],[14.14056,52.95786],[14.25954,53.00264],[14.35044,53.05829],[14.38679,53.13669],[14.36696,53.16444],[14.37853,53.20405],[14.40662,53.21098],[14.45125,53.26241],[14.44133,53.27427],[14.4215,53.27724],[14.35209,53.49506],[14.3273,53.50587],[14.30416,53.55499],[14.31904,53.61581],[14.2853,53.63392],[14.28477,53.65955],[14.27133,53.66613],[14.2836,53.67721],[14.26782,53.69866],[14.27249,53.74464],[14.21323,53.8664],[14.20823,53.90776],[14.18544,53.91258],[14.20647,53.91671],[14.22634,53.9291],[14.20811,54.12784],[13.93395,54.84044],[12.85844,54.82438],[11.90309,54.38543],[11.00303,54.63689],[10.31111,54.65968],[10.16755,54.73883],[9.89314,54.84171],[9.73563,54.8247],[9.61187,54.85548],[9.62734,54.88057],[9.58937,54.88785],[9.4659,54.83131],[9.43155,54.82586],[9.41213,54.84254],[9.38532,54.83968],[9.36496,54.81749],[9.33849,54.80233],[9.32771,54.80602],[9.2474,54.8112],[9.23445,54.83432],[9.24631,54.84726],[9.20571,54.85841],[9.14275,54.87421],[9.04629,54.87249],[8.92795,54.90452],[8.81178,54.90518],[8.76387,54.8948],[8.63979,54.91069],[8.55769,54.91837],[8.45719,55.06747],[8.02459,55.09613],[5.45168,54.20039],[6.91025,53.44221],[7.00198,53.32672],[7.19052,53.31866],[7.21679,53.20058],[7.22681,53.18165],[7.17898,53.13817],[7.21694,53.00742],[7.07253,52.81083],[7.04557,52.63318],[6.77307,52.65375],[6.71641,52.62905],[6.69507,52.488],[6.94293,52.43597],[6.99041,52.47235],[7.03417,52.40237],[7.07044,52.37805],[7.02703,52.27941],[7.06365,52.23789],[7.03729,52.22695],[6.9897,52.2271],[6.97189,52.20329],[6.83984,52.11728],[6.76117,52.11895],[6.68128,52.05052],[6.83035,51.9905],[6.82357,51.96711],[6.72319,51.89518],[6.68386,51.91861],[6.58556,51.89386],[6.50231,51.86313],[6.47179,51.85395],[6.38815,51.87257],[6.40704,51.82771],[6.30593,51.84998],[6.29872,51.86801],[6.21443,51.86801],[6.15349,51.90439],[6.11551,51.89769],[6.16902,51.84094],[6.10337,51.84829],[6.06705,51.86136],[5.99848,51.83195],[5.94568,51.82786],[5.98665,51.76944],[5.95003,51.7493],[6.04091,51.71821],[6.02767,51.6742],[6.11759,51.65609],[6.09055,51.60564],[6.18017,51.54096],[6.21724,51.48568],[6.20654,51.40049],[6.22641,51.39948],[6.22674,51.36135],[6.16977,51.33169],[6.07889,51.24432],[6.07889,51.17038],[6.17384,51.19589],[6.16706,51.15677],[5.98292,51.07469],[5.9541,51.03496],[5.9134,51.06736],[5.86735,51.05182],[5.87849,51.01969],[5.90493,51.00198],[5.90296,50.97356],[5.95282,50.98728],[6.02697,50.98303],[6.01615,50.93367],[6.09297,50.92066],[6.07486,50.89307],[6.08805,50.87223],[6.07693,50.86025],[6.07431,50.84674],[6.05702,50.85179],[6.05623,50.8572],[6.01921,50.84435],[6.02328,50.81694],[6.00462,50.80065],[5.98404,50.80988],[5.97497,50.79992],[6.02624,50.77453],[6.01976,50.75398],[6.03889,50.74618],[6.0326,50.72647],[6.0406,50.71848],[6.04428,50.72861],[6.11707,50.72231],[6.17852,50.6245],[6.26957,50.62444],[6.2476,50.60392],[6.24888,50.59869],[6.24005,50.58732],[6.22581,50.5907],[6.20281,50.56952],[6.17739,50.55875],[6.17802,50.54179],[6.19735,50.53576],[6.19579,50.5313],[6.18716,50.52653],[6.19193,50.5212],[6.20599,50.52089],[6.22335,50.49578],[6.26637,50.50272],[6.30809,50.50058],[6.3465,50.48833],[6.34005,50.46083],[6.37219,50.45397],[6.36852,50.40776],[6.34406,50.37994],[6.3688,50.35898],[6.40785,50.33557],[6.40641,50.32425],[6.35701,50.31139],[6.32488,50.32333],[6.29949,50.30887],[6.28797,50.27458],[6.208,50.25179],[6.16853,50.2234],[6.18364,50.20815],[6.18739,50.1822],[6.14588,50.17106],[6.14132,50.14971],[6.15298,50.14126],[6.1379,50.12964],[6.12055,50.09171],[6.11274,50.05916],[6.13458,50.04141],[6.13044,50.02929],[6.14666,50.02207],[6.13794,50.01466],[6.13273,50.02019],[6.1295,50.01849],[6.13806,50.01056],[6.14948,50.00908],[6.14147,49.99563],[6.1701,49.98518],[6.16466,49.97086],[6.17872,49.9537],[6.18554,49.95622],[6.18045,49.96611],[6.19089,49.96991],[6.19856,49.95053],[6.22094,49.94955],[6.22608,49.929],[6.21882,49.92403],[6.22926,49.92096],[6.23496,49.89972],[6.26146,49.88203],[6.28874,49.87592],[6.29692,49.86685],[6.30963,49.87021],[6.32303,49.85133],[6.32098,49.83728],[6.33585,49.83785],[6.34267,49.84974],[6.36576,49.85032],[6.40022,49.82029],[6.42521,49.81591],[6.42905,49.81091],[6.44131,49.81443],[6.45425,49.81164],[6.47111,49.82263],[6.48718,49.81267],[6.50647,49.80916],[6.51215,49.80124],[6.52121,49.81338],[6.53122,49.80666],[6.52169,49.79787],[6.50534,49.78952],[6.51669,49.78336],[6.51056,49.77515],[6.51828,49.76855],[6.51646,49.75961],[6.50174,49.75292],[6.50193,49.73291],[6.51805,49.72425],[6.51397,49.72058],[6.50261,49.72718],[6.49535,49.72645],[6.49694,49.72205],[6.5042,49.71808],[6.50647,49.71353],[6.49785,49.71118],[6.48014,49.69767],[6.46048,49.69092],[6.44654,49.67799],[6.42937,49.66857],[6.42726,49.66078],[6.43768,49.66021],[6.4413,49.65722],[6.41861,49.61723],[6.39822,49.60081],[6.385,49.59946],[6.37464,49.58886],[6.38342,49.5799],[6.38024,49.57593],[6.36676,49.57813],[6.35825,49.57053],[6.38228,49.55855],[6.38072,49.55171],[6.35666,49.52931],[6.36788,49.50377],[6.36907,49.48931],[6.36778,49.46937],[6.38352,49.46463],[6.39168,49.4667],[6.40274,49.46546],[6.42432,49.47683],[6.55404,49.42464],[6.533,49.40748],[6.60091,49.36864],[6.58807,49.35358],[6.572,49.35027],[6.60186,49.31055],[6.66583,49.28065],[6.69274,49.21661],[6.71843,49.2208],[6.73256,49.20486],[6.71137,49.18808],[6.73765,49.16375],[6.78265,49.16793],[6.83385,49.15162],[6.84703,49.15734],[6.86225,49.18185],[6.85016,49.19354],[6.85119,49.20038],[6.83555,49.21249],[6.85939,49.22376],[6.89298,49.20863],[6.91875,49.22261],[6.93831,49.2223],[6.94028,49.21641],[6.95963,49.203],[6.97273,49.2099],[7.01318,49.19018],[7.03459,49.19096],[7.0274,49.17042],[7.03178,49.15734],[7.04662,49.13724],[7.04409,49.12123],[7.04843,49.11422],[7.05548,49.11185],[7.06642,49.11415],[7.07162,49.1255],[7.09007,49.13094],[7.07859,49.15031],[7.10715,49.15631],[7.10384,49.13787],[7.12504,49.14253],[7.1358,49.1282],[7.1593,49.1204],[7.23473,49.12971],[7.29514,49.11426],[7.3195,49.14231],[7.35995,49.14399],[7.3662,49.17308],[7.44052,49.18354],[7.44455,49.16765],[7.49473,49.17],[7.49172,49.13915],[7.53012,49.09818],[7.56416,49.08136],[7.62575,49.07654],[7.63618,49.05428],[7.75948,49.04562],[7.79557,49.06583],[7.86386,49.03499],[7.93641,49.05544],[7.97783,49.03161],[8.14189,48.97833],[8.22604,48.97352],[8.20031,48.95856],[8.19989,48.95825],[8.12813,48.87985],[8.10253,48.81829],[8.06802,48.78957],[8.0326,48.79017],[8.01534,48.76085],[7.96994,48.75606],[7.96812,48.72491],[7.89002,48.66317],[7.84098,48.64217],[7.80057,48.5857],[7.80167,48.54758],[7.80647,48.51239],[7.76833,48.48945],[7.73109,48.39192],[7.74562,48.32736],[7.69022,48.30018],[7.6648,48.22219],[7.57137,48.12292],[7.56966,48.03265],[7.62302,47.97898],[7.55673,47.87371],[7.52921,47.77747],[7.54761,47.72912],[7.53722,47.71635],[7.51266,47.70197],[7.51915,47.68335],[7.52067,47.66437],[7.53384,47.65115],[7.5591,47.63849],[7.57423,47.61628],[7.58851,47.60794],[7.59301,47.60058],[7.58945,47.59017],[7.60523,47.58519],[7.60459,47.57869],[7.61929,47.57683],[7.64309,47.59151],[7.64213,47.5944],[7.64599,47.59695],[7.67395,47.59212],[7.68229,47.59905],[7.69385,47.60099],[7.68486,47.59601],[7.67115,47.5871],[7.68904,47.57133],[7.67655,47.56435],[7.63338,47.56256],[7.65083,47.54662],[7.66174,47.54554],[7.6656,47.53752],[7.68101,47.53232],[7.69642,47.53297],[7.71961,47.54219],[7.75261,47.54599],[7.79486,47.55691],[7.81901,47.58798],[7.84412,47.5841],[7.88664,47.58854],[7.90673,47.57674],[7.91251,47.55031],[7.94494,47.54511],[7.95682,47.55789],[7.97581,47.55493],[8.00113,47.55616],[8.02136,47.55096],[8.04383,47.55443],[8.06663,47.56374],[8.08557,47.55768],[8.10002,47.56504],[8.10395,47.57918],[8.11543,47.5841],[8.13662,47.58432],[8.13823,47.59147],[8.14947,47.59558],[8.1652,47.5945],[8.19378,47.61636],[8.20617,47.62141],[8.22011,47.6181],[8.22577,47.60385],[8.23809,47.61204],[8.25863,47.61571],[8.26313,47.6103],[8.2824,47.61225],[8.29722,47.60603],[8.29524,47.5919],[8.30277,47.58607],[8.32735,47.57133],[8.35512,47.57014],[8.38273,47.56608],[8.39477,47.57826],[8.43235,47.56617],[8.49431,47.58107],[8.48949,47.588],[8.46637,47.58389],[8.45578,47.60121],[8.50747,47.61897],[8.51686,47.63476],[8.55756,47.62394],[8.57586,47.59537],[8.60348,47.61204],[8.59545,47.64298],[8.60701,47.65271],[8.61471,47.64514],[8.60412,47.63735],[8.62049,47.63757],[8.62884,47.65098],[8.61113,47.66332],[8.6052,47.67258],[8.57683,47.66158],[8.56141,47.67088],[8.52801,47.66059],[8.5322,47.64687],[8.49656,47.64709],[8.46605,47.64103],[8.4667,47.65747],[8.44711,47.65379],[8.42264,47.66667],[8.41346,47.66676],[8.40473,47.67499],[8.4211,47.68407],[8.40569,47.69855],[8.44807,47.72426],[8.45771,47.7493],[8.48868,47.77215],[8.56814,47.78001],[8.56415,47.80633],[8.61657,47.79998],[8.62408,47.7626],[8.64425,47.76398],[8.65292,47.80066],[8.68022,47.78599],[8.68985,47.75686],[8.71778,47.76571],[8.74251,47.75168],[8.70543,47.73121],[8.73671,47.7169],[8.72617,47.69651]]]]}},{type:"Feature",properties:{iso1A2:"DG",iso1A3:"DGA",wikidata:"Q184851",nameEn:"Diego Garcia",country:"GB",groups:["IO","014","202","002"],isoStatus:"excRes",callingCodes:["246"]},geometry:{type:"MultiPolygon",coordinates:[[[[73.14823,-7.76302],[73.09982,-6.07324],[71.43792,-7.73904],[73.14823,-7.76302]]]]}},{type:"Feature",properties:{iso1A2:"DJ",iso1A3:"DJI",iso1N3:"262",wikidata:"Q977",nameEn:"Djibouti",groups:["014","202","002"],callingCodes:["253"]},geometry:{type:"MultiPolygon",coordinates:[[[[43.42425,11.70983],[43.90659,12.3823],[43.32909,12.59711],[43.29075,12.79154],[42.86195,12.58747],[42.7996,12.42629],[42.6957,12.36201],[42.46941,12.52661],[42.4037,12.46478],[41.95461,11.81157],[41.82878,11.72361],[41.77727,11.49902],[41.8096,11.33606],[41.80056,10.97127],[42.06302,10.92599],[42.13691,10.97586],[42.42669,10.98493],[42.62989,11.09711],[42.75111,11.06992],[42.79037,10.98493],[42.95776,10.98533],[43.42425,11.70983]]]]}},{type:"Feature",properties:{iso1A2:"DK",iso1A3:"DNK",iso1N3:"208",wikidata:"Q35",nameEn:"Denmark",groups:["EU","154","150"],callingCodes:["45"]},geometry:{type:"MultiPolygon",coordinates:[[[[12.16597,56.60205],[10.40861,58.38489],[7.28637,57.35913],[8.02459,55.09613],[8.45719,55.06747],[8.55769,54.91837],[8.63979,54.91069],[8.76387,54.8948],[8.81178,54.90518],[8.92795,54.90452],[9.04629,54.87249],[9.14275,54.87421],[9.20571,54.85841],[9.24631,54.84726],[9.23445,54.83432],[9.2474,54.8112],[9.32771,54.80602],[9.33849,54.80233],[9.36496,54.81749],[9.38532,54.83968],[9.41213,54.84254],[9.43155,54.82586],[9.4659,54.83131],[9.58937,54.88785],[9.62734,54.88057],[9.61187,54.85548],[9.73563,54.8247],[9.89314,54.84171],[10.16755,54.73883],[10.31111,54.65968],[11.00303,54.63689],[11.90309,54.38543],[12.85844,54.82438],[13.93395,54.84044],[15.36991,54.73263],[15.79951,55.54655],[14.89259,55.5623],[14.28399,55.1553],[12.84405,55.13257],[12.60345,55.42675],[12.88472,55.63369],[12.6372,55.91371],[12.65312,56.04345],[12.07466,56.29488],[12.16597,56.60205]]]]}},{type:"Feature",properties:{iso1A2:"DM",iso1A3:"DMA",iso1N3:"212",wikidata:"Q784",nameEn:"Dominica",groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 767"]},geometry:{type:"MultiPolygon",coordinates:[[[[-61.51867,14.96709],[-60.69955,15.22234],[-60.95725,15.70997],[-61.44899,15.79571],[-61.81728,15.58058],[-61.51867,14.96709]]]]}},{type:"Feature",properties:{iso1A2:"DO",iso1A3:"DOM",iso1N3:"214",wikidata:"Q786",nameEn:"Dominican Republic",groups:["029","003","419","019"],callingCodes:["1 809","1 829","1 849"]},geometry:{type:"MultiPolygon",coordinates:[[[[-67.87844,21.7938],[-72.38946,20.27111],[-71.77419,19.73128],[-71.75865,19.70231],[-71.7429,19.58445],[-71.71449,19.55364],[-71.71268,19.53374],[-71.6802,19.45008],[-71.69448,19.37866],[-71.77766,19.33823],[-71.73229,19.26686],[-71.62642,19.21212],[-71.65337,19.11759],[-71.69938,19.10916],[-71.71088,19.08353],[-71.74088,19.0437],[-71.88102,18.95007],[-71.77766,18.95007],[-71.72624,18.87802],[-71.71885,18.78423],[-71.82556,18.62551],[-71.95412,18.64939],[-72.00201,18.62312],[-71.88102,18.50125],[-71.90875,18.45821],[-71.69952,18.34101],[-71.78271,18.18302],[-71.75465,18.14405],[-71.74994,18.11115],[-71.73783,18.07177],[-71.75671,18.03456],[-72.29523,17.48026],[-68.39466,16.14167],[-67.87844,21.7938]]]]}},{type:"Feature",properties:{iso1A2:"DZ",iso1A3:"DZA",iso1N3:"012",wikidata:"Q262",nameEn:"Algeria",groups:["015","002"],callingCodes:["213"]},geometry:{type:"MultiPolygon",coordinates:[[[[8.59123,37.14286],[2.46645,37.97429],[-2.27707,35.35051],[-2.21248,35.08532],[-2.21445,35.04378],[-2.04734,34.93218],[-1.97833,34.93218],[-1.97469,34.886],[-1.73707,34.74226],[-1.84569,34.61907],[-1.69788,34.48056],[-1.78042,34.39018],[-1.64666,34.10405],[-1.73494,33.71721],[-1.59508,33.59929],[-1.67067,33.27084],[-1.46249,33.0499],[-1.54244,32.95499],[-1.37794,32.73628],[-0.9912,32.52467],[-1.24998,32.32993],[-1.24453,32.1917],[-1.15735,32.12096],[-1.22829,32.07832],[-2.46166,32.16603],[-2.93873,32.06557],[-2.82784,31.79459],[-3.66314,31.6339],[-3.66386,31.39202],[-3.77647,31.31912],[-3.77103,31.14984],[-3.54944,31.0503],[-3.65418,30.85566],[-3.64735,30.67539],[-4.31774,30.53229],[-4.6058,30.28343],[-5.21671,29.95253],[-5.58831,29.48103],[-5.72121,29.52322],[-5.75616,29.61407],[-6.69965,29.51623],[-6.78351,29.44634],[-6.95824,29.50924],[-7.61585,29.36252],[-8.6715,28.71194],[-8.66879,27.6666],[-8.66674,27.31569],[-4.83423,24.99935],[1.15698,21.12843],[1.20992,20.73533],[3.24648,19.81703],[3.12501,19.1366],[3.36082,18.9745],[4.26651,19.14224],[5.8153,19.45101],[7.38361,20.79165],[7.48273,20.87258],[11.96886,23.51735],[11.62498,24.26669],[11.41061,24.21456],[10.85323,24.5595],[10.33159,24.5465],[10.02432,24.98124],[10.03146,25.35635],[9.38834,26.19288],[9.51696,26.39148],[9.89569,26.57696],[9.78136,29.40961],[9.3876,30.16738],[9.55544,30.23971],[9.07483,32.07865],[8.35999,32.50101],[8.31895,32.83483],[8.1179,33.05086],[8.11433,33.10175],[7.83028,33.18851],[7.73687,33.42114],[7.54088,33.7726],[7.52851,34.06493],[7.66174,34.20167],[7.74207,34.16492],[7.81242,34.21841],[7.86264,34.3987],[8.20482,34.57575],[8.29655,34.72798],[8.25189,34.92009],[8.30727,34.95378],[8.3555,35.10007],[8.47318,35.23376],[8.30329,35.29884],[8.36086,35.47774],[8.35371,35.66373],[8.26472,35.73669],[8.2626,35.91733],[8.40731,36.42208],[8.18936,36.44939],[8.16167,36.48817],[8.47609,36.66607],[8.46537,36.7706],[8.57613,36.78062],[8.67706,36.8364],[8.62972,36.86499],[8.64044,36.9401],[8.59123,37.14286]]]]}},{type:"Feature",properties:{iso1A2:"EA",wikidata:"Q28868874",nameEn:"Ceuta, Melilla",country:"ES",groups:["015","002"],isoStatus:"excRes",callingCodes:["34"]},geometry:{type:"MultiPolygon",coordinates:[[[[-5.38491,35.92591],[-5.37338,35.88417],[-5.35844,35.87375],[-5.34379,35.8711],[-5.27056,35.88794],[-5.27635,35.91222],[-5.38491,35.92591]]],[[[-2.92224,35.3401],[-2.96038,35.31609],[-2.96648,35.30475],[-2.96978,35.29459],[-2.97035,35.28852],[-2.96507,35.28801],[-2.96826,35.28296],[-2.96516,35.27967],[-2.95431,35.2728],[-2.95065,35.26576],[-2.93893,35.26737],[-2.92674,35.27313],[-2.92181,35.28599],[-2.92224,35.3401]]]]}},{type:"Feature",properties:{iso1A2:"EC",iso1A3:"ECU",iso1N3:"218",wikidata:"Q736",nameEn:"Ecuador",groups:["005","419","019"],callingCodes:["593"]},geometry:{type:"MultiPolygon",coordinates:[[[[-75.25764,-0.11943],[-75.82927,0.09578],[-76.23441,0.42294],[-76.41215,0.38228],[-76.4094,0.24015],[-76.89177,0.24736],[-77.52001,0.40782],[-77.49984,0.64476],[-77.67815,0.73863],[-77.66416,0.81604],[-77.68613,0.83029],[-77.7148,0.85003],[-77.85677,0.80197],[-78.42749,1.15389],[-78.87137,1.47457],[-93.12365,2.64343],[-92.46744,-2.52874],[-80.30602,-3.39149],[-80.20647,-3.431],[-80.24123,-3.46124],[-80.24475,-3.47846],[-80.24586,-3.48677],[-80.23651,-3.48652],[-80.22629,-3.501],[-80.20535,-3.51667],[-80.21642,-3.5888],[-80.19848,-3.59249],[-80.18741,-3.63994],[-80.19926,-3.68894],[-80.13232,-3.90317],[-80.46386,-4.01342],[-80.4822,-4.05477],[-80.45023,-4.20938],[-80.32114,-4.21323],[-80.46386,-4.41516],[-80.39256,-4.48269],[-80.13945,-4.29786],[-79.79722,-4.47558],[-79.59402,-4.46848],[-79.26248,-4.95167],[-79.1162,-4.97774],[-79.01659,-5.01481],[-78.85149,-4.66795],[-78.68394,-4.60754],[-78.34362,-3.38633],[-78.24589,-3.39907],[-78.22642,-3.51113],[-78.14324,-3.47653],[-78.19369,-3.36431],[-77.94147,-3.05454],[-76.6324,-2.58397],[-76.05203,-2.12179],[-75.57429,-1.55961],[-75.3872,-0.9374],[-75.22862,-0.95588],[-75.22862,-0.60048],[-75.53615,-0.19213],[-75.60169,-0.18708],[-75.61997,-0.10012],[-75.40192,-0.17196],[-75.25764,-0.11943]]]]}},{type:"Feature",properties:{iso1A2:"EE",iso1A3:"EST",iso1N3:"233",wikidata:"Q191",nameEn:"Estonia",aliases:["EW"],groups:["EU","154","150"],callingCodes:["372"]},geometry:{type:"MultiPolygon",coordinates:[[[[26.32936,60.00121],[20.5104,59.15546],[19.84909,57.57876],[22.80496,57.87798],[23.20055,57.56697],[24.26221,57.91787],[24.3579,57.87471],[25.19484,58.0831],[25.28237,57.98539],[25.29581,58.08288],[25.73499,57.90193],[26.05949,57.84744],[26.0324,57.79037],[26.02456,57.78342],[26.027,57.78158],[26.0266,57.77441],[26.02069,57.77169],[26.02415,57.76865],[26.03332,57.7718],[26.0543,57.76105],[26.08098,57.76619],[26.2029,57.7206],[26.1866,57.6849],[26.29253,57.59244],[26.46527,57.56885],[26.54675,57.51813],[26.90364,57.62823],[27.34698,57.52242],[27.31919,57.57672],[27.40393,57.62125],[27.3746,57.66834],[27.52615,57.72843],[27.50171,57.78842],[27.56689,57.83356],[27.78526,57.83963],[27.81841,57.89244],[27.67282,57.92627],[27.62393,58.09462],[27.48541,58.22615],[27.55489,58.39525],[27.36366,58.78381],[27.74429,58.98351],[27.80482,59.1116],[27.87978,59.18097],[27.90911,59.24353],[28.00689,59.28351],[28.14215,59.28934],[28.19284,59.35791],[28.20537,59.36491],[28.21137,59.38058],[28.19061,59.39962],[28.04187,59.47017],[27.85643,59.58538],[26.90044,59.63819],[26.32936,60.00121]]]]}},{type:"Feature",properties:{iso1A2:"EG",iso1A3:"EGY",iso1N3:"818",wikidata:"Q79",nameEn:"Egypt",groups:["015","002"],callingCodes:["20"]},geometry:{type:"MultiPolygon",coordinates:[[[[33.62659,31.82938],[25.63787,31.9359],[25.14001,31.67534],[25.06041,31.57937],[24.83101,31.31921],[25.01077,30.73861],[24.71117,30.17441],[24.99968,29.24574],[24.99885,21.99535],[33.17563,22.00405],[34.0765,22.00501],[37.8565,22.00903],[34.51305,27.70027],[34.46254,27.99552],[34.88293,29.37455],[34.92298,29.45305],[34.26742,31.21998],[34.24012,31.29591],[34.23572,31.2966],[34.21853,31.32363],[34.052,31.46619],[33.62659,31.82938]]]]}},{type:"Feature",properties:{iso1A2:"EH",iso1A3:"ESH",iso1N3:"732",wikidata:"Q6250",nameEn:"Western Sahara",groups:["015","002"],callingCodes:["212"]},geometry:{type:"MultiPolygon",coordinates:[[[[-8.66879,27.6666],[-8.77527,27.66663],[-8.71787,26.9898],[-9.08698,26.98639],[-9.56957,26.90042],[-9.81998,26.71379],[-10.68417,26.90984],[-11.35695,26.8505],[-11.23622,26.72023],[-11.38635,26.611],[-11.62052,26.05229],[-12.06001,26.04442],[-12.12281,25.13682],[-12.92147,24.39502],[-13.00628,24.01923],[-13.75627,23.77231],[-14.10361,22.75501],[-14.1291,22.41636],[-14.48112,22.00886],[-14.47329,21.63839],[-14.78487,21.36587],[-16.44269,21.39745],[-16.9978,21.36239],[-17.02707,21.34022],[-17.21511,21.34226],[-17.35589,20.80492],[-17.0471,20.76408],[-17.0695,20.85742],[-17.06781,20.92697],[-17.0396,20.9961],[-17.0357,21.05368],[-16.99806,21.12142],[-16.95474,21.33997],[-13.01525,21.33343],[-13.08438,22.53866],[-13.15313,22.75649],[-13.10753,22.89493],[-13.00412,23.02297],[-12.5741,23.28975],[-12.36213,23.3187],[-12.14969,23.41935],[-12.00251,23.4538],[-12.0002,25.9986],[-8.66721,25.99918],[-8.66674,27.31569],[-8.66879,27.6666]]]]}},{type:"Feature",properties:{iso1A2:"ER",iso1A3:"ERI",iso1N3:"232",wikidata:"Q986",nameEn:"Eritrea",groups:["014","202","002"],callingCodes:["291"]},geometry:{type:"MultiPolygon",coordinates:[[[[41.37609,16.19728],[39.63762,18.37348],[38.57727,17.98125],[38.45916,17.87167],[38.37133,17.66269],[38.13362,17.53906],[37.50967,17.32199],[37.42694,17.04041],[36.99777,17.07172],[36.92193,16.23451],[36.76371,15.80831],[36.69761,15.75323],[36.54276,15.23478],[36.44337,15.14963],[36.54376,14.25597],[36.56536,14.26177],[36.55659,14.28237],[36.63364,14.31172],[36.85787,14.32201],[37.01622,14.2561],[37.09486,14.27155],[37.13206,14.40746],[37.3106,14.44657],[37.47319,14.2149],[37.528,14.18413],[37.91287,14.89447],[38.0364,14.72745],[38.25562,14.67287],[38.3533,14.51323],[38.45748,14.41445],[38.78306,14.4754],[38.98058,14.54895],[39.02834,14.63717],[39.16074,14.65187],[39.14772,14.61827],[39.19547,14.56996],[39.23888,14.56365],[39.26927,14.48801],[39.2302,14.44598],[39.2519,14.40393],[39.37685,14.54402],[39.52756,14.49011],[39.50585,14.55735],[39.58182,14.60987],[39.76632,14.54264],[39.9443,14.41024],[40.07236,14.54264],[40.14649,14.53969],[40.21128,14.39342],[40.25686,14.41445],[40.9167,14.11152],[41.25097,13.60787],[41.62864,13.38626],[42.05841,12.80912],[42.21469,12.75832],[42.2798,12.6355],[42.4037,12.46478],[42.46941,12.52661],[42.6957,12.36201],[42.7996,12.42629],[42.86195,12.58747],[43.29075,12.79154],[42.63806,13.58268],[41.29956,15.565],[41.37609,16.19728]]]]}},{type:"Feature",properties:{iso1A2:"ES",iso1A3:"ESP",iso1N3:"724",wikidata:"Q29",nameEn:"Spain",groups:["EU","039","150"],callingCodes:["34"]},geometry:{type:"MultiPolygon",coordinates:[[[[-2.41312,35.17111],[-2.41265,35.1877],[-2.44896,35.18777],[-2.44887,35.17075],[-2.41312,35.17111]]],[[[-3.90602,35.21494],[-3.88926,35.20841],[-3.88617,35.21406],[-3.90288,35.22024],[-3.90602,35.21494]]],[[[-4.30191,35.17419],[-4.30112,35.17058],[-4.29436,35.17149],[-4.30191,35.17419]]],[[[-7.27694,35.93599],[-5.64962,35.93752],[-5.10878,36.05227],[-2.85819,35.63219],[-2.27707,35.35051],[2.46645,37.97429],[5.18061,39.43581],[3.4481,42.4358],[3.17156,42.43545],[3.11379,42.43646],[3.10027,42.42621],[3.08167,42.42748],[3.03734,42.47363],[2.96518,42.46692],[2.94283,42.48174],[2.92107,42.4573],[2.88413,42.45938],[2.86983,42.46843],[2.85675,42.45444],[2.84335,42.45724],[2.77464,42.41046],[2.75497,42.42578],[2.72056,42.42298],[2.65311,42.38771],[2.6747,42.33974],[2.57934,42.35808],[2.55516,42.35351],[2.54382,42.33406],[2.48457,42.33933],[2.43508,42.37568],[2.43299,42.39423],[2.38504,42.39977],[2.25551,42.43757],[2.20578,42.41633],[2.16599,42.42314],[2.12789,42.41291],[2.11621,42.38393],[2.06241,42.35906],[2.00488,42.35399],[1.96482,42.37787],[1.9574,42.42401],[1.94084,42.43039],[1.94061,42.43333],[1.94292,42.44316],[1.93663,42.45439],[1.88853,42.4501],[1.83037,42.48395],[1.76335,42.48863],[1.72515,42.50338],[1.70571,42.48867],[1.66826,42.50779],[1.65674,42.47125],[1.58933,42.46275],[1.57953,42.44957],[1.55937,42.45808],[1.55073,42.43299],[1.5127,42.42959],[1.44529,42.43724],[1.43838,42.47848],[1.41648,42.48315],[1.46661,42.50949],[1.44759,42.54431],[1.41245,42.53539],[1.4234,42.55959],[1.44529,42.56722],[1.42512,42.58292],[1.44197,42.60217],[1.35562,42.71944],[1.15928,42.71407],[1.0804,42.78569],[0.98292,42.78754],[0.96166,42.80629],[0.93089,42.79154],[0.711,42.86372],[0.66121,42.84021],[0.65421,42.75872],[0.67873,42.69458],[0.40214,42.69779],[0.36251,42.72282],[0.29407,42.67431],[0.25336,42.7174],[0.17569,42.73424],[-0.02468,42.68513],[-0.10519,42.72761],[-0.16141,42.79535],[-0.17939,42.78974],[-0.3122,42.84788],[-0.38833,42.80132],[-0.41319,42.80776],[-0.44334,42.79939],[-0.50863,42.82713],[-0.55497,42.77846],[-0.67637,42.88303],[-0.69837,42.87945],[-0.72608,42.89318],[-0.73422,42.91228],[-0.72037,42.92541],[-0.75478,42.96916],[-0.81652,42.95166],[-0.97133,42.96239],[-1.00963,42.99279],[-1.10333,43.0059],[-1.22881,43.05534],[-1.25244,43.04164],[-1.30531,43.06859],[-1.30052,43.09581],[-1.27118,43.11961],[-1.32209,43.1127],[-1.34419,43.09665],[-1.35272,43.02658],[-1.44067,43.047],[-1.47555,43.08372],[-1.41562,43.12815],[-1.3758,43.24511],[-1.40942,43.27272],[-1.45289,43.27049],[-1.50992,43.29481],[-1.55963,43.28828],[-1.57674,43.25269],[-1.61341,43.25269],[-1.63052,43.28591],[-1.62481,43.30726],[-1.69407,43.31378],[-1.73074,43.29481],[-1.7397,43.32979],[-1.75079,43.3317],[-1.75334,43.34107],[-1.77068,43.34396],[-1.78714,43.35476],[-1.78332,43.36399],[-1.79319,43.37497],[-1.77289,43.38957],[-1.81005,43.59738],[-10.14298,44.17365],[-9.14112,41.86623],[-8.87157,41.86488],[-8.81794,41.90375],[-8.75712,41.92833],[-8.74606,41.9469],[-8.7478,41.96282],[-8.69071,41.98862],[-8.6681,41.99703],[-8.65832,42.02972],[-8.64626,42.03668],[-8.63791,42.04691],[-8.59493,42.05708],[-8.58086,42.05147],[-8.54563,42.0537],[-8.5252,42.06264],[-8.52837,42.07658],[-8.48185,42.0811],[-8.44123,42.08218],[-8.42512,42.07199],[-8.40143,42.08052],[-8.38323,42.07683],[-8.36353,42.09065],[-8.33912,42.08358],[-8.32161,42.10218],[-8.29809,42.106],[-8.2732,42.12396],[-8.24681,42.13993],[-8.22406,42.1328],[-8.1986,42.15402],[-8.18947,42.13853],[-8.19406,42.12141],[-8.18178,42.06436],[-8.11729,42.08537],[-8.08847,42.05767],[-8.08796,42.01398],[-8.16232,41.9828],[-8.2185,41.91237],[-8.19551,41.87459],[-8.16944,41.87944],[-8.16455,41.81753],[-8.0961,41.81024],[-8.01136,41.83453],[-7.9804,41.87337],[-7.92336,41.8758],[-7.90707,41.92432],[-7.88751,41.92553],[-7.88055,41.84571],[-7.84188,41.88065],[-7.69848,41.90977],[-7.65774,41.88308],[-7.58603,41.87944],[-7.62188,41.83089],[-7.52737,41.83939],[-7.49803,41.87095],[-7.45566,41.86488],[-7.44759,41.84451],[-7.42854,41.83262],[-7.42864,41.80589],[-7.37092,41.85031],[-7.32366,41.8406],[-7.18677,41.88793],[-7.18549,41.97515],[-7.14115,41.98855],[-7.08574,41.97401],[-7.07596,41.94977],[-7.01078,41.94977],[-6.98144,41.9728],[-6.95537,41.96553],[-6.94396,41.94403],[-6.82174,41.94493],[-6.81196,41.99097],[-6.76959,41.98734],[-6.75004,41.94129],[-6.61967,41.94008],[-6.58544,41.96674],[-6.5447,41.94371],[-6.56752,41.88429],[-6.51374,41.8758],[-6.56426,41.74219],[-6.54633,41.68623],[-6.49907,41.65823],[-6.44204,41.68258],[-6.29863,41.66432],[-6.19128,41.57638],[-6.26777,41.48796],[-6.3306,41.37677],[-6.38553,41.38655],[-6.38551,41.35274],[-6.55937,41.24417],[-6.65046,41.24725],[-6.68286,41.21641],[-6.69711,41.1858],[-6.77319,41.13049],[-6.75655,41.10187],[-6.79241,41.05397],[-6.80942,41.03629],[-6.84781,41.02692],[-6.88843,41.03027],[-6.913,41.03922],[-6.9357,41.02888],[-6.8527,40.93958],[-6.84292,40.89771],[-6.80707,40.88047],[-6.79892,40.84842],[-6.82337,40.84472],[-6.82826,40.74603],[-6.79567,40.65955],[-6.84292,40.56801],[-6.80218,40.55067],[-6.7973,40.51723],[-6.84944,40.46394],[-6.84618,40.42177],[-6.78426,40.36468],[-6.80218,40.33239],[-6.86085,40.2976],[-6.86085,40.26776],[-7.00426,40.23169],[-7.02544,40.18564],[-7.00589,40.12087],[-6.94233,40.10716],[-6.86737,40.01986],[-6.91463,39.86618],[-6.97492,39.81488],[-7.01613,39.66877],[-7.24707,39.66576],[-7.33507,39.64569],[-7.54121,39.66717],[-7.49477,39.58794],[-7.2927,39.45847],[-7.3149,39.34857],[-7.23403,39.27579],[-7.23566,39.20132],[-7.12811,39.17101],[-7.14929,39.11287],[-7.10692,39.10275],[-7.04011,39.11919],[-6.97004,39.07619],[-6.95211,39.0243],[-7.051,38.907],[-7.03848,38.87221],[-7.26174,38.72107],[-7.265,38.61674],[-7.32529,38.44336],[-7.15581,38.27597],[-7.09389,38.17227],[-6.93418,38.21454],[-7.00375,38.01914],[-7.05966,38.01966],[-7.10366,38.04404],[-7.12648,38.00296],[-7.24544,37.98884],[-7.27314,37.90145],[-7.33441,37.81193],[-7.41981,37.75729],[-7.51759,37.56119],[-7.46878,37.47127],[-7.43974,37.38913],[-7.43227,37.25152],[-7.41854,37.23813],[-7.41133,37.20314],[-7.39769,37.16868],[-7.37282,36.96896],[-7.27694,35.93599]],[[-5.28217,36.09907],[-5.3004,36.07439],[-5.32837,36.05935],[-5.36503,36.06205],[-5.39074,36.10278],[-5.40134,36.14896],[-5.38545,36.15481],[-5.36494,36.15496],[-5.34536,36.15501],[-5.33822,36.15272],[-5.27801,36.14942],[-5.28217,36.09907]]],[[[1.99838,42.44682],[2.01564,42.45171],[1.99216,42.46208],[1.98579,42.47486],[1.99766,42.4858],[1.98916,42.49351],[1.98022,42.49569],[1.97697,42.48568],[1.97227,42.48487],[1.97003,42.48081],[1.96215,42.47854],[1.95606,42.45785],[1.96125,42.45364],[1.98378,42.44697],[1.99838,42.44682]]]]}},{type:"Feature",properties:{iso1A2:"ET",iso1A3:"ETH",iso1N3:"231",wikidata:"Q115",nameEn:"Ethiopia",groups:["014","202","002"],callingCodes:["251"]},geometry:{type:"MultiPolygon",coordinates:[[[[42.4037,12.46478],[42.2798,12.6355],[42.21469,12.75832],[42.05841,12.80912],[41.62864,13.38626],[41.25097,13.60787],[40.9167,14.11152],[40.25686,14.41445],[40.21128,14.39342],[40.14649,14.53969],[40.07236,14.54264],[39.9443,14.41024],[39.76632,14.54264],[39.58182,14.60987],[39.50585,14.55735],[39.52756,14.49011],[39.37685,14.54402],[39.2519,14.40393],[39.2302,14.44598],[39.26927,14.48801],[39.23888,14.56365],[39.19547,14.56996],[39.14772,14.61827],[39.16074,14.65187],[39.02834,14.63717],[38.98058,14.54895],[38.78306,14.4754],[38.45748,14.41445],[38.3533,14.51323],[38.25562,14.67287],[38.0364,14.72745],[37.91287,14.89447],[37.528,14.18413],[37.47319,14.2149],[37.3106,14.44657],[37.13206,14.40746],[37.09486,14.27155],[37.01622,14.2561],[36.85787,14.32201],[36.63364,14.31172],[36.55659,14.28237],[36.56536,14.26177],[36.54376,14.25597],[36.44653,13.95666],[36.48824,13.83954],[36.38993,13.56459],[36.24545,13.36759],[36.13374,12.92665],[36.16651,12.88019],[36.14268,12.70879],[36.01458,12.72478],[35.70476,12.67101],[35.24302,11.91132],[35.11492,11.85156],[35.05832,11.71158],[35.09556,11.56278],[34.95704,11.24448],[35.01215,11.19626],[34.93172,10.95946],[34.97789,10.91559],[34.97491,10.86147],[34.86916,10.78832],[34.86618,10.74588],[34.77532,10.69027],[34.77383,10.74588],[34.59062,10.89072],[34.4372,10.781],[34.2823,10.53508],[34.34783,10.23914],[34.32102,10.11599],[34.22718,10.02506],[34.20484,9.9033],[34.13186,9.7492],[34.08717,9.55243],[34.10229,9.50238],[34.14304,9.04654],[34.14453,8.60204],[34.01346,8.50041],[33.89579,8.4842],[33.87195,8.41938],[33.71407,8.3678],[33.66938,8.44442],[33.54575,8.47094],[33.3119,8.45474],[33.19721,8.40317],[33.1853,8.29264],[33.18083,8.13047],[33.08401,8.05822],[33.0006,7.90333],[33.04944,7.78989],[33.24637,7.77939],[33.32531,7.71297],[33.44745,7.7543],[33.71407,7.65983],[33.87642,7.5491],[34.02984,7.36449],[34.03878,7.27437],[34.01495,7.25664],[34.19369,7.12807],[34.19369,7.04382],[34.35753,6.91963],[34.47669,6.91076],[34.53925,6.82794],[34.53776,6.74808],[34.65096,6.72589],[34.77459,6.5957],[34.87736,6.60161],[35.01738,6.46991],[34.96227,6.26415],[35.00546,5.89387],[35.12611,5.68937],[35.13058,5.62118],[35.31188,5.50106],[35.29938,5.34042],[35.50792,5.42431],[35.8576,5.33413],[35.81968,5.10757],[35.82118,4.77382],[35.9419,4.61933],[35.95449,4.53244],[36.03924,4.44406],[36.84474,4.44518],[37.07724,4.33503],[38.14168,3.62487],[38.45812,3.60445],[38.52336,3.62551],[38.91938,3.51198],[39.07736,3.5267],[39.19954,3.47834],[39.49444,3.45521],[39.51551,3.40895],[39.55132,3.39634],[39.58339,3.47434],[39.76808,3.67058],[39.86043,3.86974],[40.77498,4.27683],[41.1754,3.94079],[41.89488,3.97375],[42.07619,4.17667],[42.55853,4.20518],[42.84526,4.28357],[42.97746,4.44032],[43.04177,4.57923],[43.40263,4.79289],[44.02436,4.9451],[44.98104,4.91821],[47.97917,8.00124],[47.92477,8.00111],[46.99339,7.9989],[44.19222,8.93028],[43.32613,9.59205],[43.23518,9.84605],[43.0937,9.90579],[42.87643,10.18441],[42.69452,10.62672],[42.95776,10.98533],[42.79037,10.98493],[42.75111,11.06992],[42.62989,11.09711],[42.42669,10.98493],[42.13691,10.97586],[42.06302,10.92599],[41.80056,10.97127],[41.8096,11.33606],[41.77727,11.49902],[41.82878,11.72361],[41.95461,11.81157],[42.4037,12.46478]]]]}},{type:"Feature",properties:{iso1A2:"EU",iso1A3:"EUE",wikidata:"Q458",nameEn:"European Union",level:"union",isoStatus:"excRes"},geometry:null},{type:"Feature",properties:{iso1A2:"FI",iso1A3:"FIN",iso1N3:"246",wikidata:"Q33",nameEn:"Finland",aliases:["SF"],groups:["EU","154","150"],callingCodes:["358"]},geometry:{type:"MultiPolygon",coordinates:[[[[29.12697,69.69193],[28.36883,69.81658],[28.32849,69.88605],[27.97558,69.99671],[27.95542,70.0965],[27.57226,70.06215],[27.05802,69.92069],[26.64461,69.96565],[26.40261,69.91377],[25.96904,69.68397],[25.69679,69.27039],[25.75729,68.99383],[25.61613,68.89602],[25.42455,68.90328],[25.12206,68.78684],[25.10189,68.63307],[24.93048,68.61102],[24.90023,68.55579],[24.74898,68.65143],[24.18432,68.73936],[24.02299,68.81601],[23.781,68.84514],[23.68017,68.70276],[23.13064,68.64684],[22.53321,68.74393],[22.38367,68.71561],[22.27276,68.89514],[21.63833,69.27485],[21.27827,69.31281],[21.00732,69.22755],[20.98641,69.18809],[21.11099,69.10291],[21.05775,69.0356],[20.72171,69.11874],[20.55258,69.06069],[20.78802,69.03087],[20.91658,68.96764],[20.85104,68.93142],[20.90649,68.89696],[21.03001,68.88969],[22.00429,68.50692],[22.73028,68.40881],[23.10336,68.26551],[23.15377,68.14759],[23.26469,68.15134],[23.40081,68.05545],[23.65793,67.9497],[23.45627,67.85297],[23.54701,67.59306],[23.39577,67.46974],[23.75372,67.43688],[23.75372,67.29914],[23.54701,67.25435],[23.58735,67.20752],[23.56214,67.17038],[23.98563,66.84149],[23.98059,66.79585],[23.89488,66.772],[23.85959,66.56434],[23.63776,66.43568],[23.67591,66.3862],[23.64982,66.30603],[23.71339,66.21299],[23.90497,66.15802],[24.15791,65.85385],[24.14798,65.83466],[24.15107,65.81427],[24.14112,65.39731],[20.15877,63.06556],[19.23413,60.61414],[20.96741,60.71528],[21.15143,60.54555],[21.08159,60.20167],[21.02509,60.12142],[21.35468,59.67511],[20.5104,59.15546],[26.32936,60.00121],[27.44953,60.22766],[27.71177,60.3893],[27.77352,60.52722],[28.47974,60.93365],[28.82816,61.1233],[29.01829,61.17448],[31.10136,62.43042],[31.38369,62.66284],[31.58535,62.91642],[31.29294,63.09035],[31.23244,63.22239],[30.49637,63.46666],[29.98213,63.75795],[30.25437,63.83364],[30.55687,64.09036],[30.4762,64.25728],[30.06279,64.35782],[30.01238,64.57513],[30.12329,64.64862],[30.05271,64.79072],[29.68972,64.80789],[29.61914,65.05993],[29.84096,65.1109],[29.8813,65.22101],[29.61914,65.23791],[29.68972,65.31803],[29.84096,65.56945],[29.74013,65.64025],[29.97205,65.70256],[30.16363,65.66935],[29.91155,66.13863],[28.9839,66.94139],[29.91155,67.51507],[30.02041,67.67523],[29.66955,67.79872],[29.34179,68.06655],[28.62982,68.19816],[28.43941,68.53366],[28.78224,68.86696],[28.45957,68.91417],[28.91738,69.04774],[28.81248,69.11997],[28.8629,69.22395],[29.31664,69.47994],[29.12697,69.69193]]]]}},{type:"Feature",properties:{iso1A2:"FJ",iso1A3:"FJI",iso1N3:"242",wikidata:"Q712",nameEn:"Fiji",groups:["054","009"],driveSide:"left",callingCodes:["679"]},geometry:{type:"MultiPolygon",coordinates:[[[[174,-22.5],[179.99999,-22.5],[179.99999,-11.5],[174,-11.5],[174,-22.5]]],[[[-178.60161,-14.95666],[-180,-14.96041],[-180,-22.90585],[-176.74538,-22.89767],[-176.76826,-14.95183],[-178.60161,-14.95666]]]]}},{type:"Feature",properties:{iso1A2:"FK",iso1A3:"FLK",iso1N3:"238",wikidata:"Q9648",nameEn:"Falkland Islands",country:"GB",groups:["005","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["500"]},geometry:{type:"MultiPolygon",coordinates:[[[[-63.67376,-55.11859],[-54.56126,-51.26248],[-61.26735,-50.63919],[-63.67376,-55.11859]]]]}},{type:"Feature",properties:{iso1A2:"FM",iso1A3:"FSM",iso1N3:"583",wikidata:"Q702",nameEn:"Federated States of Micronesia",groups:["057","009"],roadSpeedUnit:"mph",callingCodes:["691"]},geometry:{type:"MultiPolygon",coordinates:[[[[136.04605,12.45908],[136.27107,6.73747],[156.88247,-1.39237],[165.35175,6.367],[159.04653,10.59067],[136.04605,12.45908]]]]}},{type:"Feature",properties:{iso1A2:"FO",iso1A3:"FRO",iso1N3:"234",wikidata:"Q4628",nameEn:"Faroe Islands",country:"DK",groups:["154","150"],callingCodes:["298"]},geometry:{type:"MultiPolygon",coordinates:[[[[-8.51774,62.35338],[-6.51083,60.95272],[-5.70102,62.77194],[-8.51774,62.35338]]]]}},{type:"Feature",properties:{iso1A2:"FR",iso1A3:"FRA",iso1N3:"250",wikidata:"Q142",nameEn:"France",groups:["EU","155","150"],callingCodes:["33"]},geometry:null},{type:"Feature",properties:{iso1A2:"FX",iso1A3:"FXX",iso1N3:"249",wikidata:"Q212429",nameEn:"Metropolitan France",country:"FR",groups:["EU","155","150"],isoStatus:"excRes",callingCodes:["33"]},geometry:{type:"MultiPolygon",coordinates:[[[[2.55904,51.07014],[2.18458,51.52087],[1.17405,50.74239],[-2.02963,49.91866],[-2.09454,49.46288],[-1.83944,49.23037],[-2.00491,48.86706],[-2.65349,49.15373],[-6.13339,48.73907],[-1.81005,43.59738],[-1.77289,43.38957],[-1.79319,43.37497],[-1.78332,43.36399],[-1.78714,43.35476],[-1.77068,43.34396],[-1.75334,43.34107],[-1.75079,43.3317],[-1.7397,43.32979],[-1.73074,43.29481],[-1.69407,43.31378],[-1.62481,43.30726],[-1.63052,43.28591],[-1.61341,43.25269],[-1.57674,43.25269],[-1.55963,43.28828],[-1.50992,43.29481],[-1.45289,43.27049],[-1.40942,43.27272],[-1.3758,43.24511],[-1.41562,43.12815],[-1.47555,43.08372],[-1.44067,43.047],[-1.35272,43.02658],[-1.34419,43.09665],[-1.32209,43.1127],[-1.27118,43.11961],[-1.30052,43.09581],[-1.30531,43.06859],[-1.25244,43.04164],[-1.22881,43.05534],[-1.10333,43.0059],[-1.00963,42.99279],[-0.97133,42.96239],[-0.81652,42.95166],[-0.75478,42.96916],[-0.72037,42.92541],[-0.73422,42.91228],[-0.72608,42.89318],[-0.69837,42.87945],[-0.67637,42.88303],[-0.55497,42.77846],[-0.50863,42.82713],[-0.44334,42.79939],[-0.41319,42.80776],[-0.38833,42.80132],[-0.3122,42.84788],[-0.17939,42.78974],[-0.16141,42.79535],[-0.10519,42.72761],[-0.02468,42.68513],[0.17569,42.73424],[0.25336,42.7174],[0.29407,42.67431],[0.36251,42.72282],[0.40214,42.69779],[0.67873,42.69458],[0.65421,42.75872],[0.66121,42.84021],[0.711,42.86372],[0.93089,42.79154],[0.96166,42.80629],[0.98292,42.78754],[1.0804,42.78569],[1.15928,42.71407],[1.35562,42.71944],[1.44197,42.60217],[1.47986,42.61346],[1.46718,42.63296],[1.48043,42.65203],[1.50867,42.64483],[1.55418,42.65669],[1.60085,42.62703],[1.63485,42.62957],[1.6625,42.61982],[1.68267,42.62533],[1.73452,42.61515],[1.72588,42.59098],[1.7858,42.57698],[1.73683,42.55492],[1.72515,42.50338],[1.76335,42.48863],[1.83037,42.48395],[1.88853,42.4501],[1.93663,42.45439],[1.94292,42.44316],[1.94061,42.43333],[1.94084,42.43039],[1.9574,42.42401],[1.96482,42.37787],[2.00488,42.35399],[2.06241,42.35906],[2.11621,42.38393],[2.12789,42.41291],[2.16599,42.42314],[2.20578,42.41633],[2.25551,42.43757],[2.38504,42.39977],[2.43299,42.39423],[2.43508,42.37568],[2.48457,42.33933],[2.54382,42.33406],[2.55516,42.35351],[2.57934,42.35808],[2.6747,42.33974],[2.65311,42.38771],[2.72056,42.42298],[2.75497,42.42578],[2.77464,42.41046],[2.84335,42.45724],[2.85675,42.45444],[2.86983,42.46843],[2.88413,42.45938],[2.92107,42.4573],[2.94283,42.48174],[2.96518,42.46692],[3.03734,42.47363],[3.08167,42.42748],[3.10027,42.42621],[3.11379,42.43646],[3.17156,42.43545],[3.4481,42.4358],[7.60802,41.05927],[10.09675,41.44089],[9.56115,43.20816],[7.50102,43.51859],[7.42422,43.72209],[7.40903,43.7296],[7.41113,43.73156],[7.41291,43.73168],[7.41298,43.73311],[7.41233,43.73439],[7.42062,43.73977],[7.42299,43.74176],[7.42443,43.74087],[7.42809,43.74396],[7.43013,43.74895],[7.43624,43.75014],[7.43708,43.75197],[7.4389,43.75151],[7.4379,43.74963],[7.47823,43.73341],[7.53006,43.78405],[7.50423,43.84345],[7.49355,43.86551],[7.51162,43.88301],[7.56075,43.89932],[7.56858,43.94506],[7.60771,43.95772],[7.65266,43.9763],[7.66848,43.99943],[7.6597,44.03009],[7.72508,44.07578],[7.66878,44.12795],[7.68694,44.17487],[7.63245,44.17877],[7.62155,44.14881],[7.36364,44.11882],[7.34547,44.14359],[7.27827,44.1462],[7.16929,44.20352],[7.00764,44.23736],[6.98221,44.28289],[6.89171,44.36637],[6.88784,44.42043],[6.94504,44.43112],[6.86233,44.49834],[6.85507,44.53072],[6.96042,44.62129],[6.95133,44.66264],[7.00582,44.69364],[7.07484,44.68073],[7.00401,44.78782],[7.02217,44.82519],[6.93499,44.8664],[6.90774,44.84322],[6.75518,44.89915],[6.74519,44.93661],[6.74791,45.01939],[6.66981,45.02324],[6.62803,45.11175],[6.7697,45.16044],[6.85144,45.13226],[6.96706,45.20841],[7.07074,45.21228],[7.13115,45.25386],[7.10572,45.32924],[7.18019,45.40071],[7.00037,45.509],[6.98948,45.63869],[6.80785,45.71864],[6.80785,45.83265],[6.95315,45.85163],[7.04151,45.92435],[7.00946,45.9944],[6.93862,46.06502],[6.87868,46.03855],[6.89321,46.12548],[6.78968,46.14058],[6.86052,46.28512],[6.77152,46.34784],[6.8024,46.39171],[6.82312,46.42661],[6.53358,46.45431],[6.25432,46.3632],[6.21981,46.31304],[6.24826,46.30175],[6.25137,46.29014],[6.23775,46.27822],[6.24952,46.26255],[6.26749,46.24745],[6.29474,46.26221],[6.31041,46.24417],[6.29663,46.22688],[6.27694,46.21566],[6.26007,46.21165],[6.24821,46.20531],[6.23913,46.20511],[6.23544,46.20714],[6.22175,46.20045],[6.22222,46.19888],[6.21844,46.19837],[6.21603,46.19507],[6.21273,46.19409],[6.21114,46.1927],[6.20539,46.19163],[6.19807,46.18369],[6.19552,46.18401],[6.18707,46.17999],[6.18871,46.16644],[6.18116,46.16187],[6.15305,46.15194],[6.13397,46.1406],[6.09926,46.14373],[6.09199,46.15191],[6.07491,46.14879],[6.05203,46.15191],[6.04564,46.14031],[6.03614,46.13712],[6.01791,46.14228],[5.9871,46.14499],[5.97893,46.13303],[5.95781,46.12925],[5.9641,46.14412],[5.97508,46.15863],[5.98188,46.17392],[5.98846,46.17046],[5.99573,46.18587],[5.96515,46.19638],[5.97542,46.21525],[6.02461,46.23313],[6.03342,46.2383],[6.04602,46.23127],[6.05029,46.23518],[6.0633,46.24583],[6.07072,46.24085],[6.08563,46.24651],[6.10071,46.23772],[6.12446,46.25059],[6.11926,46.2634],[6.1013,46.28512],[6.11697,46.29547],[6.1198,46.31157],[6.13876,46.33844],[6.15738,46.3491],[6.16987,46.36759],[6.15985,46.37721],[6.15016,46.3778],[6.09926,46.40768],[6.06407,46.41676],[6.08427,46.44305],[6.07269,46.46244],[6.1567,46.54402],[6.11084,46.57649],[6.27135,46.68251],[6.38351,46.73171],[6.45209,46.77502],[6.43216,46.80336],[6.46456,46.88865],[6.43341,46.92703],[6.71531,47.0494],[6.68823,47.06616],[6.76788,47.1208],[6.8489,47.15933],[6.9508,47.24338],[6.95108,47.26428],[6.94316,47.28747],[7.05305,47.33304],[7.0564,47.35134],[7.03125,47.36996],[6.87959,47.35335],[6.88542,47.37262],[6.93744,47.40714],[6.93953,47.43388],[7.0024,47.45264],[6.98425,47.49432],[7.0231,47.50522],[7.07425,47.48863],[7.12781,47.50371],[7.16249,47.49025],[7.19583,47.49455],[7.17026,47.44312],[7.24669,47.4205],[7.33526,47.44186],[7.35603,47.43432],[7.40308,47.43638],[7.43088,47.45846],[7.4462,47.46264],[7.4583,47.47216],[7.42923,47.48628],[7.43356,47.49712],[7.47534,47.47932],[7.51076,47.49651],[7.49804,47.51798],[7.5229,47.51644],[7.53199,47.5284],[7.51904,47.53515],[7.50588,47.52856],[7.49691,47.53821],[7.50873,47.54546],[7.51723,47.54578],[7.52831,47.55347],[7.53634,47.55553],[7.55652,47.56779],[7.55689,47.57232],[7.56548,47.57617],[7.56684,47.57785],[7.58386,47.57536],[7.58945,47.59017],[7.59301,47.60058],[7.58851,47.60794],[7.57423,47.61628],[7.5591,47.63849],[7.53384,47.65115],[7.52067,47.66437],[7.51915,47.68335],[7.51266,47.70197],[7.53722,47.71635],[7.54761,47.72912],[7.52921,47.77747],[7.55673,47.87371],[7.62302,47.97898],[7.56966,48.03265],[7.57137,48.12292],[7.6648,48.22219],[7.69022,48.30018],[7.74562,48.32736],[7.73109,48.39192],[7.76833,48.48945],[7.80647,48.51239],[7.80167,48.54758],[7.80057,48.5857],[7.84098,48.64217],[7.89002,48.66317],[7.96812,48.72491],[7.96994,48.75606],[8.01534,48.76085],[8.0326,48.79017],[8.06802,48.78957],[8.10253,48.81829],[8.12813,48.87985],[8.19989,48.95825],[8.20031,48.95856],[8.22604,48.97352],[8.14189,48.97833],[7.97783,49.03161],[7.93641,49.05544],[7.86386,49.03499],[7.79557,49.06583],[7.75948,49.04562],[7.63618,49.05428],[7.62575,49.07654],[7.56416,49.08136],[7.53012,49.09818],[7.49172,49.13915],[7.49473,49.17],[7.44455,49.16765],[7.44052,49.18354],[7.3662,49.17308],[7.35995,49.14399],[7.3195,49.14231],[7.29514,49.11426],[7.23473,49.12971],[7.1593,49.1204],[7.1358,49.1282],[7.12504,49.14253],[7.10384,49.13787],[7.10715,49.15631],[7.07859,49.15031],[7.09007,49.13094],[7.07162,49.1255],[7.06642,49.11415],[7.05548,49.11185],[7.04843,49.11422],[7.04409,49.12123],[7.04662,49.13724],[7.03178,49.15734],[7.0274,49.17042],[7.03459,49.19096],[7.01318,49.19018],[6.97273,49.2099],[6.95963,49.203],[6.94028,49.21641],[6.93831,49.2223],[6.91875,49.22261],[6.89298,49.20863],[6.85939,49.22376],[6.83555,49.21249],[6.85119,49.20038],[6.85016,49.19354],[6.86225,49.18185],[6.84703,49.15734],[6.83385,49.15162],[6.78265,49.16793],[6.73765,49.16375],[6.71137,49.18808],[6.73256,49.20486],[6.71843,49.2208],[6.69274,49.21661],[6.66583,49.28065],[6.60186,49.31055],[6.572,49.35027],[6.58807,49.35358],[6.60091,49.36864],[6.533,49.40748],[6.55404,49.42464],[6.42432,49.47683],[6.40274,49.46546],[6.39168,49.4667],[6.38352,49.46463],[6.36778,49.46937],[6.3687,49.4593],[6.28818,49.48465],[6.27875,49.503],[6.25029,49.50609],[6.2409,49.51408],[6.19543,49.50536],[6.17386,49.50934],[6.15366,49.50226],[6.16115,49.49297],[6.14321,49.48796],[6.12814,49.49365],[6.12346,49.4735],[6.10325,49.4707],[6.09845,49.46351],[6.10072,49.45268],[6.08373,49.45594],[6.07887,49.46399],[6.05553,49.46663],[6.04176,49.44801],[6.02743,49.44845],[6.02648,49.45451],[5.97693,49.45513],[5.96876,49.49053],[5.94224,49.49608],[5.94128,49.50034],[5.86571,49.50015],[5.83389,49.52152],[5.83467,49.52717],[5.84466,49.53027],[5.83648,49.5425],[5.81664,49.53775],[5.80871,49.5425],[5.81838,49.54777],[5.79195,49.55228],[5.77435,49.56298],[5.7577,49.55915],[5.75649,49.54321],[5.64505,49.55146],[5.60909,49.51228],[5.55001,49.52729],[5.46541,49.49825],[5.46734,49.52648],[5.43713,49.5707],[5.3974,49.61596],[5.34837,49.62889],[5.33851,49.61599],[5.3137,49.61225],[5.30214,49.63055],[5.33039,49.6555],[5.31465,49.66846],[5.26232,49.69456],[5.14545,49.70287],[5.09249,49.76193],[4.96714,49.79872],[4.85464,49.78995],[4.86965,49.82271],[4.85134,49.86457],[4.88529,49.9236],[4.78827,49.95609],[4.8382,50.06738],[4.88602,50.15182],[4.83279,50.15331],[4.82438,50.16878],[4.75237,50.11314],[4.70064,50.09384],[4.68695,49.99685],[4.5414,49.96911],[4.51098,49.94659],[4.43488,49.94122],[4.35051,49.95315],[4.31963,49.97043],[4.20532,49.95803],[4.14239,49.98034],[4.13508,50.01976],[4.16294,50.04719],[4.23101,50.06945],[4.20147,50.13535],[4.13561,50.13078],[4.16014,50.19239],[4.15524,50.21103],[4.21945,50.25539],[4.20651,50.27333],[4.17861,50.27443],[4.17347,50.28838],[4.15524,50.2833],[4.16808,50.25786],[4.13665,50.25609],[4.11954,50.30425],[4.10957,50.30234],[4.10237,50.31247],[4.0689,50.3254],[4.0268,50.35793],[3.96771,50.34989],[3.90781,50.32814],[3.84314,50.35219],[3.73911,50.34809],[3.70987,50.3191],[3.71009,50.30305],[3.66976,50.34563],[3.65709,50.36873],[3.67262,50.38663],[3.67494,50.40239],[3.66153,50.45165],[3.64426,50.46275],[3.61014,50.49568],[3.58361,50.49049],[3.5683,50.50192],[3.49509,50.48885],[3.51564,50.5256],[3.47385,50.53397],[3.44629,50.51009],[3.37693,50.49538],[3.28575,50.52724],[3.2729,50.60718],[3.23951,50.6585],[3.264,50.67668],[3.2536,50.68977],[3.26141,50.69151],[3.26063,50.70086],[3.24593,50.71389],[3.22042,50.71019],[3.20845,50.71662],[3.19017,50.72569],[3.20064,50.73547],[3.18811,50.74025],[3.18339,50.74981],[3.16476,50.76843],[3.15017,50.79031],[3.1257,50.78603],[3.11987,50.79188],[3.11206,50.79416],[3.10614,50.78303],[3.09163,50.77717],[3.04314,50.77674],[3.00537,50.76588],[2.96778,50.75242],[2.95019,50.75138],[2.90873,50.702],[2.91036,50.6939],[2.90069,50.69263],[2.88504,50.70656],[2.87937,50.70298],[2.86985,50.7033],[2.8483,50.72276],[2.81056,50.71773],[2.71165,50.81295],[2.63331,50.81457],[2.59093,50.91751],[2.63074,50.94746],[2.57551,51.00326],[2.55904,51.07014]]]]}},{type:"Feature",properties:{iso1A2:"GA",iso1A3:"GAB",iso1N3:"266",wikidata:"Q1000",nameEn:"Gabon",groups:["017","202","002"],callingCodes:["241"]},geometry:{type:"MultiPolygon",coordinates:[[[[13.29457,2.16106],[13.28534,2.25716],[11.37116,2.29975],[11.3561,2.17217],[11.35307,1.00251],[9.79648,1.0019],[9.78058,1.03996],[9.76085,1.05949],[9.73014,1.06721],[9.68638,1.06836],[9.66092,1.05865],[9.62096,1.03039],[9.54793,1.0185],[9.51998,0.96418],[9.35563,0.84865],[7.24416,-0.64092],[10.75913,-4.39519],[11.12647,-3.94169],[11.22301,-3.69888],[11.48764,-3.51089],[11.57949,-3.52798],[11.68608,-3.68942],[11.87083,-3.71571],[11.92719,-3.62768],[11.8318,-3.5812],[11.96554,-3.30267],[11.70227,-3.17465],[11.70558,-3.0773],[11.80365,-3.00424],[11.64798,-2.81146],[11.5359,-2.85654],[11.64487,-2.61865],[11.57637,-2.33379],[11.74605,-2.39936],[11.96866,-2.33559],[12.04895,-2.41704],[12.47925,-2.32626],[12.44656,-1.92025],[12.61312,-1.8129],[12.82172,-1.91091],[13.02759,-2.33098],[13.47977,-2.43224],[13.75884,-2.09293],[13.92073,-2.35581],[13.85846,-2.46935],[14.10442,-2.49268],[14.23829,-2.33715],[14.16202,-2.23916],[14.23518,-2.15671],[14.25932,-1.97624],[14.41838,-1.89412],[14.52569,-0.57818],[14.41887,-0.44799],[14.2165,-0.38261],[14.06862,-0.20826],[13.90632,-0.2287],[13.88648,0.26652],[14.10909,0.58563],[14.26066,0.57255],[14.48179,0.9152],[14.25186,1.39842],[13.89582,1.4261],[13.15519,1.23368],[13.25447,1.32339],[13.13461,1.57238],[13.29457,2.16106]]]]}},{type:"Feature",properties:{iso1A2:"GB",iso1A3:"GBR",iso1N3:"826",wikidata:"Q145",nameEn:"United Kingdom",aliases:["UK","Britain","Great Britain"],groups:["154","150"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["44"]},geometry:{type:"MultiPolygon",coordinates:[[[[-5.83481,53.87749],[-4.1819,54.57861],[-3.64906,54.12723],[-5.37267,53.63269],[-5.79914,52.03902],[-7.74976,48.64773],[1.17405,50.74239],[2.18458,51.52087],[2.56575,51.85301],[-0.3751,61.32236],[-14.78497,57.60709],[-7.93366,55.84142],[-6.79943,55.54107],[-6.71944,55.27952],[-6.9734,55.19878],[-7.2471,55.06933],[-7.34464,55.04688],[-7.4033,55.00391],[-7.40004,54.94498],[-7.44404,54.9403],[-7.4473,54.87003],[-7.47626,54.83084],[-7.54508,54.79401],[-7.54671,54.74606],[-7.64449,54.75265],[-7.75041,54.7103],[-7.83352,54.73854],[-7.93293,54.66603],[-7.70315,54.62077],[-7.8596,54.53671],[-7.99812,54.54427],[-8.04538,54.48941],[-8.179,54.46763],[-8.04555,54.36292],[-7.87101,54.29299],[-7.8596,54.21779],[-7.81397,54.20159],[-7.69501,54.20731],[-7.55812,54.12239],[-7.4799,54.12239],[-7.44567,54.1539],[-7.32834,54.11475],[-7.30553,54.11869],[-7.34005,54.14698],[-7.29157,54.17191],[-7.28017,54.16714],[-7.29687,54.1354],[-7.29493,54.12013],[-7.26316,54.13863],[-7.25012,54.20063],[-7.14908,54.22732],[-7.19145,54.31296],[-7.02034,54.4212],[-6.87775,54.34682],[-6.85179,54.29176],[-6.81583,54.22791],[-6.74575,54.18788],[-6.70175,54.20218],[-6.6382,54.17071],[-6.66264,54.0666],[-6.62842,54.03503],[-6.47849,54.06947],[-6.36605,54.07234],[-6.36279,54.11248],[-6.32694,54.09337],[-6.29003,54.11278],[-6.26218,54.09785],[-5.83481,53.87749]]],[[[33.70575,34.97947],[33.83531,34.73974],[33.98684,34.76642],[33.90075,34.96623],[33.86432,34.97592],[33.84811,34.97075],[33.83505,34.98108],[33.85621,34.98956],[33.85891,35.001],[33.85216,35.00579],[33.84045,35.00616],[33.82875,35.01685],[33.83055,35.02865],[33.81524,35.04192],[33.8012,35.04786],[33.82051,35.0667],[33.8355,35.05777],[33.85261,35.0574],[33.88367,35.07877],[33.89485,35.06873],[33.90247,35.07686],[33.91299,35.07579],[33.91789,35.08688],[33.89853,35.11377],[33.88737,35.11408],[33.88943,35.12007],[33.88561,35.12449],[33.87224,35.12293],[33.87622,35.10457],[33.87097,35.09389],[33.87479,35.08881],[33.8541,35.07201],[33.84168,35.06823],[33.82067,35.07826],[33.78581,35.05104],[33.76106,35.04253],[33.73824,35.05321],[33.71482,35.03722],[33.70209,35.04882],[33.7161,35.07279],[33.70861,35.07644],[33.69095,35.06237],[33.68474,35.06602],[33.67742,35.05963],[33.67678,35.03866],[33.69938,35.03123],[33.69731,35.01754],[33.71514,35.00294],[33.70639,34.99303],[33.70575,34.97947]],[[33.77312,34.9976],[33.77553,34.99518],[33.78516,34.99582],[33.79191,34.98914],[33.78917,34.98854],[33.78571,34.98951],[33.78318,34.98699],[33.78149,34.98854],[33.77843,34.988],[33.7778,34.98981],[33.76738,34.99188],[33.76605,34.99543],[33.75682,34.99916],[33.75994,35.00113],[33.77312,34.9976]],[[33.74144,35.01053],[33.7343,35.01178],[33.73781,35.02181],[33.74265,35.02329],[33.74983,35.02274],[33.7492,35.01319],[33.74144,35.01053]]],[[[32.86014,34.70585],[32.82717,34.70622],[32.79433,34.67883],[32.76136,34.68318],[32.75515,34.64985],[32.74412,34.43926],[33.26744,34.49942],[33.0138,34.64424],[32.96968,34.64046],[32.96718,34.63446],[32.95891,34.62919],[32.95323,34.64075],[32.95471,34.64528],[32.94976,34.65204],[32.94796,34.6587],[32.95325,34.66462],[32.97079,34.66112],[32.97736,34.65277],[32.99014,34.65518],[32.98668,34.67268],[32.99135,34.68061],[32.95539,34.68471],[32.94683,34.67907],[32.94379,34.67111],[32.93693,34.67027],[32.93449,34.66241],[32.92807,34.66736],[32.93043,34.67091],[32.91398,34.67343],[32.9068,34.66102],[32.86167,34.68734],[32.86014,34.70585]]]]}},{type:"Feature",properties:{iso1A2:"GD",iso1A3:"GRD",iso1N3:"308",wikidata:"Q769",nameEn:"Grenada",aliases:["WG"],groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 473"]},geometry:{type:"MultiPolygon",coordinates:[[[[-62.14806,11.87638],[-61.57265,11.65795],[-61.13395,12.51526],[-61.38256,12.52991],[-61.73897,12.61191],[-62.14806,11.87638]]]]}},{type:"Feature",properties:{iso1A2:"GE",iso1A3:"GEO",iso1N3:"268",wikidata:"Q230",nameEn:"Georgia",groups:["145","142"],callingCodes:["995"]},geometry:{type:"MultiPolygon",coordinates:[[[[46.42738,41.91323],[45.61676,42.20768],[45.78692,42.48358],[45.36501,42.55268],[45.15318,42.70598],[44.88754,42.74934],[44.80941,42.61277],[44.70002,42.74679],[44.54202,42.75699],[43.95517,42.55396],[43.73119,42.62043],[43.81453,42.74297],[43.0419,43.02413],[43.03322,43.08883],[42.75889,43.19651],[42.66667,43.13917],[42.40563,43.23226],[41.64935,43.22331],[40.65957,43.56212],[40.10657,43.57344],[40.04445,43.47776],[40.03312,43.44262],[40.01007,43.42411],[40.01552,43.42025],[40.00853,43.40578],[40.0078,43.38551],[39.81147,43.06294],[40.89217,41.72528],[41.54366,41.52185],[41.7148,41.4932],[41.7124,41.47417],[41.81939,41.43621],[41.95134,41.52466],[42.26387,41.49346],[42.51772,41.43606],[42.59202,41.58183],[42.72794,41.59714],[42.84471,41.58912],[42.78995,41.50126],[42.84899,41.47265],[42.8785,41.50516],[43.02956,41.37891],[43.21707,41.30331],[43.13373,41.25503],[43.1945,41.25242],[43.23096,41.17536],[43.36118,41.2028],[43.44973,41.17666],[43.4717,41.12611],[43.67712,41.13398],[43.74717,41.1117],[43.84835,41.16329],[44.16591,41.19141],[44.18148,41.24644],[44.32139,41.2079],[44.34337,41.20312],[44.34417,41.2382],[44.46791,41.18204],[44.59322,41.1933],[44.61462,41.24018],[44.72814,41.20338],[44.82084,41.21513],[44.87887,41.20195],[44.89911,41.21366],[44.84358,41.23088],[44.81749,41.23488],[44.80053,41.25949],[44.81437,41.30371],[44.93493,41.25685],[45.0133,41.29747],[45.09867,41.34065],[45.1797,41.42231],[45.26285,41.46433],[45.31352,41.47168],[45.4006,41.42402],[45.45973,41.45898],[45.68389,41.3539],[45.71035,41.36208],[45.75705,41.35157],[45.69946,41.29545],[45.80842,41.2229],[45.95786,41.17956],[46.13221,41.19479],[46.27698,41.19011],[46.37661,41.10805],[46.456,41.09984],[46.48558,41.0576],[46.55096,41.1104],[46.63969,41.09515],[46.66148,41.20533],[46.72375,41.28609],[46.63658,41.37727],[46.4669,41.43331],[46.40307,41.48464],[46.33925,41.4963],[46.29794,41.5724],[46.34126,41.57454],[46.34039,41.5947],[46.3253,41.60912],[46.28182,41.60089],[46.26531,41.63339],[46.24429,41.59883],[46.19759,41.62327],[46.17891,41.72094],[46.20538,41.77205],[46.23962,41.75811],[46.30863,41.79133],[46.3984,41.84399],[46.42738,41.91323]]]]}},{type:"Feature",properties:{iso1A2:"GF",iso1A3:"GUF",iso1N3:"254",wikidata:"Q3769",nameEn:"French Guiana",country:"FR",groups:["EU","005","419","019"],callingCodes:["594"]},geometry:{type:"MultiPolygon",coordinates:[[[[-51.35485,4.8383],[-53.7094,6.2264],[-54.01074,5.68785],[-54.01877,5.52789],[-54.26916,5.26909],[-54.4717,4.91964],[-54.38444,4.13222],[-54.19367,3.84387],[-54.05128,3.63557],[-53.98914,3.627],[-53.9849,3.58697],[-54.28534,2.67798],[-54.42864,2.42442],[-54.6084,2.32856],[-54.16286,2.10779],[-53.78743,2.34412],[-52.96539,2.1881],[-52.6906,2.37298],[-52.31787,3.17896],[-51.85573,3.83427],[-51.82312,3.85825],[-51.79599,3.89336],[-51.61983,4.14596],[-51.63798,4.51394],[-51.35485,4.8383]]]]}},{type:"Feature",properties:{iso1A2:"GG",iso1A3:"GGY",iso1N3:"831",wikidata:"Q25230",nameEn:"Guernsey",country:"GB",groups:["830","154","150"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["44 01481"]},geometry:{type:"MultiPolygon",coordinates:[[[[-2.65349,49.15373],[-2.36485,49.48223],[-2.09454,49.46288],[-2.02963,49.91866],[-3.28154,49.57329],[-2.65349,49.15373]]]]}},{type:"Feature",properties:{iso1A2:"GH",iso1A3:"GHA",iso1N3:"288",wikidata:"Q117",nameEn:"Ghana",groups:["011","202","002"],callingCodes:["233"]},geometry:{type:"MultiPolygon",coordinates:[[[[-0.13493,11.14075],[-0.27374,11.17157],[-0.28566,11.12713],[-0.35955,11.07801],[-0.38219,11.12596],[-0.42391,11.11661],[-0.44298,11.04292],[-0.61937,10.91305],[-0.67143,10.99811],[-2.83373,11.0067],[-2.94232,10.64281],[-2.83108,10.40252],[-2.74174,9.83172],[-2.76534,9.56589],[-2.68802,9.49343],[-2.69814,9.22717],[-2.77799,9.04949],[-2.66357,9.01771],[-2.58243,8.7789],[-2.49037,8.20872],[-2.62901,8.11495],[-2.61232,8.02645],[-2.67787,8.02055],[-2.74819,7.92613],[-2.78395,7.94974],[-2.79467,7.86002],[-2.92339,7.60847],[-2.97822,7.27165],[-2.95438,7.23737],[-3.23327,6.81744],[-3.21954,6.74407],[-3.25999,6.62521],[-3.01896,5.71697],[-2.95323,5.71865],[-2.96671,5.6415],[-2.93132,5.62137],[-2.85378,5.65156],[-2.76614,5.60963],[-2.72737,5.34789],[-2.77625,5.34621],[-2.73074,5.1364],[-2.75502,5.10657],[-2.95261,5.12477],[-2.96554,5.10397],[-3.063,5.13665],[-3.11073,5.12675],[-3.10675,5.08515],[-3.34019,4.17519],[1.07031,5.15655],[1.27574,5.93551],[1.19771,6.11522],[1.19966,6.17069],[1.09187,6.17074],[1.05969,6.22998],[1.03108,6.24064],[0.99652,6.33779],[0.89283,6.33779],[0.71048,6.53083],[0.74862,6.56517],[0.63659,6.63857],[0.6497,6.73682],[0.58176,6.76049],[0.57406,6.80348],[0.52853,6.82921],[0.56508,6.92971],[0.52098,6.94391],[0.52217,6.9723],[0.59606,7.01252],[0.65327,7.31643],[0.62943,7.41099],[0.57223,7.39326],[0.52455,7.45354],[0.51979,7.58706],[0.58295,7.62368],[0.62943,7.85751],[0.58891,8.12779],[0.6056,8.13959],[0.61156,8.18324],[0.5913,8.19622],[0.63897,8.25873],[0.73432,8.29529],[0.64731,8.48866],[0.47211,8.59945],[0.37319,8.75262],[0.52455,8.87746],[0.45424,9.04581],[0.56388,9.40697],[0.49118,9.48339],[0.36485,9.49749],[0.33148,9.44812],[0.25758,9.42696],[0.2254,9.47869],[0.31241,9.50337],[0.30406,9.521],[0.2409,9.52335],[0.23851,9.57389],[0.38153,9.58682],[0.36008,9.6256],[0.29334,9.59387],[0.26712,9.66437],[0.28261,9.69022],[0.32313,9.6491],[0.34816,9.66907],[0.34816,9.71607],[0.32075,9.72781],[0.36366,10.03309],[0.41252,10.02018],[0.41371,10.06361],[0.35293,10.09412],[0.39584,10.31112],[0.33028,10.30408],[0.29453,10.41546],[0.18846,10.4096],[0.12886,10.53149],[-0.05945,10.63458],[-0.09141,10.7147],[-0.07327,10.71845],[-0.07183,10.76794],[-0.0228,10.81916],[-0.02685,10.8783],[-0.00908,10.91644],[-0.0063,10.96417],[0.03355,10.9807],[0.02395,11.06229],[0.00342,11.08317],[-0.00514,11.10763],[-0.0275,11.11202],[-0.05733,11.08628],[-0.14462,11.10811],[-0.13493,11.14075]]]]}},{type:"Feature",properties:{iso1A2:"GI",iso1A3:"GIB",iso1N3:"292",wikidata:"Q1410",nameEn:"Gibraltar",country:"GB",groups:["039","150"],callingCodes:["350"]},geometry:{type:"MultiPolygon",coordinates:[[[[-5.28217,36.09907],[-5.27801,36.14942],[-5.33822,36.15272],[-5.34536,36.15501],[-5.36494,36.15496],[-5.38545,36.15481],[-5.40134,36.14896],[-5.39074,36.10278],[-5.36503,36.06205],[-5.32837,36.05935],[-5.3004,36.07439],[-5.28217,36.09907]]]]}},{type:"Feature",properties:{iso1A2:"GL",iso1A3:"GRL",iso1N3:"304",wikidata:"Q223",nameEn:"Greenland",country:"DK",groups:["021","003","019"],callingCodes:["299"]},geometry:{type:"MultiPolygon",coordinates:[[[[-45.47832,84.58738],[-68.21821,80.48551],[-76.75614,76.72014],[-46.37635,57.3249],[-9.68082,72.73731],[-5.7106,84.28058],[-45.47832,84.58738]]]]}},{type:"Feature",properties:{iso1A2:"GM",iso1A3:"GMB",iso1N3:"270",wikidata:"Q1005",nameEn:"The Gambia",groups:["011","202","002"],callingCodes:["220"]},geometry:{type:"MultiPolygon",coordinates:[[[[-15.14917,13.57989],[-14.36795,13.23033],[-13.79409,13.34472],[-13.8955,13.59126],[-14.34721,13.46578],[-14.93719,13.80173],[-15.36504,13.79313],[-15.47902,13.58758],[-17.43598,13.59273],[-17.43966,13.04579],[-16.74676,13.06025],[-16.69343,13.16791],[-15.80355,13.16729],[-15.80478,13.34832],[-15.26908,13.37768],[-15.14917,13.57989]]]]}},{type:"Feature",properties:{iso1A2:"GN",iso1A3:"GIN",iso1N3:"324",wikidata:"Q1006",nameEn:"Guinea",groups:["011","202","002"],callingCodes:["224"]},geometry:{type:"MultiPolygon",coordinates:[[[[-11.37536,12.40788],[-11.46267,12.44559],[-11.91331,12.42008],[-12.35415,12.32758],[-12.87336,12.51892],[-13.06603,12.49342],[-13.05296,12.64003],[-13.70523,12.68013],[-13.7039,12.60313],[-13.65089,12.49515],[-13.64168,12.42764],[-13.70851,12.24978],[-13.92745,12.24077],[-13.94589,12.16869],[-13.7039,12.00869],[-13.7039,11.70195],[-14.09799,11.63649],[-14.26623,11.67486],[-14.31513,11.60713],[-14.51173,11.49708],[-14.66677,11.51188],[-14.77786,11.36323],[-14.95993,10.99244],[-15.07174,10.89557],[-15.96748,10.162],[-14.36218,8.64107],[-13.29911,9.04245],[-13.18586,9.0925],[-13.08953,9.0409],[-12.94095,9.26335],[-12.76788,9.3133],[-12.47254,9.86834],[-12.24262,9.92386],[-12.12634,9.87203],[-11.91023,9.93927],[-11.89624,9.99763],[-11.2118,10.00098],[-10.6534,9.29919],[-10.74484,9.07998],[-10.5783,9.06386],[-10.56197,8.81225],[-10.47707,8.67669],[-10.61422,8.5314],[-10.70565,8.29235],[-10.63934,8.35326],[-10.54891,8.31174],[-10.37257,8.48941],[-10.27575,8.48711],[-10.203,8.47991],[-10.14579,8.52665],[-10.05375,8.50697],[-10.05873,8.42578],[-9.77763,8.54633],[-9.47415,8.35195],[-9.50898,8.18455],[-9.41445,8.02448],[-9.44928,7.9284],[-9.35724,7.74111],[-9.37465,7.62032],[-9.48161,7.37122],[-9.41943,7.41809],[-9.305,7.42056],[-9.20798,7.38109],[-9.18311,7.30461],[-9.09107,7.1985],[-8.93435,7.2824],[-8.85724,7.26019],[-8.8448,7.35149],[-8.72789,7.51429],[-8.67814,7.69428],[-8.55874,7.70167],[-8.55874,7.62525],[-8.47114,7.55676],[-8.4003,7.6285],[-8.21374,7.54466],[-8.09931,7.78626],[-8.13414,7.87991],[-8.06449,8.04989],[-7.94695,8.00925],[-7.99919,8.11023],[-7.98675,8.20134],[-8.062,8.16071],[-8.2411,8.24196],[-8.22991,8.48438],[-7.92518,8.50652],[-7.65653,8.36873],[-7.69882,8.66148],[-7.95503,8.81146],[-7.92518,8.99332],[-7.73862,9.08422],[-7.90777,9.20456],[-7.85056,9.41812],[-8.03463,9.39604],[-8.14657,9.55062],[-8.09434,9.86936],[-8.15652,9.94288],[-8.11921,10.04577],[-8.01225,10.1021],[-7.97971,10.17117],[-7.9578,10.2703],[-8.10207,10.44649],[-8.22711,10.41722],[-8.32614,10.69273],[-8.2667,10.91762],[-8.35083,11.06234],[-8.66923,10.99397],[-8.40058,11.37466],[-8.80854,11.66715],[-8.94784,12.34842],[-9.13689,12.50875],[-9.38067,12.48446],[-9.32097,12.29009],[-9.63938,12.18312],[-9.714,12.0226],[-10.30604,12.24634],[-10.71897,11.91552],[-10.80355,12.1053],[-10.99758,12.24634],[-11.24136,12.01286],[-11.50006,12.17826],[-11.37536,12.40788]]]]}},{type:"Feature",properties:{iso1A2:"GP",iso1A3:"GLP",iso1N3:"312",wikidata:"Q17012",nameEn:"Guadeloupe",country:"FR",groups:["EU","029","003","419","019"],callingCodes:["590"]},geometry:{type:"MultiPolygon",coordinates:[[[[-60.95725,15.70997],[-60.71337,16.48911],[-61.44461,16.81958],[-61.83929,16.66647],[-62.17275,16.35721],[-61.81728,15.58058],[-61.44899,15.79571],[-60.95725,15.70997]]]]}},{type:"Feature",properties:{iso1A2:"GQ",iso1A3:"GNQ",iso1N3:"226",wikidata:"Q983",nameEn:"Equatorial Guinea",groups:["017","202","002"],callingCodes:["240"]},geometry:{type:"MultiPolygon",coordinates:[[[[9.22018,3.72052],[8.34397,4.30689],[8.05799,3.48284],[8.0168,1.79377],[6.69416,-0.53945],[5.38965,-1.19244],[5.3459,-2.30107],[7.24416,-0.64092],[9.35563,0.84865],[9.51998,0.96418],[9.54793,1.0185],[9.62096,1.03039],[9.66092,1.05865],[9.68638,1.06836],[9.73014,1.06721],[9.76085,1.05949],[9.78058,1.03996],[9.79648,1.0019],[11.35307,1.00251],[11.3561,2.17217],[9.991,2.16561],[9.90749,2.20049],[9.89012,2.20457],[9.84716,2.24676],[9.83238,2.29079],[9.83754,2.32428],[9.82123,2.35097],[9.81162,2.33797],[9.22018,3.72052]]]]}},{type:"Feature",properties:{iso1A2:"GR",iso1A3:"GRC",iso1N3:"300",wikidata:"Q41",nameEn:"Greece",aliases:["EL"],groups:["EU","039","150"],callingCodes:["30"]},geometry:{type:"MultiPolygon",coordinates:[[[[26.03489,40.73051],[26.0754,40.72772],[26.08638,40.73214],[26.12495,40.74283],[26.12854,40.77339],[26.15685,40.80709],[26.21351,40.83298],[26.20856,40.86048],[26.26169,40.9168],[26.29441,40.89119],[26.28623,40.93005],[26.32259,40.94042],[26.35894,40.94292],[26.33297,40.98388],[26.3606,41.02027],[26.31928,41.07386],[26.32259,41.24929],[26.39861,41.25053],[26.5209,41.33993],[26.5837,41.32131],[26.62997,41.34613],[26.61767,41.42281],[26.59742,41.48058],[26.59196,41.60491],[26.5209,41.62592],[26.47958,41.67037],[26.35957,41.71149],[26.30255,41.70925],[26.2654,41.71544],[26.22888,41.74139],[26.21325,41.73223],[26.16841,41.74858],[26.06148,41.70345],[26.07083,41.64584],[26.15146,41.60828],[26.14328,41.55496],[26.17951,41.55409],[26.176,41.50072],[26.14796,41.47533],[26.20288,41.43943],[26.16548,41.42278],[26.12926,41.35878],[25.87919,41.30526],[25.8266,41.34563],[25.70507,41.29209],[25.66183,41.31316],[25.61042,41.30614],[25.55082,41.31667],[25.52394,41.2798],[25.48187,41.28506],[25.28322,41.23411],[25.11611,41.34212],[24.942,41.38685],[24.90928,41.40876],[24.86136,41.39298],[24.82514,41.4035],[24.8041,41.34913],[24.71529,41.41928],[24.61129,41.42278],[24.52599,41.56808],[24.30513,41.51297],[24.27124,41.57682],[24.18126,41.51735],[24.10063,41.54796],[24.06323,41.53222],[24.06908,41.46132],[23.96975,41.44118],[23.91483,41.47971],[23.89613,41.45257],[23.80148,41.43943],[23.76525,41.40175],[23.67644,41.41139],[23.63203,41.37632],[23.52453,41.40262],[23.40416,41.39999],[23.33639,41.36317],[23.31301,41.40525],[23.22771,41.37106],[23.21953,41.33773],[23.1833,41.31755],[22.93334,41.34104],[22.81199,41.3398],[22.76408,41.32225],[22.74538,41.16321],[22.71266,41.13945],[22.65306,41.18168],[22.62852,41.14385],[22.58295,41.11568],[22.5549,41.13065],[22.42285,41.11921],[22.26744,41.16409],[22.17629,41.15969],[22.1424,41.12449],[22.06527,41.15617],[21.90869,41.09191],[21.91102,41.04786],[21.7556,40.92525],[21.69601,40.9429],[21.57448,40.86076],[21.53007,40.90759],[21.41555,40.9173],[21.35595,40.87578],[21.25779,40.86165],[21.21105,40.8855],[21.15262,40.85546],[20.97887,40.85475],[20.98396,40.79109],[20.95752,40.76982],[20.98134,40.76046],[21.05833,40.66586],[21.03932,40.56299],[20.96908,40.51526],[20.94925,40.46625],[20.83688,40.47882],[20.7906,40.42726],[20.78234,40.35803],[20.71789,40.27739],[20.67162,40.09433],[20.62566,40.0897],[20.61081,40.07866],[20.55593,40.06524],[20.51297,40.08168],[20.48487,40.06271],[20.42373,40.06777],[20.37911,39.99058],[20.31135,39.99438],[20.41546,39.82832],[20.41475,39.81437],[20.38572,39.78516],[20.30804,39.81563],[20.29152,39.80421],[20.31961,39.72799],[20.27412,39.69884],[20.22707,39.67459],[20.22376,39.64532],[20.15988,39.652],[20.12956,39.65805],[20.05189,39.69112],[20.00957,39.69227],[19.98042,39.6504],[19.92466,39.69533],[19.97622,39.78684],[19.95905,39.82857],[19.0384,40.35325],[19.20409,39.7532],[22.5213,33.45682],[29.73302,35.92555],[29.69611,36.10365],[29.61805,36.14179],[29.61002,36.1731],[29.48192,36.18377],[29.30783,36.01033],[28.23708,36.56812],[27.95037,36.46155],[27.89482,36.69898],[27.46117,36.53789],[27.24613,36.71622],[27.45627,36.9008],[27.20312,36.94571],[27.14757,37.32],[26.95583,37.64989],[26.99377,37.69034],[27.16428,37.72343],[27.05537,37.9131],[26.21136,38.17558],[26.24183,38.44695],[26.32173,38.48731],[26.21136,38.65436],[26.61814,38.81372],[26.70773,39.0312],[26.43357,39.43096],[25.94257,39.39358],[25.61285,40.17161],[26.04292,40.3958],[25.94795,40.72797],[26.03489,40.73051]]]]}},{type:"Feature",properties:{iso1A2:"GS",iso1A3:"SGS",iso1N3:"239",wikidata:"Q35086",nameEn:"South Georgia and South Sandwich Islands",country:"GB",groups:["005","419","019"],driveSide:"left",callingCodes:["500"]},geometry:{type:"MultiPolygon",coordinates:[[[[-35.26394,-43.68272],[-53.39656,-59.87088],[-22.31757,-59.85974],[-35.26394,-43.68272]]]]}},{type:"Feature",properties:{iso1A2:"GT",iso1A3:"GTM",iso1N3:"320",wikidata:"Q774",nameEn:"Guatemala",groups:["013","003","419","019"],callingCodes:["502"]},geometry:{type:"MultiPolygon",coordinates:[[[[-89.14985,17.81563],[-90.98678,17.81655],[-90.99199,17.25192],[-91.43809,17.25373],[-91.04436,16.92175],[-90.69064,16.70697],[-90.61212,16.49832],[-90.40499,16.40524],[-90.44567,16.07573],[-91.73182,16.07371],[-92.20983,15.26077],[-92.0621,15.07406],[-92.1454,14.98143],[-92.1423,14.88647],[-92.18161,14.84147],[-92.1454,14.6804],[-92.2261,14.53423],[-92.37213,14.39277],[-90.55276,12.8866],[-90.11344,13.73679],[-90.10505,13.85104],[-89.88937,14.0396],[-89.81807,14.07073],[-89.76103,14.02923],[-89.73251,14.04133],[-89.75569,14.07073],[-89.70756,14.1537],[-89.61844,14.21937],[-89.52397,14.22628],[-89.50614,14.26084],[-89.58814,14.33165],[-89.57441,14.41637],[-89.39028,14.44561],[-89.34776,14.43013],[-89.35189,14.47553],[-89.23719,14.58046],[-89.15653,14.57802],[-89.13132,14.71582],[-89.23467,14.85596],[-89.15149,14.97775],[-89.18048,14.99967],[-89.15149,15.07392],[-88.97343,15.14039],[-88.32467,15.63665],[-88.31459,15.66942],[-88.24022,15.69247],[-88.22552,15.72294],[-88.20359,16.03858],[-88.40779,16.09624],[-88.95358,15.88698],[-89.02415,15.9063],[-89.17418,15.90898],[-89.22683,15.88619],[-89.15025,17.04813],[-89.14985,17.81563]]]]}},{type:"Feature",properties:{iso1A2:"GU",iso1A3:"GUM",iso1N3:"316",wikidata:"Q16635",nameEn:"Guam",country:"US",groups:["057","009"],roadSpeedUnit:"mph",callingCodes:["1 671"]},geometry:{type:"MultiPolygon",coordinates:[[[[146.25931,13.85876],[143.82485,13.92273],[144.61642,12.82462],[146.25931,13.85876]]]]}},{type:"Feature",properties:{iso1A2:"GW",iso1A3:"GNB",iso1N3:"624",wikidata:"Q1007",nameEn:"Guinea-Bissau",groups:["011","202","002"],callingCodes:["245"]},geometry:{type:"MultiPolygon",coordinates:[[[[-14.31513,11.60713],[-14.26623,11.67486],[-14.09799,11.63649],[-13.7039,11.70195],[-13.7039,12.00869],[-13.94589,12.16869],[-13.92745,12.24077],[-13.70851,12.24978],[-13.64168,12.42764],[-13.65089,12.49515],[-13.7039,12.60313],[-13.70523,12.68013],[-15.17582,12.6847],[-15.67302,12.42974],[-16.20591,12.46157],[-16.38191,12.36449],[-16.70562,12.34803],[-17.4623,11.92379],[-15.96748,10.162],[-15.07174,10.89557],[-14.95993,10.99244],[-14.77786,11.36323],[-14.66677,11.51188],[-14.51173,11.49708],[-14.31513,11.60713]]]]}},{type:"Feature",properties:{iso1A2:"GY",iso1A3:"GUY",iso1N3:"328",wikidata:"Q734",nameEn:"Guyana",groups:["005","419","019"],driveSide:"left",callingCodes:["592"]},geometry:{type:"MultiPolygon",coordinates:[[[[-56.84822,6.73257],[-59.54058,8.6862],[-59.98508,8.53046],[-59.85562,8.35213],[-59.80661,8.28906],[-59.83156,8.23261],[-59.97059,8.20791],[-60.02407,8.04557],[-60.38056,7.8302],[-60.51959,7.83373],[-60.64793,7.56877],[-60.71923,7.55817],[-60.59802,7.33194],[-60.63367,7.25061],[-60.54098,7.14804],[-60.44116,7.20817],[-60.28074,7.1162],[-60.39419,6.94847],[-60.54873,6.8631],[-61.13632,6.70922],[-61.20762,6.58174],[-61.15058,6.19558],[-61.4041,5.95304],[-60.73204,5.20931],[-60.32352,5.21299],[-60.20944,5.28754],[-59.98129,5.07097],[-60.04189,4.69801],[-60.15953,4.53456],[-59.78878,4.45637],[-59.69361,4.34069],[-59.73353,4.20399],[-59.51963,3.91951],[-59.86899,3.57089],[-59.79769,3.37162],[-59.99733,2.92312],[-59.91177,2.36759],[-59.7264,2.27497],[-59.74066,1.87596],[-59.25583,1.40559],[-58.92072,1.31293],[-58.84229,1.17749],[-58.53571,1.29154],[-58.4858,1.48399],[-58.33887,1.58014],[-58.01873,1.51966],[-57.97336,1.64566],[-57.77281,1.73344],[-57.55743,1.69605],[-57.35073,1.98327],[-57.23981,1.95808],[-57.09109,2.01854],[-57.07092,1.95304],[-56.7659,1.89509],[-56.47045,1.95135],[-56.55439,2.02003],[-56.70519,2.02964],[-57.35891,3.32121],[-58.0307,3.95513],[-57.8699,4.89394],[-57.37442,5.0208],[-57.22536,5.15605],[-57.31629,5.33714],[-56.84822,6.73257]]]]}},{type:"Feature",properties:{iso1A2:"HK",iso1A3:"HKG",iso1N3:"344",wikidata:"Q8646",nameEn:"Hong Kong",country:"CN",groups:["030","142"],driveSide:"left",callingCodes:["852"]},geometry:{type:"MultiPolygon",coordinates:[[[[113.92195,22.13873],[114.50148,22.15017],[114.44998,22.55977],[114.25154,22.55977],[114.22888,22.5436],[114.22185,22.55343],[114.20655,22.55706],[114.18338,22.55444],[114.17247,22.55944],[114.1597,22.56041],[114.15123,22.55163],[114.1482,22.54091],[114.13823,22.54319],[114.12665,22.54003],[114.11656,22.53415],[114.11181,22.52878],[114.1034,22.5352],[114.09692,22.53435],[114.09048,22.53716],[114.08606,22.53276],[114.07817,22.52997],[114.07267,22.51855],[114.06272,22.51617],[114.05729,22.51104],[114.05438,22.5026],[114.03113,22.5065],[113.86771,22.42972],[113.81621,22.2163],[113.83338,22.1826],[113.92195,22.13873]]]]}},{type:"Feature",properties:{iso1A2:"HM",iso1A3:"HMD",iso1N3:"334",wikidata:"Q131198",nameEn:"Heard Island and McDonald Islands",country:"AU",groups:["053","009"],driveSide:"left"},geometry:{type:"MultiPolygon",coordinates:[[[[71.08716,-53.87687],[75.44182,-53.99822],[72.87012,-51.48322],[71.08716,-53.87687]]]]}},{type:"Feature",properties:{iso1A2:"HN",iso1A3:"HND",iso1N3:"340",wikidata:"Q783",nameEn:"Honduras",groups:["013","003","419","019"],callingCodes:["504"]},geometry:{type:"MultiPolygon",coordinates:[[[[-83.86109,17.73736],[-88.20359,16.03858],[-88.22552,15.72294],[-88.24022,15.69247],[-88.31459,15.66942],[-88.32467,15.63665],[-88.97343,15.14039],[-89.15149,15.07392],[-89.18048,14.99967],[-89.15149,14.97775],[-89.23467,14.85596],[-89.13132,14.71582],[-89.15653,14.57802],[-89.23719,14.58046],[-89.35189,14.47553],[-89.34776,14.43013],[-89.04187,14.33644],[-88.94608,14.20207],[-88.85785,14.17763],[-88.815,14.11652],[-88.73182,14.10919],[-88.70661,14.04317],[-88.49738,13.97224],[-88.48982,13.86458],[-88.25791,13.91108],[-88.23018,13.99915],[-88.07641,13.98447],[-88.00331,13.86948],[-87.7966,13.91353],[-87.68821,13.80829],[-87.73106,13.75443],[-87.78148,13.52906],[-87.71657,13.50577],[-87.72115,13.46083],[-87.73841,13.44169],[-87.77354,13.45767],[-87.83467,13.44655],[-87.84675,13.41078],[-87.80177,13.35689],[-87.73714,13.32715],[-87.69751,13.25228],[-87.55124,13.12523],[-87.37107,12.98646],[-87.06306,13.00892],[-87.03785,12.98682],[-86.93197,13.05313],[-86.93383,13.18677],[-86.87066,13.30641],[-86.71267,13.30348],[-86.76812,13.79605],[-86.35219,13.77157],[-86.14801,14.04317],[-86.00685,14.08474],[-86.03458,13.99181],[-85.75477,13.8499],[-85.73964,13.9698],[-85.45762,14.11304],[-85.32149,14.2562],[-85.18602,14.24929],[-85.1575,14.53934],[-84.90082,14.80489],[-84.82596,14.82212],[-84.70119,14.68078],[-84.48373,14.63249],[-84.10584,14.76353],[-83.89551,14.76697],[-83.62101,14.89448],[-83.49268,15.01158],[-83.13724,15.00002],[-83.04763,15.03256],[-82.06974,14.49418],[-81.58685,18.0025],[-83.86109,17.73736]]]]}},{type:"Feature",properties:{iso1A2:"HR",iso1A3:"HRV",iso1N3:"191",wikidata:"Q224",nameEn:"Croatia",groups:["EU","039","150"],callingCodes:["385"]},geometry:{type:"MultiPolygon",coordinates:[[[[17.6444,42.88641],[17.5392,42.92787],[17.70879,42.97223],[17.64268,43.08595],[17.46986,43.16559],[17.286,43.33065],[17.25579,43.40353],[17.29699,43.44542],[17.24411,43.49376],[17.15828,43.49376],[17.00585,43.58037],[16.80736,43.76011],[16.75316,43.77157],[16.70922,43.84887],[16.55472,43.95326],[16.50528,44.0244],[16.43629,44.02826],[16.43662,44.07523],[16.36864,44.08263],[16.18688,44.27012],[16.21346,44.35231],[16.12969,44.38275],[16.16814,44.40679],[16.10566,44.52586],[16.03012,44.55572],[16.00884,44.58605],[16.05828,44.61538],[15.89348,44.74964],[15.8255,44.71501],[15.72584,44.82334],[15.79472,44.8455],[15.76096,44.87045],[15.74723,44.96818],[15.78568,44.97401],[15.74585,45.0638],[15.78842,45.11519],[15.76371,45.16508],[15.83512,45.22459],[15.98412,45.23088],[16.12153,45.09616],[16.29036,44.99732],[16.35404,45.00241],[16.35863,45.03529],[16.3749,45.05206],[16.38219,45.05139],[16.38309,45.05955],[16.40023,45.1147],[16.4634,45.14522],[16.49155,45.21153],[16.52982,45.22713],[16.5501,45.2212],[16.56559,45.22307],[16.60194,45.23042],[16.64962,45.20714],[16.74845,45.20393],[16.78219,45.19002],[16.81137,45.18434],[16.83804,45.18951],[16.92405,45.27607],[16.9385,45.22742],[17.0415,45.20759],[17.18438,45.14764],[17.24325,45.146],[17.25131,45.14957],[17.26815,45.18444],[17.32092,45.16246],[17.33573,45.14521],[17.41229,45.13335],[17.4498,45.16119],[17.45615,45.12523],[17.47589,45.12656],[17.51469,45.10791],[17.59104,45.10816],[17.66571,45.13408],[17.84826,45.04489],[17.87148,45.04645],[17.93706,45.08016],[17.97336,45.12245],[17.97834,45.13831],[17.99479,45.14958],[18.01594,45.15163],[18.03121,45.12632],[18.1624,45.07654],[18.24387,45.13699],[18.32077,45.1021],[18.41896,45.11083],[18.47939,45.05871],[18.65723,45.07544],[18.78357,44.97741],[18.80661,44.93561],[18.76369,44.93707],[18.76347,44.90669],[18.8704,44.85097],[19.01994,44.85493],[18.98957,44.90645],[19.02871,44.92541],[19.06853,44.89915],[19.15573,44.95409],[19.05205,44.97692],[19.1011,45.01191],[19.07952,45.14668],[19.14063,45.12972],[19.19144,45.17863],[19.43589,45.17137],[19.41941,45.23475],[19.28208,45.23813],[19.10774,45.29547],[18.97446,45.37528],[18.99918,45.49333],[19.08364,45.48804],[19.07471,45.53086],[18.94562,45.53712],[18.88776,45.57253],[18.96691,45.66731],[18.90305,45.71863],[18.85783,45.85493],[18.81394,45.91329],[18.80211,45.87995],[18.6792,45.92057],[18.57483,45.80772],[18.44368,45.73972],[18.12439,45.78905],[18.08869,45.76511],[17.99805,45.79671],[17.87377,45.78522],[17.66545,45.84207],[17.56821,45.93728],[17.35672,45.95209],[17.14592,46.16697],[16.8903,46.28122],[16.8541,46.36255],[16.7154,46.39523],[16.6639,46.45203],[16.59527,46.47524],[16.52604,46.47831],[16.5007,46.49644],[16.44036,46.5171],[16.38771,46.53608],[16.37193,46.55008],[16.29793,46.5121],[16.26733,46.51505],[16.26759,46.50566],[16.23961,46.49653],[16.25124,46.48067],[16.27398,46.42875],[16.27329,46.41467],[16.30162,46.40437],[16.30233,46.37837],[16.18824,46.38282],[16.14859,46.40547],[16.05281,46.39141],[16.05065,46.3833],[16.07314,46.36458],[16.07616,46.3463],[15.97965,46.30652],[15.79284,46.25811],[15.78817,46.21719],[15.75479,46.20336],[15.75436,46.21969],[15.67395,46.22478],[15.6434,46.21396],[15.64904,46.19229],[15.59909,46.14761],[15.6083,46.11992],[15.62317,46.09103],[15.72977,46.04682],[15.71246,46.01196],[15.70327,46.00015],[15.70636,45.92116],[15.67967,45.90455],[15.68383,45.88867],[15.68232,45.86819],[15.70411,45.8465],[15.66662,45.84085],[15.64185,45.82915],[15.57952,45.84953],[15.52234,45.82195],[15.47325,45.8253],[15.47531,45.79802],[15.40836,45.79491],[15.25423,45.72275],[15.30872,45.69014],[15.34919,45.71623],[15.4057,45.64727],[15.38952,45.63682],[15.34214,45.64702],[15.34695,45.63382],[15.31027,45.6303],[15.27747,45.60504],[15.29837,45.5841],[15.30249,45.53224],[15.38188,45.48752],[15.33051,45.45258],[15.27758,45.46678],[15.16862,45.42309],[15.05187,45.49079],[15.02385,45.48533],[14.92266,45.52788],[14.90554,45.47769],[14.81992,45.45913],[14.80124,45.49515],[14.71718,45.53442],[14.68605,45.53006],[14.69694,45.57366],[14.59576,45.62812],[14.60977,45.66403],[14.57397,45.67165],[14.53816,45.6205],[14.5008,45.60852],[14.49769,45.54424],[14.36693,45.48642],[14.32487,45.47142],[14.27681,45.4902],[14.26611,45.48239],[14.24239,45.50607],[14.22371,45.50388],[14.20348,45.46896],[14.07116,45.48752],[14.00578,45.52352],[13.96063,45.50825],[13.99488,45.47551],[13.97309,45.45258],[13.90771,45.45149],[13.88124,45.42637],[13.81742,45.43729],[13.7785,45.46787],[13.67398,45.4436],[13.62902,45.45898],[13.56979,45.4895],[13.45644,45.59464],[13.05142,45.33128],[13.12821,44.48877],[16.15283,42.18525],[18.45131,42.21682],[18.54128,42.39171],[18.52152,42.42302],[18.43588,42.48556],[18.44307,42.51077],[18.43735,42.55921],[18.36197,42.61423],[18.24318,42.6112],[17.88201,42.83668],[17.80854,42.9182],[17.7948,42.89556],[17.68151,42.92725],[17.6444,42.88641]]]]}},{type:"Feature",properties:{iso1A2:"HT",iso1A3:"HTI",iso1N3:"332",wikidata:"Q790",nameEn:"Haiti",aliases:["RH"],groups:["029","003","419","019"],callingCodes:["509"]},geometry:{type:"MultiPolygon",coordinates:[[[[-71.71885,18.78423],[-71.72624,18.87802],[-71.77766,18.95007],[-71.88102,18.95007],[-71.74088,19.0437],[-71.71088,19.08353],[-71.69938,19.10916],[-71.65337,19.11759],[-71.62642,19.21212],[-71.73229,19.26686],[-71.77766,19.33823],[-71.69448,19.37866],[-71.6802,19.45008],[-71.71268,19.53374],[-71.71449,19.55364],[-71.7429,19.58445],[-71.75865,19.70231],[-71.77419,19.73128],[-72.38946,20.27111],[-73.37289,20.43199],[-74.7289,18.71009],[-74.76465,18.06252],[-72.29523,17.48026],[-71.75671,18.03456],[-71.73783,18.07177],[-71.74994,18.11115],[-71.75465,18.14405],[-71.78271,18.18302],[-71.69952,18.34101],[-71.90875,18.45821],[-71.88102,18.50125],[-72.00201,18.62312],[-71.95412,18.64939],[-71.82556,18.62551],[-71.71885,18.78423]]]]}},{type:"Feature",properties:{iso1A2:"HU",iso1A3:"HUN",iso1N3:"348",wikidata:"Q28",nameEn:"Hungary",groups:["EU","151","150"],callingCodes:["36"]},geometry:{type:"MultiPolygon",coordinates:[[[[21.72525,48.34628],[21.67134,48.3989],[21.6068,48.50365],[21.44063,48.58456],[21.11516,48.49546],[20.83248,48.5824],[20.5215,48.53336],[20.29943,48.26104],[20.24312,48.2784],[19.92452,48.1283],[19.63338,48.25006],[19.52489,48.19791],[19.47957,48.09437],[19.28182,48.08336],[19.23924,48.0595],[19.01952,48.07052],[18.82176,48.04206],[18.76134,47.97499],[18.76821,47.87469],[18.8506,47.82308],[18.74074,47.8157],[18.66521,47.76772],[18.56496,47.76588],[18.29305,47.73541],[18.02938,47.75665],[17.71215,47.7548],[17.23699,48.02094],[17.16001,48.00636],[17.09786,47.97336],[17.11022,47.92461],[17.08275,47.87719],[17.00997,47.86245],[17.07039,47.81129],[17.05048,47.79377],[17.08893,47.70928],[16.87538,47.68895],[16.86509,47.72268],[16.82938,47.68432],[16.7511,47.67878],[16.72089,47.73469],[16.65679,47.74197],[16.61183,47.76171],[16.54779,47.75074],[16.53514,47.73837],[16.55129,47.72268],[16.4222,47.66537],[16.58699,47.61772],[16.64193,47.63114],[16.71059,47.52692],[16.64821,47.50155],[16.6718,47.46139],[16.57152,47.40868],[16.52414,47.41007],[16.49908,47.39416],[16.45104,47.41181],[16.47782,47.25918],[16.44142,47.25079],[16.43663,47.21127],[16.41739,47.20649],[16.42801,47.18422],[16.4523,47.18812],[16.46442,47.16845],[16.44932,47.14418],[16.52863,47.13974],[16.46134,47.09395],[16.52176,47.05747],[16.43936,47.03548],[16.51369,47.00084],[16.28202,47.00159],[16.27594,46.9643],[16.22403,46.939],[16.19904,46.94134],[16.10983,46.867],[16.14365,46.8547],[16.15711,46.85434],[16.21892,46.86961],[16.2365,46.87775],[16.2941,46.87137],[16.34547,46.83836],[16.3408,46.80641],[16.31303,46.79838],[16.30966,46.7787],[16.37816,46.69975],[16.42641,46.69228],[16.41863,46.66238],[16.38594,46.6549],[16.39217,46.63673],[16.50139,46.56684],[16.52885,46.53303],[16.52604,46.5051],[16.59527,46.47524],[16.6639,46.45203],[16.7154,46.39523],[16.8541,46.36255],[16.8903,46.28122],[17.14592,46.16697],[17.35672,45.95209],[17.56821,45.93728],[17.66545,45.84207],[17.87377,45.78522],[17.99805,45.79671],[18.08869,45.76511],[18.12439,45.78905],[18.44368,45.73972],[18.57483,45.80772],[18.6792,45.92057],[18.80211,45.87995],[18.81394,45.91329],[18.99712,45.93537],[19.01284,45.96529],[19.0791,45.96458],[19.10388,46.04015],[19.14543,45.9998],[19.28826,45.99694],[19.52473,46.1171],[19.56113,46.16824],[19.66007,46.19005],[19.81491,46.1313],[19.93508,46.17553],[20.01816,46.17696],[20.03533,46.14509],[20.09713,46.17315],[20.26068,46.12332],[20.28324,46.1438],[20.35573,46.16629],[20.45377,46.14405],[20.49718,46.18721],[20.63863,46.12728],[20.76085,46.21002],[20.74574,46.25467],[20.86797,46.28884],[21.06572,46.24897],[21.16872,46.30118],[21.28061,46.44941],[21.26929,46.4993],[21.33214,46.63035],[21.43926,46.65109],[21.5151,46.72147],[21.48935,46.7577],[21.52028,46.84118],[21.59307,46.86935],[21.59581,46.91628],[21.68645,46.99595],[21.648,47.03902],[21.78395,47.11104],[21.94463,47.38046],[22.01055,47.37767],[22.03389,47.42508],[22.00917,47.50492],[22.31816,47.76126],[22.41979,47.7391],[22.46559,47.76583],[22.67247,47.7871],[22.76617,47.8417],[22.77991,47.87211],[22.89849,47.95851],[22.84276,47.98602],[22.87847,48.04665],[22.81804,48.11363],[22.73427,48.12005],[22.66835,48.09162],[22.58733,48.10813],[22.59007,48.15121],[22.49806,48.25189],[22.38133,48.23726],[22.2083,48.42534],[22.14689,48.4005],[21.83339,48.36242],[21.8279,48.33321],[21.72525,48.34628]]]]}},{type:"Feature",properties:{iso1A2:"IC",wikidata:"Q5813",nameEn:"Canary Islands",country:"ES",groups:["EU","039","150"],isoStatus:"excRes",callingCodes:["34"]},geometry:{type:"MultiPolygon",coordinates:[[[[-15.92339,29.50503],[-25.3475,27.87574],[-14.43883,27.02969],[-9.94494,32.97138],[-15.92339,29.50503]]]]}},{type:"Feature",properties:{iso1A2:"ID",iso1A3:"IDN",iso1N3:"360",wikidata:"Q252",nameEn:"Indonesia",aliases:["RI"],groups:["035","142"],driveSide:"left",callingCodes:["62"]},geometry:{type:"MultiPolygon",coordinates:[[[[141.02352,0.08993],[128.97621,3.08804],[126.69413,6.02692],[124.97752,4.82064],[118.41402,3.99509],[118.07935,4.15511],[117.89538,4.16637],[117.67641,4.16535],[117.47313,4.18857],[117.25801,4.35108],[115.90217,4.37708],[115.58276,3.93499],[115.53713,3.14776],[115.11343,2.82879],[115.1721,2.49671],[114.80706,2.21665],[114.80706,1.92351],[114.57892,1.5],[114.03788,1.44787],[113.64677,1.23933],[113.01448,1.42832],[113.021,1.57819],[112.48648,1.56516],[112.2127,1.44135],[112.15679,1.17004],[111.94553,1.12016],[111.82846,0.99349],[111.55434,0.97864],[111.22979,1.08326],[110.62374,0.873],[110.49182,0.88088],[110.35354,0.98869],[109.66397,1.60425],[109.66397,1.79972],[109.57923,1.80624],[109.53794,1.91771],[109.62558,1.99182],[109.64506,2.08014],[109.71058,2.32059],[108.10426,5.42408],[105.01437,3.24936],[104.56723,1.44271],[104.34728,1.33529],[104.12282,1.27714],[104.03085,1.26954],[103.74084,1.12902],[103.66049,1.18825],[103.56591,1.19719],[103.03657,1.30383],[96.11174,6.69841],[74.28481,-3.17525],[122.14954,-11.52517],[125.68138,-9.85176],[125.09025,-9.46406],[124.97892,-9.19281],[125.04044,-9.17093],[125.09434,-9.19669],[125.18907,-9.16434],[125.18632,-9.03142],[125.11764,-8.96359],[124.97742,-9.08128],[124.94011,-8.85617],[124.46701,-9.13002],[124.45971,-9.30263],[124.38554,-9.3582],[124.35258,-9.43002],[124.3535,-9.48493],[124.28115,-9.50453],[124.28115,-9.42189],[124.21247,-9.36904],[124.14517,-9.42324],[124.10539,-9.41206],[124.04286,-9.34243],[124.04628,-9.22671],[124.33472,-9.11416],[124.92337,-8.75859],[125.31127,-8.22976],[125.65946,-8.06136],[125.87691,-8.31789],[127.42116,-8.22471],[127.55165,-9.05052],[140.88922,-9.34945],[141.00782,-9.1242],[141.01763,-6.90181],[140.85295,-6.72996],[140.99813,-6.3233],[141.02352,0.08993]]]]}},{type:"Feature",properties:{iso1A2:"IE",iso1A3:"IRL",iso1N3:"372",wikidata:"Q27",nameEn:"Ireland",groups:["EU","154","150"],driveSide:"left",callingCodes:["353"]},geometry:{type:"MultiPolygon",coordinates:[[[[-6.26218,54.09785],[-6.29003,54.11278],[-6.32694,54.09337],[-6.36279,54.11248],[-6.36605,54.07234],[-6.47849,54.06947],[-6.62842,54.03503],[-6.66264,54.0666],[-6.6382,54.17071],[-6.70175,54.20218],[-6.74575,54.18788],[-6.81583,54.22791],[-6.85179,54.29176],[-6.87775,54.34682],[-7.02034,54.4212],[-7.19145,54.31296],[-7.14908,54.22732],[-7.25012,54.20063],[-7.26316,54.13863],[-7.29493,54.12013],[-7.29687,54.1354],[-7.28017,54.16714],[-7.29157,54.17191],[-7.34005,54.14698],[-7.30553,54.11869],[-7.32834,54.11475],[-7.44567,54.1539],[-7.4799,54.12239],[-7.55812,54.12239],[-7.69501,54.20731],[-7.81397,54.20159],[-7.8596,54.21779],[-7.87101,54.29299],[-8.04555,54.36292],[-8.179,54.46763],[-8.04538,54.48941],[-7.99812,54.54427],[-7.8596,54.53671],[-7.70315,54.62077],[-7.93293,54.66603],[-7.83352,54.73854],[-7.75041,54.7103],[-7.64449,54.75265],[-7.54671,54.74606],[-7.54508,54.79401],[-7.47626,54.83084],[-7.4473,54.87003],[-7.44404,54.9403],[-7.40004,54.94498],[-7.4033,55.00391],[-7.34464,55.04688],[-7.2471,55.06933],[-6.9734,55.19878],[-6.71944,55.27952],[-6.79943,55.54107],[-7.93366,55.84142],[-22.01468,48.19557],[-5.79914,52.03902],[-5.37267,53.63269],[-5.83481,53.87749],[-6.26218,54.09785]]]]}},{type:"Feature",properties:{iso1A2:"IL",iso1A3:"ISR",iso1N3:"376",wikidata:"Q801",nameEn:"Israel",groups:["145","142"],callingCodes:["972"]},geometry:{type:"MultiPolygon",coordinates:[[[[34.052,31.46619],[34.29262,31.70393],[34.48681,31.59711],[34.56797,31.54197],[34.48892,31.48365],[34.40077,31.40926],[34.36505,31.36404],[34.37381,31.30598],[34.36523,31.28963],[34.29417,31.24194],[34.26742,31.21998],[34.92298,29.45305],[34.97718,29.54294],[34.98207,29.58147],[35.02147,29.66343],[35.14108,30.07374],[35.19183,30.34636],[35.16218,30.43535],[35.19595,30.50297],[35.21379,30.60401],[35.29311,30.71365],[35.33456,30.81224],[35.33984,30.8802],[35.41371,30.95565],[35.43658,31.12444],[35.40316,31.25535],[35.47672,31.49578],[35.39675,31.49572],[35.22921,31.37445],[35.13033,31.3551],[35.02459,31.35979],[34.92571,31.34337],[34.88932,31.37093],[34.87833,31.39321],[34.89756,31.43891],[34.93258,31.47816],[34.94356,31.50743],[34.9415,31.55601],[34.95249,31.59813],[35.00879,31.65426],[35.08226,31.69107],[35.10782,31.71594],[35.11895,31.71454],[35.12933,31.7325],[35.13931,31.73012],[35.15119,31.73634],[35.15474,31.73352],[35.16478,31.73242],[35.18023,31.72067],[35.20538,31.72388],[35.21937,31.71578],[35.22392,31.71899],[35.23972,31.70896],[35.24315,31.71244],[35.2438,31.7201],[35.24981,31.72543],[35.25182,31.73945],[35.26319,31.74846],[35.25225,31.7678],[35.26058,31.79064],[35.25573,31.81362],[35.26404,31.82567],[35.251,31.83085],[35.25753,31.8387],[35.24816,31.8458],[35.2304,31.84222],[35.2249,31.85433],[35.22817,31.8638],[35.22567,31.86745],[35.22294,31.87889],[35.22014,31.88264],[35.2136,31.88241],[35.21276,31.88153],[35.21016,31.88237],[35.20945,31.8815],[35.20791,31.8821],[35.20673,31.88151],[35.20381,31.86716],[35.21128,31.863],[35.216,31.83894],[35.21469,31.81835],[35.19461,31.82687],[35.18169,31.82542],[35.18603,31.80901],[35.14174,31.81325],[35.07677,31.85627],[35.05617,31.85685],[35.01978,31.82944],[34.9724,31.83352],[34.99712,31.85569],[35.03489,31.85919],[35.03978,31.89276],[35.03489,31.92448],[35.00124,31.93264],[34.98682,31.96935],[35.00261,32.027],[34.9863,32.09551],[34.99437,32.10962],[34.98507,32.12606],[34.99039,32.14626],[34.96009,32.17503],[34.95703,32.19522],[34.98885,32.20758],[35.01841,32.23981],[35.02939,32.2671],[35.01119,32.28684],[35.01772,32.33863],[35.04243,32.35008],[35.05142,32.3667],[35.0421,32.38242],[35.05311,32.4024],[35.05423,32.41754],[35.07059,32.4585],[35.08564,32.46948],[35.09236,32.47614],[35.10024,32.47856],[35.10882,32.4757],[35.15937,32.50466],[35.2244,32.55289],[35.25049,32.52453],[35.29306,32.50947],[35.30685,32.51024],[35.35212,32.52047],[35.40224,32.50136],[35.42034,32.46009],[35.41598,32.45593],[35.41048,32.43706],[35.42078,32.41562],[35.55807,32.38674],[35.55494,32.42687],[35.57485,32.48669],[35.56614,32.64393],[35.59813,32.65159],[35.61669,32.67999],[35.66527,32.681],[35.68467,32.70715],[35.75983,32.74803],[35.78745,32.77938],[35.83758,32.82817],[35.84021,32.8725],[35.87012,32.91976],[35.89298,32.9456],[35.87188,32.98028],[35.84802,33.1031],[35.81911,33.11077],[35.81911,33.1336],[35.84285,33.16673],[35.83846,33.19397],[35.81647,33.2028],[35.81295,33.24841],[35.77513,33.27342],[35.813,33.3172],[35.77477,33.33609],[35.62019,33.27278],[35.62283,33.24226],[35.58502,33.26653],[35.58326,33.28381],[35.56523,33.28969],[35.55555,33.25844],[35.54544,33.25513],[35.54808,33.236],[35.5362,33.23196],[35.54228,33.19865],[35.52573,33.11921],[35.50335,33.114],[35.50272,33.09056],[35.448,33.09264],[35.43059,33.06659],[35.35223,33.05617],[35.31429,33.10515],[35.1924,33.08743],[35.10645,33.09318],[34.78515,33.20368],[33.62659,31.82938],[34.052,31.46619]]]]}},{type:"Feature",properties:{iso1A2:"IM",iso1A3:"IMN",iso1N3:"833",wikidata:"Q9676",nameEn:"Isle of Man",country:"GB",groups:["154","150"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["44 01624","44 07624","44 07524","44 07924"]},geometry:{type:"MultiPolygon",coordinates:[[[[-3.64906,54.12723],[-4.1819,54.57861],[-5.83481,53.87749],[-5.37267,53.63269],[-3.64906,54.12723]]]]}},{type:"Feature",properties:{iso1A2:"IN",iso1A3:"IND",iso1N3:"356",wikidata:"Q668",nameEn:"India",groups:["034","142"],driveSide:"left",callingCodes:["91"]},geometry:{type:"MultiPolygon",coordinates:[[[[78.11664,35.48022],[77.80532,35.52058],[77.70232,35.46244],[77.44277,35.46132],[76.96624,35.5932],[76.84539,35.67356],[76.77323,35.66062],[76.75475,35.52617],[76.85088,35.39754],[76.93465,35.39866],[77.11796,35.05419],[76.99251,34.93349],[76.87193,34.96906],[76.74514,34.92488],[76.74377,34.84039],[76.67648,34.76371],[76.47186,34.78965],[76.15463,34.6429],[76.04614,34.67566],[75.75438,34.51827],[75.38009,34.55021],[75.01479,34.64629],[74.6663,34.703],[74.58083,34.77386],[74.31239,34.79626],[74.12897,34.70073],[73.96423,34.68244],[73.93401,34.63386],[73.93951,34.57169],[73.89419,34.54568],[73.88732,34.48911],[73.74999,34.3781],[73.74862,34.34183],[73.8475,34.32935],[73.90517,34.35317],[73.98208,34.2522],[73.90677,34.10504],[73.88732,34.05105],[73.91341,34.01235],[74.21554,34.03853],[74.25262,34.01577],[74.26086,33.92237],[74.14001,33.83002],[74.05898,33.82089],[74.00891,33.75437],[73.96423,33.73071],[73.98968,33.66155],[73.97367,33.64061],[74.03576,33.56718],[74.10115,33.56392],[74.18121,33.4745],[74.17983,33.3679],[74.08782,33.26232],[74.01366,33.25199],[74.02144,33.18908],[74.15374,33.13477],[74.17571,33.07495],[74.31854,33.02891],[74.34875,32.97823],[74.31227,32.92795],[74.41467,32.90563],[74.45312,32.77755],[74.6289,32.75561],[74.64675,32.82604],[74.7113,32.84219],[74.65345,32.71225],[74.69542,32.66792],[74.64424,32.60985],[74.65251,32.56416],[74.67431,32.56676],[74.68362,32.49298],[74.84725,32.49075],[74.97634,32.45367],[75.03265,32.49538],[75.28259,32.36556],[75.38046,32.26836],[75.25649,32.10187],[75.00793,32.03786],[74.9269,32.0658],[74.86236,32.04485],[74.79919,31.95983],[74.58907,31.87824],[74.47771,31.72227],[74.57498,31.60382],[74.61517,31.55698],[74.59319,31.50197],[74.64713,31.45605],[74.59773,31.4136],[74.53223,31.30321],[74.51629,31.13829],[74.56023,31.08303],[74.60281,31.10419],[74.60006,31.13711],[74.6852,31.12771],[74.67971,31.05479],[74.5616,31.04153],[73.88993,30.36305],[73.95736,30.28466],[73.97225,30.19829],[73.80299,30.06969],[73.58665,30.01848],[73.3962,29.94707],[73.28094,29.56646],[73.05886,29.1878],[73.01337,29.16422],[72.94272,29.02487],[72.40402,28.78283],[72.29495,28.66367],[72.20329,28.3869],[71.9244,28.11555],[71.89921,27.96035],[70.79054,27.68423],[70.60927,28.02178],[70.37307,28.01208],[70.12502,27.8057],[70.03136,27.56627],[69.58519,27.18109],[69.50904,26.74892],[69.88555,26.56836],[70.05584,26.60398],[70.17532,26.55362],[70.17532,26.24118],[70.08193,26.08094],[70.0985,25.93238],[70.2687,25.71156],[70.37444,25.67443],[70.53649,25.68928],[70.60378,25.71898],[70.67382,25.68186],[70.66695,25.39314],[70.89148,25.15064],[70.94002,24.92843],[71.09405,24.69017],[70.97594,24.60904],[71.00341,24.46038],[71.12838,24.42662],[71.04461,24.34657],[70.94985,24.3791],[70.85784,24.30903],[70.88393,24.27398],[70.71502,24.23517],[70.57906,24.27774],[70.5667,24.43787],[70.11712,24.30915],[70.03428,24.172],[69.73335,24.17007],[69.59579,24.29777],[69.29778,24.28712],[69.19341,24.25646],[69.07806,24.29777],[68.97781,24.26021],[68.90914,24.33156],[68.7416,24.31904],[68.74643,23.97027],[68.39339,23.96838],[68.20763,23.85849],[68.11329,23.53945],[72.15131,7.6285],[78.52781,7.63099],[79.50447,8.91876],[79.42124,9.80115],[80.48418,10.20786],[94.53911,5.99016],[94.6371,13.81803],[92.61042,13.76986],[89.13606,21.42955],[89.13927,21.60785],[89.03553,21.77397],[89.07114,22.15335],[88.9367,22.58527],[88.94614,22.66941],[88.9151,22.75228],[88.96713,22.83346],[88.87063,22.95235],[88.88327,23.03885],[88.86377,23.08759],[88.99148,23.21134],[88.71133,23.2492],[88.79254,23.46028],[88.79351,23.50535],[88.74841,23.47361],[88.56507,23.64044],[88.58087,23.87105],[88.66189,23.87607],[88.73743,23.91751],[88.6976,24.14703],[88.74841,24.1959],[88.68801,24.31464],[88.50934,24.32474],[88.12296,24.51301],[88.08786,24.63232],[88.00683,24.66477],[88.15515,24.85806],[88.14004,24.93529],[88.21832,24.96642],[88.27325,24.88796],[88.33917,24.86803],[88.46277,25.07468],[88.44766,25.20149],[88.94067,25.18534],[89.00463,25.26583],[89.01105,25.30303],[88.85278,25.34679],[88.81296,25.51546],[88.677,25.46959],[88.4559,25.59227],[88.45103,25.66245],[88.242,25.80811],[88.13138,25.78773],[88.08804,25.91334],[88.16581,26.0238],[88.1844,26.14417],[88.34757,26.22216],[88.35153,26.29123],[88.51649,26.35923],[88.48749,26.45855],[88.36938,26.48683],[88.35153,26.45241],[88.33093,26.48929],[88.41196,26.63837],[88.4298,26.54489],[88.62144,26.46783],[88.69485,26.38353],[88.67837,26.26291],[88.78961,26.31093],[88.85004,26.23211],[89.05328,26.2469],[88.91321,26.37984],[88.92357,26.40711],[88.95612,26.4564],[89.08899,26.38845],[89.15869,26.13708],[89.35953,26.0077],[89.53515,26.00382],[89.57101,25.9682],[89.63968,26.22595],[89.70201,26.15138],[89.73581,26.15818],[89.77865,26.08387],[89.77728,26.04254],[89.86592,25.93115],[89.80585,25.82489],[89.84388,25.70042],[89.86129,25.61714],[89.81208,25.37244],[89.84086,25.31854],[89.83371,25.29548],[89.87629,25.28337],[89.90478,25.31038],[90.1155,25.22686],[90.40034,25.1534],[90.65042,25.17788],[90.87427,25.15799],[91.25517,25.20677],[91.63648,25.12846],[92.0316,25.1834],[92.33957,25.07593],[92.39147,25.01471],[92.49887,24.88796],[92.38626,24.86055],[92.25854,24.9191],[92.15796,24.54435],[92.11662,24.38997],[91.96603,24.3799],[91.89258,24.14674],[91.82596,24.22345],[91.76004,24.23848],[91.73257,24.14703],[91.65292,24.22095],[91.63782,24.1132],[91.55542,24.08687],[91.37414,24.10693],[91.35741,23.99072],[91.29587,24.0041],[91.22308,23.89616],[91.25192,23.83463],[91.15579,23.6599],[91.28293,23.37538],[91.36453,23.06612],[91.40848,23.07117],[91.4035,23.27522],[91.46615,23.2328],[91.54993,23.01051],[91.61571,22.93929],[91.7324,23.00043],[91.81634,23.08001],[91.76417,23.26619],[91.84789,23.42235],[91.95642,23.47361],[91.95093,23.73284],[92.04706,23.64229],[92.15417,23.73409],[92.26541,23.70392],[92.38214,23.28705],[92.37665,22.9435],[92.5181,22.71441],[92.60029,22.1522],[92.56616,22.13554],[92.60949,21.97638],[92.67532,22.03547],[92.70416,22.16017],[92.86208,22.05456],[92.89504,21.95143],[92.93899,22.02656],[92.99804,21.98964],[92.99255,22.05965],[93.04885,22.20595],[93.15734,22.18687],[93.14224,22.24535],[93.19991,22.25425],[93.18206,22.43716],[93.13537,22.45873],[93.11477,22.54374],[93.134,22.59573],[93.09417,22.69459],[93.134,22.92498],[93.12988,23.05772],[93.2878,23.00464],[93.38478,23.13698],[93.36862,23.35426],[93.38781,23.36139],[93.39981,23.38828],[93.38805,23.4728],[93.43475,23.68299],[93.3908,23.7622],[93.3908,23.92925],[93.36059,23.93176],[93.32351,24.04468],[93.34735,24.10151],[93.41415,24.07854],[93.46633,23.97067],[93.50616,23.94432],[93.62871,24.00922],[93.75952,24.0003],[93.80279,23.92549],[93.92089,23.95812],[94.14081,23.83333],[94.30215,24.23752],[94.32362,24.27692],[94.45279,24.56656],[94.50729,24.59281],[94.5526,24.70764],[94.60204,24.70889],[94.73937,25.00545],[94.74212,25.13606],[94.57458,25.20318],[94.68032,25.47003],[94.80117,25.49359],[95.18556,26.07338],[95.11428,26.1019],[95.12801,26.38397],[95.05798,26.45408],[95.23513,26.68499],[95.30339,26.65372],[95.437,26.7083],[95.81603,27.01335],[95.93002,27.04149],[96.04949,27.19428],[96.15591,27.24572],[96.40779,27.29818],[96.55761,27.29928],[96.73888,27.36638],[96.88445,27.25046],[96.85287,27.2065],[96.89132,27.17474],[97.14675,27.09041],[97.17422,27.14052],[96.91431,27.45752],[96.90112,27.62149],[97.29919,27.92233],[97.35824,27.87256],[97.38845,28.01329],[97.35412,28.06663],[97.31292,28.06784],[97.34547,28.21385],[97.1289,28.3619],[96.98882,28.32564],[96.88445,28.39452],[96.85561,28.4875],[96.6455,28.61657],[96.48895,28.42955],[96.40929,28.51526],[96.61391,28.72742],[96.3626,29.10607],[96.20467,29.02325],[96.18682,29.11087],[96.31316,29.18643],[96.05361,29.38167],[95.84899,29.31464],[95.75149,29.32063],[95.72086,29.20797],[95.50842,29.13487],[95.41091,29.13007],[95.3038,29.13847],[95.26122,29.07727],[95.2214,29.10727],[95.11291,29.09527],[95.0978,29.14446],[94.81353,29.17804],[94.69318,29.31739],[94.2752,29.11687],[94.35897,29.01965],[93.72797,28.68821],[93.44621,28.67189],[93.18069,28.50319],[93.14635,28.37035],[92.93075,28.25671],[92.67486,28.15018],[92.65472,28.07632],[92.73025,28.05814],[92.7275,27.98662],[92.42538,27.80092],[92.32101,27.79363],[92.27432,27.89077],[91.87057,27.7195],[91.84722,27.76325],[91.6469,27.76358],[91.55819,27.6144],[91.65007,27.48287],[92.01132,27.47352],[92.12019,27.27829],[92.04702,27.26861],[92.03457,27.07334],[92.11863,26.893],[92.05523,26.8692],[91.83181,26.87318],[91.50067,26.79223],[90.67715,26.77215],[90.48504,26.8594],[90.39271,26.90704],[90.30402,26.85098],[90.04535,26.72422],[89.86124,26.73307],[89.63369,26.74402],[89.42349,26.83727],[89.3901,26.84225],[89.38319,26.85963],[89.37913,26.86224],[89.1926,26.81329],[89.12825,26.81661],[89.09554,26.89089],[88.95807,26.92668],[88.92301,26.99286],[88.8714,26.97488],[88.86984,27.10937],[88.74219,27.144],[88.91901,27.32483],[88.82981,27.38814],[88.77517,27.45415],[88.88091,27.85192],[88.83559,28.01936],[88.63235,28.12356],[88.54858,28.06057],[88.25332,27.9478],[88.1278,27.95417],[88.13378,27.88015],[88.1973,27.85067],[88.19107,27.79285],[88.04008,27.49223],[88.07277,27.43007],[88.01646,27.21612],[88.01587,27.21388],[87.9887,27.11045],[88.11719,26.98758],[88.13422,26.98705],[88.12302,26.95324],[88.19107,26.75516],[88.1659,26.68177],[88.16452,26.64111],[88.09963,26.54195],[88.09414,26.43732],[88.00895,26.36029],[87.90115,26.44923],[87.89085,26.48565],[87.84193,26.43663],[87.7918,26.46737],[87.76004,26.40711],[87.67893,26.43501],[87.66803,26.40294],[87.59175,26.38342],[87.55274,26.40596],[87.51571,26.43106],[87.46566,26.44058],[87.37314,26.40815],[87.34568,26.34787],[87.26568,26.37294],[87.26587,26.40592],[87.24682,26.4143],[87.18863,26.40558],[87.14751,26.40542],[87.09147,26.45039],[87.0707,26.58571],[87.04691,26.58685],[87.01559,26.53228],[86.95912,26.52076],[86.94543,26.52076],[86.82898,26.43919],[86.76797,26.45892],[86.74025,26.42386],[86.69124,26.45169],[86.62686,26.46891],[86.61313,26.48658],[86.57073,26.49825],[86.54258,26.53819],[86.49726,26.54218],[86.31564,26.61925],[86.26235,26.61886],[86.22513,26.58863],[86.13596,26.60651],[86.02729,26.66756],[85.8492,26.56667],[85.85126,26.60866],[85.83126,26.61134],[85.76907,26.63076],[85.72315,26.67471],[85.73483,26.79613],[85.66239,26.84822],[85.61621,26.86721],[85.59461,26.85161],[85.5757,26.85955],[85.56471,26.84133],[85.47752,26.79292],[85.34302,26.74954],[85.21159,26.75933],[85.18046,26.80519],[85.19291,26.86909],[85.15883,26.86966],[85.02635,26.85381],[85.05592,26.88991],[85.00536,26.89523],[84.97186,26.9149],[84.96687,26.95599],[84.85754,26.98984],[84.82913,27.01989],[84.793,26.9968],[84.64496,27.04669],[84.69166,27.21294],[84.62161,27.33885],[84.29315,27.39],[84.25735,27.44941],[84.21376,27.45218],[84.10791,27.52399],[84.02229,27.43836],[83.93306,27.44939],[83.86182,27.4241],[83.85595,27.35797],[83.61288,27.47013],[83.39495,27.4798],[83.38872,27.39276],[83.35136,27.33885],[83.29999,27.32778],[83.2673,27.36235],[83.27197,27.38309],[83.19413,27.45632],[82.94938,27.46036],[82.93261,27.50328],[82.74119,27.49838],[82.70378,27.72122],[82.46405,27.6716],[82.06554,27.92222],[81.97214,27.93322],[81.91223,27.84995],[81.47867,28.08303],[81.48179,28.12148],[81.38683,28.17638],[81.32923,28.13521],[81.19847,28.36284],[81.08507,28.38346],[80.89648,28.47237],[80.55142,28.69182],[80.50575,28.6706],[80.52443,28.54897],[80.44504,28.63098],[80.37188,28.63371],[80.12125,28.82346],[80.06957,28.82763],[80.05743,28.91479],[80.18085,29.13649],[80.23178,29.11626],[80.26602,29.13938],[80.24112,29.21414],[80.28626,29.20327],[80.31428,29.30784],[80.24322,29.44299],[80.37939,29.57098],[80.41858,29.63581],[80.38428,29.68513],[80.36803,29.73865],[80.41554,29.79451],[80.43458,29.80466],[80.48997,29.79566],[80.56247,29.86661],[80.56957,29.88176],[80.60226,29.95732],[80.67076,29.95732],[80.8778,30.13384],[80.93695,30.18229],[81.03953,30.20059],[80.83343,30.32023],[80.54504,30.44936],[80.20721,30.58541],[79.93255,30.88288],[79.59884,30.93943],[79.22805,31.34963],[79.14016,31.43403],[79.01931,31.42817],[78.77898,31.31209],[78.71032,31.50197],[78.84516,31.60631],[78.69933,31.78723],[78.78036,31.99478],[78.74404,32.00384],[78.68754,32.10256],[78.49609,32.2762],[78.4645,32.45367],[78.38897,32.53938],[78.73916,32.69438],[78.7831,32.46873],[78.96713,32.33655],[78.99322,32.37948],[79.0979,32.38051],[79.13174,32.47766],[79.26768,32.53277],[79.46562,32.69668],[79.14016,33.02545],[79.15252,33.17156],[78.73636,33.56521],[78.67599,33.66445],[78.77349,33.73871],[78.73367,34.01121],[78.65657,34.03195],[78.66225,34.08858],[78.91769,34.15452],[78.99802,34.3027],[79.05364,34.32482],[78.74465,34.45174],[78.56475,34.50835],[78.54964,34.57283],[78.27781,34.61484],[78.18435,34.7998],[78.22692,34.88771],[78.00033,35.23954],[78.03466,35.3785],[78.11664,35.48022]]]]}},{type:"Feature",properties:{iso1A2:"IO",iso1A3:"IOT",iso1N3:"086",wikidata:"Q43448",nameEn:"British Indian Ocean Territory",country:"GB",groups:["014","202","002"],callingCodes:["246"]},geometry:{type:"MultiPolygon",coordinates:[[[[70.64754,-4.95745],[70.67958,-8.2663],[73.70488,-4.92492],[70.64754,-4.95745]]]]}},{type:"Feature",properties:{iso1A2:"IQ",iso1A3:"IRQ",iso1N3:"368",wikidata:"Q796",nameEn:"Iraq",groups:["145","142"],callingCodes:["964"]},geometry:{type:"MultiPolygon",coordinates:[[[[42.78887,37.38615],[42.56725,37.14878],[42.35724,37.10998],[42.36697,37.0627],[41.81736,36.58782],[41.40058,36.52502],[41.28864,36.35368],[41.2564,36.06012],[41.37027,35.84095],[41.38184,35.62502],[41.26569,35.42708],[41.21654,35.1508],[41.2345,34.80049],[41.12388,34.65742],[40.97676,34.39788],[40.64314,34.31604],[38.79171,33.37328],[39.08202,32.50304],[38.98762,32.47694],[39.04251,32.30203],[39.26157,32.35555],[39.29903,32.23259],[40.01521,32.05667],[42.97601,30.72204],[42.97796,30.48295],[44.72255,29.19736],[46.42415,29.05947],[46.5527,29.10283],[46.89695,29.50584],[47.15166,30.01044],[47.37192,30.10421],[47.7095,30.10453],[48.01114,29.98906],[48.06782,30.02906],[48.17332,30.02448],[48.40479,29.85763],[48.59531,29.66815],[48.83867,29.78572],[48.61441,29.93675],[48.51011,29.96238],[48.44785,30.00148],[48.4494,30.04456],[48.43384,30.08233],[48.38869,30.11062],[48.38714,30.13485],[48.41671,30.17254],[48.41117,30.19846],[48.26393,30.3408],[48.24385,30.33846],[48.21279,30.31644],[48.19425,30.32796],[48.18321,30.39703],[48.14585,30.44133],[48.02443,30.4789],[48.03221,30.9967],[47.68219,31.00004],[47.6804,31.39086],[47.86337,31.78422],[47.64771,32.07666],[47.52474,32.15972],[47.57144,32.20583],[47.37529,32.47808],[47.17218,32.45393],[46.46788,32.91992],[46.32298,32.9731],[46.17198,32.95612],[46.09103,32.98354],[46.15175,33.07229],[46.03966,33.09577],[46.05367,33.13097],[46.11905,33.11924],[46.20623,33.20395],[45.99919,33.5082],[45.86687,33.49263],[45.96183,33.55751],[45.89801,33.63661],[45.77814,33.60938],[45.50261,33.94968],[45.42789,33.9458],[45.41077,33.97421],[45.47264,34.03099],[45.56176,34.15088],[45.58667,34.30147],[45.53552,34.35148],[45.49171,34.3439],[45.46697,34.38221],[45.43879,34.45949],[45.51883,34.47692],[45.53219,34.60441],[45.59074,34.55558],[45.60224,34.55057],[45.73923,34.54416],[45.70031,34.69277],[45.65672,34.7222],[45.68284,34.76624],[45.70031,34.82322],[45.73641,34.83975],[45.79682,34.85133],[45.78904,34.91135],[45.86532,34.89858],[45.89477,34.95805],[45.87864,35.03441],[45.92173,35.0465],[45.92203,35.09538],[45.93108,35.08148],[45.94756,35.09188],[46.06508,35.03699],[46.07747,35.0838],[46.11763,35.07551],[46.19116,35.11097],[46.15642,35.1268],[46.16229,35.16984],[46.19738,35.18536],[46.18457,35.22561],[46.11367,35.23729],[46.15474,35.2883],[46.13152,35.32548],[46.05358,35.38568],[45.98453,35.49848],[46.01518,35.52012],[45.97584,35.58132],[46.03028,35.57416],[46.01307,35.59756],[46.0165,35.61501],[45.99452,35.63574],[46.0117,35.65059],[46.01631,35.69139],[46.23736,35.71414],[46.34166,35.78363],[46.32921,35.82655],[46.17198,35.8013],[46.08325,35.8581],[45.94711,35.82218],[45.89784,35.83708],[45.81442,35.82107],[45.76145,35.79898],[45.6645,35.92872],[45.60018,35.96069],[45.55245,35.99943],[45.46594,36.00042],[45.38275,35.97156],[45.33916,35.99424],[45.37652,36.06222],[45.37312,36.09917],[45.32235,36.17383],[45.30038,36.27769],[45.26261,36.3001],[45.27394,36.35846],[45.23953,36.43257],[45.11811,36.40751],[45.00759,36.5402],[45.06985,36.62645],[45.06985,36.6814],[45.01537,36.75128],[44.84725,36.77622],[44.83479,36.81362],[44.90173,36.86096],[44.91199,36.91468],[44.89862,37.01897],[44.81611,37.04383],[44.75229,37.11958],[44.78319,37.1431],[44.76698,37.16162],[44.63179,37.19229],[44.42631,37.05825],[44.38117,37.05825],[44.35315,37.04955],[44.35937,37.02843],[44.30645,36.97373],[44.25975,36.98119],[44.18503,37.09551],[44.22239,37.15756],[44.27998,37.16501],[44.2613,37.25055],[44.13521,37.32486],[44.02002,37.33229],[43.90949,37.22453],[43.84878,37.22205],[43.82699,37.19477],[43.8052,37.22825],[43.7009,37.23692],[43.63085,37.21957],[43.56702,37.25675],[43.50787,37.24436],[43.33508,37.33105],[43.30083,37.30629],[43.11403,37.37436],[42.93705,37.32015],[42.78887,37.38615]]]]}},{type:"Feature",properties:{iso1A2:"IR",iso1A3:"IRN",iso1N3:"364",wikidata:"Q794",nameEn:"Iran",groups:["034","142"],callingCodes:["98"]},geometry:{type:"MultiPolygon",coordinates:[[[[44.96746,39.42998],[44.88916,39.59653],[44.81043,39.62677],[44.71806,39.71124],[44.65422,39.72163],[44.6137,39.78393],[44.47298,39.68788],[44.48111,39.61579],[44.41849,39.56659],[44.42832,39.4131],[44.37921,39.4131],[44.29818,39.378],[44.22452,39.4169],[44.03667,39.39223],[44.1043,39.19842],[44.20946,39.13975],[44.18863,38.93881],[44.30322,38.81581],[44.26155,38.71427],[44.28065,38.6465],[44.32058,38.62752],[44.3207,38.49799],[44.3119,38.37887],[44.38309,38.36117],[44.44386,38.38295],[44.50115,38.33939],[44.42476,38.25763],[44.22509,37.88859],[44.3883,37.85433],[44.45948,37.77065],[44.55498,37.783],[44.62096,37.71985],[44.56887,37.6429],[44.61401,37.60165],[44.58449,37.45018],[44.81021,37.2915],[44.75986,37.21549],[44.7868,37.16644],[44.78319,37.1431],[44.75229,37.11958],[44.81611,37.04383],[44.89862,37.01897],[44.91199,36.91468],[44.90173,36.86096],[44.83479,36.81362],[44.84725,36.77622],[45.01537,36.75128],[45.06985,36.6814],[45.06985,36.62645],[45.00759,36.5402],[45.11811,36.40751],[45.23953,36.43257],[45.27394,36.35846],[45.26261,36.3001],[45.30038,36.27769],[45.32235,36.17383],[45.37312,36.09917],[45.37652,36.06222],[45.33916,35.99424],[45.38275,35.97156],[45.46594,36.00042],[45.55245,35.99943],[45.60018,35.96069],[45.6645,35.92872],[45.76145,35.79898],[45.81442,35.82107],[45.89784,35.83708],[45.94711,35.82218],[46.08325,35.8581],[46.17198,35.8013],[46.32921,35.82655],[46.34166,35.78363],[46.23736,35.71414],[46.01631,35.69139],[46.0117,35.65059],[45.99452,35.63574],[46.0165,35.61501],[46.01307,35.59756],[46.03028,35.57416],[45.97584,35.58132],[46.01518,35.52012],[45.98453,35.49848],[46.05358,35.38568],[46.13152,35.32548],[46.15474,35.2883],[46.11367,35.23729],[46.18457,35.22561],[46.19738,35.18536],[46.16229,35.16984],[46.15642,35.1268],[46.19116,35.11097],[46.11763,35.07551],[46.07747,35.0838],[46.06508,35.03699],[45.94756,35.09188],[45.93108,35.08148],[45.92203,35.09538],[45.92173,35.0465],[45.87864,35.03441],[45.89477,34.95805],[45.86532,34.89858],[45.78904,34.91135],[45.79682,34.85133],[45.73641,34.83975],[45.70031,34.82322],[45.68284,34.76624],[45.65672,34.7222],[45.70031,34.69277],[45.73923,34.54416],[45.60224,34.55057],[45.59074,34.55558],[45.53219,34.60441],[45.51883,34.47692],[45.43879,34.45949],[45.46697,34.38221],[45.49171,34.3439],[45.53552,34.35148],[45.58667,34.30147],[45.56176,34.15088],[45.47264,34.03099],[45.41077,33.97421],[45.42789,33.9458],[45.50261,33.94968],[45.77814,33.60938],[45.89801,33.63661],[45.96183,33.55751],[45.86687,33.49263],[45.99919,33.5082],[46.20623,33.20395],[46.11905,33.11924],[46.05367,33.13097],[46.03966,33.09577],[46.15175,33.07229],[46.09103,32.98354],[46.17198,32.95612],[46.32298,32.9731],[46.46788,32.91992],[47.17218,32.45393],[47.37529,32.47808],[47.57144,32.20583],[47.52474,32.15972],[47.64771,32.07666],[47.86337,31.78422],[47.6804,31.39086],[47.68219,31.00004],[48.03221,30.9967],[48.02443,30.4789],[48.14585,30.44133],[48.18321,30.39703],[48.19425,30.32796],[48.21279,30.31644],[48.24385,30.33846],[48.26393,30.3408],[48.41117,30.19846],[48.41671,30.17254],[48.38714,30.13485],[48.38869,30.11062],[48.43384,30.08233],[48.4494,30.04456],[48.44785,30.00148],[48.51011,29.96238],[48.61441,29.93675],[48.83867,29.78572],[49.98877,27.87827],[50.37726,27.89227],[54.39838,25.68383],[55.14145,25.62624],[55.81777,26.18798],[56.2644,26.58649],[56.68954,26.76645],[56.79239,26.41236],[56.82555,25.7713],[56.86325,25.03856],[61.5251,24.57287],[61.57592,25.0492],[61.6433,25.27541],[61.683,25.66638],[61.83968,25.7538],[61.83831,26.07249],[61.89391,26.26251],[62.05117,26.31647],[62.21304,26.26601],[62.31484,26.528],[62.77352,26.64099],[63.1889,26.65072],[63.18688,26.83844],[63.25005,26.84212],[63.25005,27.08692],[63.32283,27.14437],[63.19649,27.25674],[62.80604,27.22412],[62.79684,27.34381],[62.84905,27.47627],[62.7638,28.02992],[62.79412,28.28108],[62.59499,28.24842],[62.40259,28.42703],[61.93581,28.55284],[61.65978,28.77937],[61.53765,29.00507],[61.31508,29.38903],[60.87231,29.86514],[61.80829,30.84224],[61.78268,30.92724],[61.8335,30.97669],[61.83257,31.0452],[61.80957,31.12576],[61.80569,31.16167],[61.70929,31.37391],[60.84541,31.49561],[60.86191,32.22565],[60.56485,33.12944],[60.88908,33.50219],[60.91133,33.55596],[60.69573,33.56054],[60.57762,33.59772],[60.5485,33.73422],[60.5838,33.80793],[60.50209,34.13992],[60.66502,34.31539],[60.91321,34.30411],[60.72316,34.52857],[60.99922,34.63064],[61.00197,34.70631],[61.06926,34.82139],[61.12831,35.09938],[61.0991,35.27845],[61.18187,35.30249],[61.27371,35.61482],[61.22719,35.67038],[61.26152,35.80749],[61.22444,35.92879],[61.12007,35.95992],[61.22719,36.12759],[61.1393,36.38782],[61.18187,36.55348],[61.14516,36.64644],[60.34767,36.63214],[60.00768,37.04102],[59.74678,37.12499],[59.55178,37.13594],[59.39385,37.34257],[59.39797,37.47892],[59.33507,37.53146],[59.22905,37.51161],[58.9338,37.67374],[58.6921,37.64548],[58.5479,37.70526],[58.47786,37.6433],[58.39959,37.63134],[58.22999,37.6856],[58.21399,37.77281],[57.79534,37.89299],[57.35042,37.98546],[57.37236,38.09321],[57.21169,38.28965],[57.03453,38.18717],[56.73928,38.27887],[56.62255,38.24005],[56.43303,38.26054],[56.32454,38.18502],[56.33278,38.08132],[55.97847,38.08024],[55.76561,38.12238],[55.44152,38.08564],[55.13412,37.94705],[54.851,37.75739],[54.77684,37.62264],[54.81804,37.61285],[54.77822,37.51597],[54.67247,37.43532],[54.58664,37.45809],[54.36211,37.34912],[54.24565,37.32047],[53.89734,37.3464],[48.88288,38.43975],[48.84969,38.45015],[48.81072,38.44853],[48.78979,38.45026],[48.70001,38.40564],[48.62217,38.40198],[48.58793,38.45076],[48.45084,38.61013],[48.3146,38.59958],[48.24773,38.71883],[48.02581,38.82705],[48.01409,38.90333],[48.07734,38.91616],[48.08627,38.94434],[48.28437,38.97186],[48.33884,39.03022],[48.31239,39.09278],[48.15361,39.19419],[48.12404,39.25208],[48.15984,39.30028],[48.37385,39.37584],[48.34264,39.42935],[47.98977,39.70999],[47.84774,39.66285],[47.50099,39.49615],[47.38978,39.45999],[47.31301,39.37492],[47.05927,39.24846],[47.05771,39.20143],[46.95341,39.13505],[46.92539,39.16644],[46.83822,39.13143],[46.75752,39.03231],[46.53497,38.86548],[46.34059,38.92076],[46.20601,38.85262],[46.14785,38.84206],[46.06766,38.87861],[46.00228,38.87376],[45.94624,38.89072],[45.90266,38.87739],[45.83883,38.90768],[45.65172,38.95199],[45.6155,38.94304],[45.6131,38.964],[45.44966,38.99243],[45.44811,39.04927],[45.40452,39.07224],[45.40148,39.09007],[45.30489,39.18333],[45.16168,39.21952],[45.08751,39.35052],[45.05932,39.36435],[44.96746,39.42998]]]]}},{type:"Feature",properties:{iso1A2:"IS",iso1A3:"ISL",iso1N3:"352",wikidata:"Q189",nameEn:"Iceland",groups:["154","150"],callingCodes:["354"]},geometry:{type:"MultiPolygon",coordinates:[[[[-33.15676,62.62995],[-8.25539,63.0423],[-15.70914,69.67442],[-33.15676,62.62995]]]]}},{type:"Feature",properties:{iso1A2:"IT",iso1A3:"ITA",iso1N3:"380",wikidata:"Q38",nameEn:"Italy",groups:["EU","039","150"],callingCodes:["39"]},geometry:{type:"MultiPolygon",coordinates:[[[[8.95861,45.96485],[8.97604,45.96151],[8.97741,45.98317],[8.96668,45.98436],[8.95861,45.96485]]],[[[7.63035,43.57419],[9.56115,43.20816],[10.09675,41.44089],[7.60802,41.05927],[7.89009,38.19924],[11.2718,37.6713],[12.13667,34.20326],[14.02721,36.53141],[17.67657,35.68918],[18.83516,40.36999],[16.15283,42.18525],[13.12821,44.48877],[13.05142,45.33128],[13.45644,45.59464],[13.6076,45.64761],[13.7198,45.59352],[13.74587,45.59811],[13.78445,45.5825],[13.84106,45.58185],[13.86771,45.59898],[13.8695,45.60835],[13.9191,45.6322],[13.87933,45.65207],[13.83422,45.68703],[13.83332,45.70855],[13.8235,45.7176],[13.66986,45.79955],[13.59784,45.8072],[13.58858,45.83503],[13.57563,45.8425],[13.58644,45.88173],[13.59565,45.89446],[13.60857,45.89907],[13.61931,45.91782],[13.63815,45.93607],[13.6329,45.94894],[13.64307,45.98326],[13.63458,45.98947],[13.62074,45.98388],[13.58903,45.99009],[13.56759,45.96991],[13.52963,45.96588],[13.50104,45.98078],[13.47474,46.00546],[13.49702,46.01832],[13.50998,46.04498],[13.49568,46.04839],[13.50104,46.05986],[13.57072,46.09022],[13.64053,46.13587],[13.66472,46.17392],[13.64451,46.18966],[13.56682,46.18703],[13.56114,46.2054],[13.47587,46.22725],[13.42218,46.20758],[13.37671,46.29668],[13.44808,46.33507],[13.43418,46.35992],[13.47019,46.3621],[13.5763,46.40915],[13.5763,46.42613],[13.59777,46.44137],[13.68684,46.43881],[13.7148,46.5222],[13.64088,46.53438],[13.27627,46.56059],[12.94445,46.60401],[12.59992,46.6595],[12.38708,46.71529],[12.27591,46.88651],[12.2006,46.88854],[12.11675,47.01241],[12.21781,47.03996],[12.19254,47.09331],[11.74789,46.98484],[11.50739,47.00644],[11.33355,46.99862],[11.10618,46.92966],[11.00764,46.76896],[10.72974,46.78972],[10.75753,46.82258],[10.66405,46.87614],[10.54783,46.84505],[10.47197,46.85698],[10.38659,46.67847],[10.40475,46.63671],[10.44686,46.64162],[10.49375,46.62049],[10.46136,46.53164],[10.25309,46.57432],[10.23674,46.63484],[10.10307,46.61003],[10.03715,46.44479],[10.165,46.41051],[10.10506,46.3372],[10.17862,46.25626],[10.14439,46.22992],[10.07055,46.21668],[9.95249,46.38045],[9.73086,46.35071],[9.71273,46.29266],[9.57015,46.2958],[9.46117,46.37481],[9.45936,46.50873],[9.40487,46.46621],[9.36128,46.5081],[9.28136,46.49685],[9.25502,46.43743],[9.29226,46.32717],[9.24503,46.23616],[9.01618,46.04928],[8.99257,45.9698],[9.09065,45.89906],[9.06642,45.8761],[9.04546,45.84968],[9.04059,45.8464],[9.03505,45.83976],[9.03793,45.83548],[9.03279,45.82865],[9.0298,45.82127],[9.00324,45.82055],[8.99663,45.83466],[8.9621,45.83707],[8.94737,45.84285],[8.91129,45.8388],[8.93504,45.86245],[8.94372,45.86587],[8.93649,45.86775],[8.88904,45.95465],[8.86688,45.96135],[8.85121,45.97239],[8.8319,45.9879],[8.79362,45.99207],[8.78585,45.98973],[8.79414,46.00913],[8.85617,46.0748],[8.80778,46.10085],[8.75697,46.10395],[8.62242,46.12112],[8.45032,46.26869],[8.46317,46.43712],[8.42464,46.46367],[8.30648,46.41587],[8.31162,46.38044],[8.08814,46.26692],[8.16866,46.17817],[8.11383,46.11577],[8.02906,46.10331],[7.98881,45.99867],[7.9049,45.99945],[7.85949,45.91485],[7.56343,45.97421],[7.10685,45.85653],[7.04151,45.92435],[6.95315,45.85163],[6.80785,45.83265],[6.80785,45.71864],[6.98948,45.63869],[7.00037,45.509],[7.18019,45.40071],[7.10572,45.32924],[7.13115,45.25386],[7.07074,45.21228],[6.96706,45.20841],[6.85144,45.13226],[6.7697,45.16044],[6.62803,45.11175],[6.66981,45.02324],[6.74791,45.01939],[6.74519,44.93661],[6.75518,44.89915],[6.90774,44.84322],[6.93499,44.8664],[7.02217,44.82519],[7.00401,44.78782],[7.07484,44.68073],[7.00582,44.69364],[6.95133,44.66264],[6.96042,44.62129],[6.85507,44.53072],[6.86233,44.49834],[6.94504,44.43112],[6.88784,44.42043],[6.89171,44.36637],[6.98221,44.28289],[7.00764,44.23736],[7.16929,44.20352],[7.27827,44.1462],[7.34547,44.14359],[7.36364,44.11882],[7.62155,44.14881],[7.63245,44.17877],[7.68694,44.17487],[7.66878,44.12795],[7.72508,44.07578],[7.6597,44.03009],[7.66848,43.99943],[7.65266,43.9763],[7.60771,43.95772],[7.56858,43.94506],[7.56075,43.89932],[7.51162,43.88301],[7.49355,43.86551],[7.50423,43.84345],[7.53006,43.78405],[7.63035,43.57419]],[[12.45181,41.90056],[12.44834,41.90095],[12.44582,41.90194],[12.44815,41.90326],[12.44984,41.90545],[12.45091,41.90625],[12.45543,41.90738],[12.45561,41.90629],[12.45762,41.9058],[12.45755,41.9033],[12.45826,41.90281],[12.45834,41.90174],[12.4577,41.90115],[12.45691,41.90125],[12.45626,41.90172],[12.45435,41.90143],[12.45446,41.90028],[12.45181,41.90056]],[[12.45648,43.89369],[12.44184,43.90498],[12.41641,43.89991],[12.40935,43.9024],[12.41233,43.90956],[12.40733,43.92379],[12.41551,43.92984],[12.41165,43.93769],[12.40506,43.94325],[12.40415,43.95485],[12.41414,43.95273],[12.42005,43.9578],[12.43662,43.95698],[12.44684,43.96597],[12.46205,43.97463],[12.47853,43.98052],[12.49406,43.98492],[12.50678,43.99113],[12.51463,43.99122],[12.5154,43.98508],[12.51064,43.98165],[12.51109,43.97201],[12.50622,43.97131],[12.50875,43.96198],[12.50655,43.95796],[12.51427,43.94897],[12.51553,43.94096],[12.50496,43.93017],[12.50269,43.92363],[12.49724,43.92248],[12.49247,43.91774],[12.49429,43.90973],[12.48771,43.89706],[12.45648,43.89369]]]]}},{type:"Feature",properties:{iso1A2:"JE",iso1A3:"JEY",iso1N3:"832",wikidata:"Q785",nameEn:"Jersey",country:"GB",groups:["830","154","150"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["44 01534"]},geometry:{type:"MultiPolygon",coordinates:[[[[-2.00491,48.86706],[-1.83944,49.23037],[-2.09454,49.46288],[-2.65349,49.15373],[-2.00491,48.86706]]]]}},{type:"Feature",properties:{iso1A2:"JM",iso1A3:"JAM",iso1N3:"388",wikidata:"Q766",nameEn:"Jamaica",aliases:["JA"],groups:["029","003","419","019"],driveSide:"left",callingCodes:["1 876","1 658"]},geometry:{type:"MultiPolygon",coordinates:[[[[-75.50728,17.08879],[-76.34192,18.86145],[-78.75694,18.78765],[-78.34606,16.57862],[-75.50728,17.08879]]]]}},{type:"Feature",properties:{iso1A2:"JO",iso1A3:"JOR",iso1N3:"400",wikidata:"Q810",nameEn:"Jordan",groups:["145","142"],callingCodes:["962"]},geometry:{type:"MultiPolygon",coordinates:[[[[39.04251,32.30203],[38.98762,32.47694],[39.08202,32.50304],[38.79171,33.37328],[36.83946,32.31293],[36.40959,32.37908],[36.23948,32.50108],[36.20875,32.49529],[36.20379,32.52751],[36.08074,32.51463],[36.02239,32.65911],[35.96633,32.66237],[35.93307,32.71966],[35.88405,32.71321],[35.75983,32.74803],[35.68467,32.70715],[35.66527,32.681],[35.61669,32.67999],[35.59813,32.65159],[35.56614,32.64393],[35.57485,32.48669],[35.55494,32.42687],[35.55807,32.38674],[35.57111,32.21877],[35.52012,32.04076],[35.54375,31.96587],[35.52758,31.9131],[35.55941,31.76535],[35.47672,31.49578],[35.40316,31.25535],[35.43658,31.12444],[35.41371,30.95565],[35.33984,30.8802],[35.33456,30.81224],[35.29311,30.71365],[35.21379,30.60401],[35.19595,30.50297],[35.16218,30.43535],[35.19183,30.34636],[35.14108,30.07374],[35.02147,29.66343],[34.98207,29.58147],[34.97718,29.54294],[34.92298,29.45305],[34.88293,29.37455],[34.95987,29.35727],[36.07081,29.18469],[36.50005,29.49696],[36.75083,29.86903],[37.4971,29.99949],[37.66395,30.33245],[37.99354,30.49998],[36.99791,31.50081],[38.99233,31.99721],[39.29903,32.23259],[39.26157,32.35555],[39.04251,32.30203]]]]}},{type:"Feature",properties:{iso1A2:"JP",iso1A3:"JPN",iso1N3:"392",wikidata:"Q17",nameEn:"Japan",groups:["030","142"],driveSide:"left",callingCodes:["81"]},geometry:{type:"MultiPolygon",coordinates:[[[[145.82361,43.38904],[145.23667,43.76813],[145.82343,44.571],[140.9182,45.92937],[133.61399,37.41],[129.2669,34.87122],[122.26612,25.98197],[123.92912,17.8782],[155.16731,23.60141],[145.82361,43.38904]]]]}},{type:"Feature",properties:{iso1A2:"KE",iso1A3:"KEN",iso1N3:"404",wikidata:"Q114",nameEn:"Kenya",groups:["014","202","002"],driveSide:"left",callingCodes:["254"]},geometry:{type:"MultiPolygon",coordinates:[[[[35.9419,4.61933],[35.51424,4.61643],[35.42366,4.76969],[35.47843,4.91872],[35.30992,4.90402],[35.34151,5.02364],[34.47601,4.72162],[33.9873,4.23316],[34.06046,4.15235],[34.15429,3.80464],[34.45815,3.67385],[34.44922,3.51627],[34.39112,3.48802],[34.41794,3.44342],[34.40006,3.37949],[34.45815,3.18319],[34.56242,3.11478],[34.60114,2.93034],[34.65774,2.8753],[34.73967,2.85447],[34.78137,2.76223],[34.77244,2.70272],[34.95267,2.47209],[34.90947,2.42447],[34.98692,1.97348],[34.9899,1.6668],[34.92734,1.56109],[34.87819,1.5596],[34.7918,1.36752],[34.82606,1.30944],[34.82606,1.26626],[34.80223,1.22754],[34.67562,1.21265],[34.58029,1.14712],[34.57427,1.09868],[34.52369,1.10692],[34.43349,0.85254],[34.40041,0.80266],[34.31516,0.75693],[34.27345,0.63182],[34.20196,0.62289],[34.13493,0.58118],[34.11408,0.48884],[34.08727,0.44713],[34.10067,0.36372],[33.90936,0.10581],[33.98449,-0.13079],[33.9264,-0.54188],[33.93107,-0.99298],[34.02286,-1.00779],[34.03084,-1.05101],[34.0824,-1.02264],[37.67199,-3.06222],[37.71745,-3.304],[37.5903,-3.42735],[37.63099,-3.50723],[37.75036,-3.54243],[37.81321,-3.69179],[39.21631,-4.67835],[39.44306,-4.93877],[39.62121,-4.68136],[41.75542,-1.85308],[41.56362,-1.66375],[41.56,-1.59812],[41.00099,-0.83068],[40.98767,2.82959],[41.31368,3.14314],[41.89488,3.97375],[41.1754,3.94079],[40.77498,4.27683],[39.86043,3.86974],[39.76808,3.67058],[39.58339,3.47434],[39.55132,3.39634],[39.51551,3.40895],[39.49444,3.45521],[39.19954,3.47834],[39.07736,3.5267],[38.91938,3.51198],[38.52336,3.62551],[38.45812,3.60445],[38.14168,3.62487],[37.07724,4.33503],[36.84474,4.44518],[36.03924,4.44406],[35.95449,4.53244],[35.9419,4.61933]]]]}},{type:"Feature",properties:{iso1A2:"KG",iso1A3:"KGZ",iso1N3:"417",wikidata:"Q813",nameEn:"Kyrgyzstan",groups:["143","142"],callingCodes:["996"]},geometry:{type:"MultiPolygon",coordinates:[[[[74.88756,42.98612],[74.75,42.99029],[74.70331,43.02519],[74.64615,43.05881],[74.57491,43.13702],[74.22489,43.24657],[73.55634,43.03071],[73.50992,42.82356],[73.44393,42.43098],[71.88792,42.83578],[71.62405,42.76613],[71.53272,42.8014],[71.2724,42.77853],[71.22785,42.69248],[71.17807,42.67381],[71.15232,42.60486],[70.97717,42.50147],[70.85973,42.30188],[70.94483,42.26238],[71.13263,42.28356],[71.28719,42.18033],[70.69777,41.92554],[70.17682,41.5455],[70.48909,41.40335],[70.67586,41.47953],[70.78572,41.36419],[70.77885,41.24813],[70.86263,41.23833],[70.9615,41.16393],[71.02193,41.19494],[71.11806,41.15359],[71.25813,41.18796],[71.27187,41.11015],[71.34877,41.16807],[71.40198,41.09436],[71.46148,41.13958],[71.43814,41.19644],[71.46688,41.31883],[71.57227,41.29175],[71.6787,41.42111],[71.65914,41.49599],[71.73054,41.54713],[71.71132,41.43012],[71.76625,41.4466],[71.83914,41.3546],[71.91457,41.2982],[71.85964,41.19081],[72.07249,41.11739],[72.10745,41.15483],[72.16433,41.16483],[72.17594,41.15522],[72.14864,41.13363],[72.1792,41.10621],[72.21061,41.05607],[72.17594,41.02377],[72.18339,40.99571],[72.324,41.03381],[72.34026,41.04539],[72.34757,41.06104],[72.36138,41.04384],[72.38511,41.02785],[72.45206,41.03018],[72.48742,40.97136],[72.55109,40.96046],[72.59136,40.86947],[72.68157,40.84942],[72.84291,40.85512],[72.94454,40.8094],[73.01869,40.84681],[73.13267,40.83512],[73.13412,40.79122],[73.0612,40.76678],[72.99133,40.76457],[72.93296,40.73089],[72.8722,40.71111],[72.85372,40.7116],[72.84754,40.67229],[72.80137,40.67856],[72.74866,40.60873],[72.74894,40.59592],[72.75982,40.57273],[72.74862,40.57131],[72.74768,40.58051],[72.73995,40.58409],[72.69579,40.59778],[72.66713,40.59076],[72.66713,40.5219],[72.47795,40.5532],[72.40517,40.61917],[72.34406,40.60144],[72.41714,40.55736],[72.38384,40.51535],[72.41513,40.50856],[72.44191,40.48222],[72.40346,40.4007],[72.24368,40.46091],[72.18648,40.49893],[71.96401,40.31907],[72.05464,40.27586],[71.85002,40.25647],[71.82646,40.21872],[71.73054,40.14818],[71.71719,40.17886],[71.69621,40.18492],[71.70569,40.20391],[71.68386,40.26984],[71.61931,40.26775],[71.61725,40.20615],[71.51549,40.22986],[71.51215,40.26943],[71.4246,40.28619],[71.36663,40.31593],[71.13042,40.34106],[71.05901,40.28765],[70.95789,40.28761],[70.9818,40.22392],[70.80495,40.16813],[70.7928,40.12797],[70.65827,40.0981],[70.65946,39.9878],[70.58912,39.95211],[70.55033,39.96619],[70.47557,39.93216],[70.57384,39.99394],[70.58297,40.00891],[70.01283,40.23288],[69.67001,40.10639],[69.64704,40.12165],[69.57615,40.10524],[69.55555,40.12296],[69.53794,40.11833],[69.53855,40.0887],[69.5057,40.03277],[69.53615,39.93991],[69.43557,39.92877],[69.43134,39.98431],[69.35649,40.01994],[69.26938,39.8127],[69.3594,39.52516],[69.68677,39.59281],[69.87491,39.53882],[70.11111,39.58223],[70.2869,39.53141],[70.44757,39.60128],[70.64087,39.58792],[70.7854,39.38933],[71.06418,39.41586],[71.08752,39.50704],[71.49814,39.61397],[71.55856,39.57588],[71.5517,39.45722],[71.62688,39.44056],[71.76816,39.45456],[71.80164,39.40631],[71.7522,39.32031],[71.79202,39.27355],[71.90601,39.27674],[72.04059,39.36704],[72.09689,39.26823],[72.17242,39.2661],[72.23834,39.17248],[72.33173,39.33093],[72.62027,39.39696],[72.85934,39.35116],[73.18454,39.35536],[73.31912,39.38615],[73.45096,39.46677],[73.59831,39.46425],[73.87018,39.47879],[73.94683,39.60733],[73.92354,39.69565],[73.9051,39.75073],[73.83006,39.76136],[73.97049,40.04378],[74.25533,40.13191],[74.35063,40.09742],[74.69875,40.34668],[74.85996,40.32857],[74.78168,40.44886],[74.82013,40.52197],[75.08243,40.43945],[75.22834,40.45382],[75.5854,40.66874],[75.69663,40.28642],[75.91361,40.2948],[75.96168,40.38064],[76.33659,40.3482],[76.5261,40.46114],[76.75681,40.95354],[76.99302,41.0696],[77.28004,41.0033],[77.3693,41.0375],[77.52723,41.00227],[77.76206,41.01574],[77.81287,41.14307],[78.12873,41.23091],[78.15757,41.38565],[78.3732,41.39603],[79.92977,42.04113],[80.17842,42.03211],[80.17807,42.21166],[79.97364,42.42816],[79.52921,42.44778],[79.19763,42.804],[78.91502,42.76839],[78.48469,42.89649],[75.82823,42.94848],[75.72174,42.79672],[75.29966,42.86183],[75.22619,42.85528],[74.88756,42.98612]],[[70.74189,39.86319],[70.63105,39.77923],[70.59667,39.83542],[70.54998,39.85137],[70.52631,39.86989],[70.53651,39.89155],[70.74189,39.86319]],[[71.86463,39.98598],[71.84316,39.95582],[71.7504,39.93701],[71.71511,39.96348],[71.78838,40.01404],[71.86463,39.98598]],[[71.21139,40.03369],[71.1427,39.95026],[71.23067,39.93581],[71.16101,39.88423],[71.10531,39.91354],[71.04979,39.89808],[71.10501,39.95568],[71.09063,39.99],[71.11668,39.99291],[71.11037,40.01984],[71.01035,40.05481],[71.00236,40.18154],[71.06305,40.1771],[71.12218,40.03052],[71.21139,40.03369]]]]}},{type:"Feature",properties:{iso1A2:"KH",iso1A3:"KHM",iso1N3:"116",wikidata:"Q424",nameEn:"Cambodia",groups:["035","142"],callingCodes:["855"]},geometry:{type:"MultiPolygon",coordinates:[[[[105.87328,11.55953],[105.81645,11.56876],[105.80867,11.60536],[105.8507,11.66635],[105.88962,11.67854],[105.95188,11.63738],[106.00792,11.7197],[106.02038,11.77457],[106.06708,11.77761],[106.13158,11.73283],[106.18539,11.75171],[106.26478,11.72122],[106.30525,11.67549],[106.37219,11.69836],[106.44691,11.66787],[106.45158,11.68616],[106.41577,11.76999],[106.44535,11.8279],[106.44068,11.86294],[106.4687,11.86751],[106.4111,11.97413],[106.70687,11.96956],[106.79405,12.0807],[106.92325,12.06548],[106.99953,12.08983],[107.15831,12.27547],[107.34511,12.33327],[107.42917,12.24657],[107.4463,12.29373],[107.55059,12.36824],[107.5755,12.52177],[107.55993,12.7982],[107.49611,12.88926],[107.49144,13.01215],[107.62843,13.3668],[107.61909,13.52577],[107.53503,13.73908],[107.45252,13.78897],[107.46498,13.91593],[107.44318,13.99751],[107.38247,13.99147],[107.35757,14.02319],[107.37158,14.07906],[107.33577,14.11832],[107.40427,14.24509],[107.39493,14.32655],[107.44941,14.41552],[107.48521,14.40346],[107.52569,14.54665],[107.52102,14.59034],[107.55371,14.628],[107.54361,14.69092],[107.47238,14.61523],[107.44435,14.52785],[107.37897,14.54443],[107.3276,14.58812],[107.29803,14.58963],[107.26534,14.54292],[107.256,14.48716],[107.21241,14.48716],[107.17038,14.41782],[107.09722,14.3937],[107.03962,14.45099],[107.04585,14.41782],[106.98825,14.36806],[106.9649,14.3198],[106.90574,14.33639],[106.8497,14.29416],[106.80767,14.31226],[106.73762,14.42687],[106.63333,14.44194],[106.59908,14.50977],[106.57106,14.50525],[106.54148,14.59565],[106.50723,14.58963],[106.45898,14.55045],[106.47766,14.50977],[106.43874,14.52032],[106.40916,14.45249],[106.32355,14.44043],[106.25194,14.48415],[106.21302,14.36203],[106.00131,14.36957],[105.99509,14.32734],[106.02311,14.30623],[106.04801,14.20363],[106.10872,14.18401],[106.11962,14.11307],[106.18656,14.06324],[106.16632,14.01794],[106.10094,13.98471],[106.10405,13.9137],[105.90791,13.92881],[105.78182,14.02247],[105.78338,14.08438],[105.5561,14.15684],[105.44869,14.10703],[105.36775,14.09948],[105.2759,14.17496],[105.20894,14.34967],[105.17748,14.34432],[105.14012,14.23873],[105.08408,14.20402],[105.02804,14.23722],[104.97667,14.38806],[104.69335,14.42726],[104.55014,14.36091],[104.27616,14.39861],[103.93836,14.3398],[103.70175,14.38052],[103.71109,14.4348],[103.53518,14.42575],[103.39353,14.35639],[103.16469,14.33075],[102.93275,14.19044],[102.91251,14.01531],[102.77864,13.93374],[102.72727,13.77806],[102.56848,13.69366],[102.5481,13.6589],[102.58635,13.6286],[102.62483,13.60883],[102.57573,13.60461],[102.5358,13.56933],[102.44601,13.5637],[102.36859,13.57488],[102.33828,13.55613],[102.361,13.50551],[102.35563,13.47307],[102.35692,13.38274],[102.34611,13.35618],[102.36001,13.31142],[102.36146,13.26006],[102.43422,13.09061],[102.46011,13.08057],[102.52275,12.99813],[102.48694,12.97537],[102.49335,12.92711],[102.53053,12.77506],[102.4994,12.71736],[102.51963,12.66117],[102.57567,12.65358],[102.7796,12.43781],[102.78116,12.40284],[102.73134,12.37091],[102.70176,12.1686],[102.77026,12.06815],[102.78427,11.98746],[102.83957,11.8519],[102.90973,11.75613],[102.91449,11.65512],[102.52395,11.25257],[102.47649,9.66162],[103.99198,10.48391],[104.43778,10.42386],[104.47963,10.43046],[104.49869,10.4057],[104.59018,10.53073],[104.87933,10.52833],[104.95094,10.64003],[105.09571,10.72722],[105.02722,10.89236],[105.08326,10.95656],[105.11449,10.96332],[105.34011,10.86179],[105.42884,10.96878],[105.50045,10.94586],[105.77751,11.03671],[105.86376,10.89839],[105.84603,10.85873],[105.93403,10.83853],[105.94535,10.9168],[106.06708,10.8098],[106.18539,10.79451],[106.14301,10.98176],[106.20095,10.97795],[106.1757,11.07301],[106.1527,11.10476],[106.10444,11.07879],[105.86782,11.28343],[105.88962,11.43605],[105.87328,11.55953]]]]}},{type:"Feature",properties:{iso1A2:"KI",iso1A3:"KIR",iso1N3:"296",wikidata:"Q710",nameEn:"Kiribati",groups:["057","009"],driveSide:"left",callingCodes:["686"]},geometry:{type:"MultiPolygon",coordinates:[[[[169,3.9],[169,-3.5],[178,-3.5],[178,3.9],[169,3.9]]],[[[-158.62058,-1.35506],[-161.04969,-1.36251],[-175.33482,-1.40631],[-175.31804,-7.54825],[-174.18707,-7.54408],[-167.75329,-7.52784],[-156.50903,-7.4975],[-156.4957,-12.32002],[-149.61166,-12.30171],[-149.6249,-7.51261],[-149.65979,5.27712],[-161.06795,5.2462],[-161.05669,1.11722],[-158.62734,1.1296],[-158.62058,-1.35506]]]]}},{type:"Feature",properties:{iso1A2:"KM",iso1A3:"COM",iso1N3:"174",wikidata:"Q970",nameEn:"Comoros",groups:["014","202","002"],callingCodes:["269"]},geometry:{type:"MultiPolygon",coordinates:[[[[42.93552,-11.11413],[42.99868,-12.65261],[44.75722,-12.58368],[44.69407,-11.04481],[42.93552,-11.11413]]]]}},{type:"Feature",properties:{iso1A2:"KN",iso1A3:"KNA",iso1N3:"659",wikidata:"Q763",nameEn:"St. Kitts and Nevis",groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 869"]},geometry:{type:"MultiPolygon",coordinates:[[[[-62.27053,17.22145],[-62.76692,17.64353],[-63.11114,17.23125],[-62.62949,16.82364],[-62.27053,17.22145]]]]}},{type:"Feature",properties:{iso1A2:"KP",iso1A3:"PRK",iso1N3:"408",wikidata:"Q423",nameEn:"North Korea",groups:["030","142"],callingCodes:["850"]},geometry:{type:"MultiPolygon",coordinates:[[[[130.26095,42.9027],[130.09764,42.91425],[130.12957,42.98361],[129.96409,42.97306],[129.95082,43.01051],[129.8865,43.00395],[129.85261,42.96494],[129.83277,42.86746],[129.80719,42.79218],[129.7835,42.76521],[129.77183,42.69435],[129.75294,42.59409],[129.72541,42.43739],[129.60482,42.44461],[129.54701,42.37254],[129.42882,42.44702],[129.28541,42.41574],[129.22423,42.3553],[129.22285,42.26491],[129.15178,42.17224],[128.96068,42.06657],[128.94007,42.03537],[128.04487,42.01769],[128.15119,41.74568],[128.30716,41.60322],[128.20061,41.40895],[128.18546,41.41279],[128.12967,41.37931],[128.03311,41.39232],[128.02633,41.42103],[127.92943,41.44291],[127.29712,41.49473],[127.17841,41.59714],[126.90729,41.79955],[126.60631,41.65565],[126.53189,41.35206],[126.242,41.15454],[126.00335,40.92835],[125.76869,40.87908],[125.71172,40.85223],[124.86913,40.45387],[124.40719,40.13655],[124.38556,40.11047],[124.3322,40.05573],[124.37089,40.03004],[124.35029,39.95639],[124.23201,39.9248],[124.17532,39.8232],[123.90497,38.79949],[123.85601,37.49093],[124.67666,38.05679],[124.84224,37.977],[124.87921,37.80827],[125.06408,37.66334],[125.37112,37.62643],[125.81159,37.72949],[126.13074,37.70512],[126.18776,37.74728],[126.19097,37.81462],[126.24402,37.83113],[126.43239,37.84095],[126.46818,37.80873],[126.56709,37.76857],[126.59918,37.76364],[126.66067,37.7897],[126.68793,37.83728],[126.68793,37.9175],[126.67023,37.95852],[126.84961,38.0344],[126.88106,38.10246],[126.95887,38.1347],[126.95338,38.17735],[127.04479,38.25518],[127.15749,38.30722],[127.38727,38.33227],[127.49672,38.30647],[127.55013,38.32257],[128.02917,38.31861],[128.27652,38.41657],[128.31105,38.58462],[128.37487,38.62345],[128.65655,38.61914],[131.95041,41.5445],[130.65022,42.32281],[130.66367,42.38024],[130.64181,42.41422],[130.60805,42.4317],[130.56835,42.43281],[130.55143,42.52158],[130.50123,42.61636],[130.44361,42.54849],[130.41826,42.6011],[130.2385,42.71127],[130.23068,42.80125],[130.26095,42.9027]]]]}},{type:"Feature",properties:{iso1A2:"KR",iso1A3:"KOR",iso1N3:"410",wikidata:"Q884",nameEn:"South Korea",groups:["030","142"],callingCodes:["82"]},geometry:{type:"MultiPolygon",coordinates:[[[[133.61399,37.41],[128.65655,38.61914],[128.37487,38.62345],[128.31105,38.58462],[128.27652,38.41657],[128.02917,38.31861],[127.55013,38.32257],[127.49672,38.30647],[127.38727,38.33227],[127.15749,38.30722],[127.04479,38.25518],[126.95338,38.17735],[126.95887,38.1347],[126.88106,38.10246],[126.84961,38.0344],[126.67023,37.95852],[126.68793,37.9175],[126.68793,37.83728],[126.66067,37.7897],[126.59918,37.76364],[126.56709,37.76857],[126.46818,37.80873],[126.43239,37.84095],[126.24402,37.83113],[126.19097,37.81462],[126.18776,37.74728],[126.13074,37.70512],[125.81159,37.72949],[125.37112,37.62643],[125.06408,37.66334],[124.87921,37.80827],[124.84224,37.977],[124.67666,38.05679],[123.85601,37.49093],[122.80525,33.30571],[125.99728,32.63328],[129.2669,34.87122],[133.61399,37.41]]]]}},{type:"Feature",properties:{iso1A2:"KW",iso1A3:"KWT",iso1N3:"414",wikidata:"Q817",nameEn:"Kuwait",groups:["145","142"],callingCodes:["965"]},geometry:{type:"MultiPolygon",coordinates:[[[[49.00421,28.81495],[48.59531,29.66815],[48.40479,29.85763],[48.17332,30.02448],[48.06782,30.02906],[48.01114,29.98906],[47.7095,30.10453],[47.37192,30.10421],[47.15166,30.01044],[46.89695,29.50584],[46.5527,29.10283],[47.46202,29.0014],[47.58376,28.83382],[47.59863,28.66798],[47.70561,28.5221],[48.42991,28.53628],[49.00421,28.81495]]]]}},{type:"Feature",properties:{iso1A2:"KY",iso1A3:"CYM",iso1N3:"136",wikidata:"Q5785",nameEn:"Cayman Islands",country:"GB",groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 345"]},geometry:{type:"MultiPolygon",coordinates:[[[[-82.11509,19.60401],[-80.36068,18.11751],[-79.32727,20.06742],[-82.11509,19.60401]]]]}},{type:"Feature",properties:{iso1A2:"KZ",iso1A3:"KAZ",iso1N3:"398",wikidata:"Q232",nameEn:"Kazakhstan",groups:["143","142"],callingCodes:["7"]},geometry:{type:"MultiPolygon",coordinates:[[[[68.90865,55.38148],[68.19206,55.18823],[68.26661,55.09226],[68.21308,54.98645],[65.20174,54.55216],[65.24663,54.35721],[65.11033,54.33028],[64.97216,54.4212],[63.97686,54.29763],[64.02715,54.22679],[63.91224,54.20013],[63.80604,54.27079],[62.58651,54.05871],[62.56876,53.94047],[62.45931,53.90737],[62.38535,54.03961],[62.00966,54.04134],[62.03913,53.94768],[61.65318,54.02445],[61.56941,53.95703],[61.47603,54.08048],[61.3706,54.08464],[61.26863,53.92797],[60.99796,53.93699],[61.14283,53.90063],[61.22574,53.80268],[60.90626,53.62937],[61.55706,53.57144],[61.57185,53.50112],[61.37957,53.45887],[61.29082,53.50992],[61.14291,53.41481],[61.19024,53.30536],[62.14574,53.09626],[62.12799,52.99133],[62.0422,52.96105],[61.23462,53.03227],[61.05842,52.92217],[60.71989,52.75923],[60.71693,52.66245],[60.84118,52.63912],[60.84709,52.52228],[60.98021,52.50068],[61.05417,52.35096],[60.78201,52.22067],[60.72581,52.15538],[60.48915,52.15175],[60.19925,51.99173],[59.99809,51.98263],[60.09867,51.87135],[60.50986,51.7964],[60.36787,51.66815],[60.5424,51.61675],[60.92401,51.61124],[60.95655,51.48615],[61.50677,51.40687],[61.55114,51.32746],[61.6813,51.25716],[61.56889,51.23679],[61.4431,50.80679],[60.81833,50.6629],[60.31914,50.67705],[60.17262,50.83312],[60.01288,50.8163],[59.81172,50.54451],[59.51886,50.49937],[59.48928,50.64216],[58.87974,50.70852],[58.3208,51.15151],[57.75578,51.13852],[57.74986,50.93017],[57.44221,50.88354],[57.17302,51.11253],[56.17906,50.93204],[56.11398,50.7471],[55.67774,50.54508],[54.72067,51.03261],[54.56685,51.01958],[54.71476,50.61214],[54.55797,50.52006],[54.41894,50.61214],[54.46331,50.85554],[54.12248,51.11542],[53.69299,51.23466],[53.46165,51.49445],[52.54329,51.48444],[52.36119,51.74161],[51.8246,51.67916],[51.77431,51.49536],[51.301,51.48799],[51.26254,51.68466],[50.59695,51.61859],[50.26859,51.28677],[49.97277,51.2405],[49.76866,51.11067],[49.39001,51.09396],[49.41959,50.85927],[49.12673,50.78639],[48.86936,50.61589],[48.57946,50.63278],[48.90782,50.02281],[48.68352,49.89546],[48.42564,49.82283],[48.24519,49.86099],[48.10044,50.09242],[47.58551,50.47867],[47.30448,50.30894],[47.34589,50.09308],[47.18319,49.93721],[46.9078,49.86707],[46.78398,49.34026],[46.98795,49.23531],[47.04416,49.17152],[47.01458,49.07085],[46.91104,48.99715],[46.78392,48.95352],[46.49011,48.43019],[47.11516,48.27188],[47.12107,47.83687],[47.38731,47.68176],[47.41689,47.83687],[47.64973,47.76559],[48.15348,47.74545],[48.45173,47.40818],[48.52326,47.4102],[49.01136,46.72716],[48.51142,46.69268],[48.54988,46.56267],[49.16518,46.38542],[49.32259,46.26944],[49.88945,46.04554],[49.2134,44.84989],[52.26048,41.69249],[52.47884,41.78034],[52.97575,42.1308],[54.20635,42.38477],[54.95182,41.92424],[55.45471,41.25609],[56.00314,41.32584],[55.97584,44.99322],[55.97584,44.99328],[55.97584,44.99338],[55.97584,44.99343],[55.97584,44.99348],[55.97584,44.99353],[55.97584,44.99359],[55.97584,44.99369],[55.97584,44.99374],[55.97584,44.99384],[55.97584,44.9939],[55.97584,44.994],[55.97584,44.99405],[55.97584,44.99415],[55.97584,44.99421],[55.97584,44.99426],[55.97584,44.99431],[55.97584,44.99436],[55.97584,44.99441],[55.97594,44.99446],[55.97605,44.99452],[55.97605,44.99457],[55.97605,44.99462],[55.97605,44.99467],[55.97605,44.99477],[55.97615,44.99477],[55.97615,44.99483],[55.97615,44.99493],[55.97615,44.99498],[55.97615,44.99503],[55.97615,44.99508],[55.97625,44.99514],[55.97636,44.99519],[55.97636,44.99524],[55.97646,44.99529],[55.97646,44.99534],[55.97656,44.99539],[55.97667,44.99545],[55.97677,44.9955],[55.97677,44.99555],[55.97677,44.9956],[55.97687,44.9956],[55.97698,44.99565],[55.97698,44.9957],[55.97708,44.99576],[55.97718,44.99581],[55.97729,44.99586],[55.97739,44.99586],[55.97739,44.99591],[55.97749,44.99591],[55.9776,44.99591],[55.9777,44.99596],[55.9777,44.99601],[55.9778,44.99607],[55.97791,44.99607],[55.97801,44.99607],[55.97801,44.99612],[55.97811,44.99617],[55.97822,44.99617],[55.97832,44.99622],[55.97842,44.99622],[58.59711,45.58671],[61.01475,44.41383],[62.01711,43.51008],[63.34656,43.64003],[64.53885,43.56941],[64.96464,43.74748],[65.18666,43.48835],[65.53277,43.31856],[65.85194,42.85481],[66.09482,42.93426],[66.00546,41.94455],[66.53302,41.87388],[66.69129,41.1311],[67.9644,41.14611],[67.98511,41.02794],[68.08273,41.08148],[68.1271,41.0324],[67.96736,40.83798],[68.49983,40.56437],[68.63,40.59358],[68.58444,40.91447],[68.49983,40.99669],[68.62221,41.03019],[68.65662,40.93861],[68.73945,40.96989],[68.7217,41.05025],[69.01308,41.22804],[69.05006,41.36183],[69.15137,41.43078],[69.17701,41.43769],[69.18528,41.45175],[69.20439,41.45391],[69.22671,41.46298],[69.23332,41.45847],[69.25059,41.46693],[69.29778,41.43673],[69.35554,41.47211],[69.37468,41.46555],[69.45081,41.46246],[69.39485,41.51518],[69.45751,41.56863],[69.49545,41.545],[70.94483,42.26238],[70.85973,42.30188],[70.97717,42.50147],[71.15232,42.60486],[71.17807,42.67381],[71.22785,42.69248],[71.2724,42.77853],[71.53272,42.8014],[71.62405,42.76613],[71.88792,42.83578],[73.44393,42.43098],[73.50992,42.82356],[73.55634,43.03071],[74.22489,43.24657],[74.57491,43.13702],[74.64615,43.05881],[74.70331,43.02519],[74.75,42.99029],[74.88756,42.98612],[75.22619,42.85528],[75.29966,42.86183],[75.72174,42.79672],[75.82823,42.94848],[78.48469,42.89649],[78.91502,42.76839],[79.19763,42.804],[79.52921,42.44778],[79.97364,42.42816],[80.17807,42.21166],[80.26841,42.23797],[80.16892,42.61137],[80.26886,42.8366],[80.38169,42.83142],[80.58999,42.9011],[80.3735,43.01557],[80.62913,43.141],[80.78817,43.14235],[80.77771,43.30065],[80.69718,43.32589],[80.75156,43.44948],[80.40031,44.10986],[80.40229,44.23319],[80.38384,44.63073],[79.8987,44.89957],[80.11169,45.03352],[81.73278,45.3504],[82.51374,45.1755],[82.58474,45.40027],[82.21792,45.56619],[83.04622,47.19053],[83.92184,46.98912],[84.73077,47.01394],[84.93995,46.87399],[85.22443,47.04816],[85.54294,47.06171],[85.69696,47.2898],[85.61067,47.49753],[85.5169,48.05493],[85.73581,48.3939],[86.38069,48.46064],[86.75343,48.70331],[86.73568,48.99918],[86.87238,49.12432],[87.28386,49.11626],[87.31465,49.23603],[87.03071,49.25142],[86.82606,49.51796],[86.61307,49.60239],[86.79056,49.74787],[86.63674,49.80136],[86.18709,49.50259],[85.24047,49.60239],[84.99198,50.06793],[84.29385,50.27257],[83.8442,50.87375],[83.14607,51.00796],[82.55443,50.75412],[81.94999,50.79307],[81.46581,50.77658],[81.41248,50.97524],[81.06091,50.94833],[81.16999,51.15662],[80.80318,51.28262],[80.44819,51.20855],[80.4127,50.95581],[80.08138,50.77658],[79.11255,52.01171],[77.90383,53.29807],[76.54243,53.99329],[76.44076,54.16017],[76.82266,54.1798],[76.91052,54.4677],[75.3668,54.07439],[75.43398,53.98652],[75.07405,53.80831],[73.39218,53.44623],[73.25412,53.61532],[73.68921,53.86522],[73.74778,54.07194],[73.37963,53.96132],[72.71026,54.1161],[72.43415,53.92685],[72.17477,54.36303],[71.96141,54.17736],[71.10379,54.13326],[71.08706,54.33376],[71.24185,54.64965],[71.08288,54.71253],[70.96009,55.10558],[70.76493,55.3027],[70.19179,55.1476],[69.74917,55.35545],[69.34224,55.36344],[68.90865,55.38148]]]]}},{type:"Feature",properties:{iso1A2:"LA",iso1A3:"LAO",iso1N3:"418",wikidata:"Q819",nameEn:"Laos",groups:["035","142"],callingCodes:["856"]},geometry:{type:"MultiPolygon",coordinates:[[[[102.1245,22.43372],[102.03633,22.46164],[101.98487,22.42766],[101.91344,22.44417],[101.90714,22.38688],[101.86828,22.38397],[101.7685,22.50337],[101.68973,22.46843],[101.61306,22.27515],[101.56789,22.28876],[101.53638,22.24794],[101.60675,22.13513],[101.57525,22.13026],[101.62566,21.96574],[101.7791,21.83019],[101.74555,21.72852],[101.83257,21.61562],[101.80001,21.57461],[101.7475,21.5873],[101.7727,21.51794],[101.74224,21.48276],[101.74014,21.30967],[101.84412,21.25291],[101.83887,21.20983],[101.76745,21.21571],[101.79266,21.19025],[101.7622,21.14813],[101.70548,21.14911],[101.66977,21.20004],[101.60886,21.17947],[101.59491,21.18621],[101.6068,21.23329],[101.54563,21.25668],[101.29326,21.17254],[101.2229,21.23271],[101.26912,21.36482],[101.19349,21.41959],[101.2124,21.56422],[101.15156,21.56129],[101.16198,21.52808],[101.00234,21.39612],[100.80173,21.2934],[100.72716,21.31786],[100.63578,21.05639],[100.55281,21.02796],[100.50974,20.88574],[100.64628,20.88279],[100.60112,20.8347],[100.51079,20.82194],[100.36375,20.82783],[100.1957,20.68247],[100.08404,20.36626],[100.09999,20.31614],[100.09337,20.26293],[100.11785,20.24787],[100.1712,20.24324],[100.16668,20.2986],[100.22076,20.31598],[100.25769,20.3992],[100.33383,20.4028],[100.37439,20.35156],[100.41473,20.25625],[100.44992,20.23644],[100.4537,20.19971],[100.47567,20.19133],[100.51052,20.14928],[100.55218,20.17741],[100.58808,20.15791],[100.5094,19.87904],[100.398,19.75047],[100.49604,19.53504],[100.58219,19.49164],[100.64606,19.55884],[100.77231,19.48324],[100.90302,19.61901],[101.08928,19.59748],[101.26545,19.59242],[101.26991,19.48324],[101.21347,19.46223],[101.20604,19.35296],[101.24911,19.33334],[101.261,19.12717],[101.35606,19.04716],[101.25803,18.89545],[101.22832,18.73377],[101.27585,18.68875],[101.06047,18.43247],[101.18227,18.34367],[101.15108,18.25624],[101.19118,18.2125],[101.1793,18.0544],[101.02185,17.87637],[100.96541,17.57926],[101.15108,17.47586],[101.44667,17.7392],[101.72294,17.92867],[101.78087,18.07559],[101.88485,18.02474],[102.11359,18.21532],[102.45523,17.97106],[102.59234,17.96127],[102.60971,17.95411],[102.61432,17.92273],[102.5896,17.84889],[102.59485,17.83537],[102.68194,17.80151],[102.69946,17.81686],[102.67543,17.84529],[102.68538,17.86653],[102.75954,17.89561],[102.79044,17.93612],[102.81988,17.94233],[102.86323,17.97531],[102.95812,18.0054],[102.9912,17.9949],[103.01998,17.97095],[103.0566,18.00144],[103.07823,18.03833],[103.07343,18.12351],[103.1493,18.17799],[103.14994,18.23172],[103.17093,18.2618],[103.29757,18.30475],[103.23818,18.34875],[103.24779,18.37807],[103.30977,18.4341],[103.41044,18.4486],[103.47773,18.42841],[103.60957,18.40528],[103.699,18.34125],[103.82449,18.33979],[103.85642,18.28666],[103.93916,18.33914],[103.97725,18.33631],[104.06533,18.21656],[104.10927,18.10826],[104.21776,17.99335],[104.2757,17.86139],[104.35432,17.82871],[104.45404,17.66788],[104.69867,17.53038],[104.80061,17.39367],[104.80716,17.19025],[104.73712,17.01404],[104.7373,16.91125],[104.76442,16.84752],[104.7397,16.81005],[104.76099,16.69302],[104.73349,16.565],[104.88057,16.37311],[105.00262,16.25627],[105.06204,16.09792],[105.42001,16.00657],[105.38508,15.987],[105.34115,15.92737],[105.37959,15.84074],[105.42285,15.76971],[105.46573,15.74742],[105.61756,15.68792],[105.60446,15.53301],[105.58191,15.41031],[105.47635,15.3796],[105.4692,15.33709],[105.50662,15.32054],[105.58043,15.32724],[105.46661,15.13132],[105.61162,15.00037],[105.5121,14.80802],[105.53864,14.55731],[105.43783,14.43865],[105.20894,14.34967],[105.2759,14.17496],[105.36775,14.09948],[105.44869,14.10703],[105.5561,14.15684],[105.78338,14.08438],[105.78182,14.02247],[105.90791,13.92881],[106.10405,13.9137],[106.10094,13.98471],[106.16632,14.01794],[106.18656,14.06324],[106.11962,14.11307],[106.10872,14.18401],[106.04801,14.20363],[106.02311,14.30623],[105.99509,14.32734],[106.00131,14.36957],[106.21302,14.36203],[106.25194,14.48415],[106.32355,14.44043],[106.40916,14.45249],[106.43874,14.52032],[106.47766,14.50977],[106.45898,14.55045],[106.50723,14.58963],[106.54148,14.59565],[106.57106,14.50525],[106.59908,14.50977],[106.63333,14.44194],[106.73762,14.42687],[106.80767,14.31226],[106.8497,14.29416],[106.90574,14.33639],[106.9649,14.3198],[106.98825,14.36806],[107.04585,14.41782],[107.03962,14.45099],[107.09722,14.3937],[107.17038,14.41782],[107.21241,14.48716],[107.256,14.48716],[107.26534,14.54292],[107.29803,14.58963],[107.3276,14.58812],[107.37897,14.54443],[107.44435,14.52785],[107.47238,14.61523],[107.54361,14.69092],[107.51579,14.79282],[107.59285,14.87795],[107.48277,14.93751],[107.46516,15.00982],[107.61486,15.0566],[107.61926,15.13949],[107.58844,15.20111],[107.62587,15.2266],[107.60605,15.37524],[107.62367,15.42193],[107.53341,15.40496],[107.50699,15.48771],[107.3815,15.49832],[107.34408,15.62345],[107.27583,15.62769],[107.27143,15.71459],[107.21859,15.74638],[107.21419,15.83747],[107.34188,15.89464],[107.39471,15.88829],[107.46296,16.01106],[107.44975,16.08511],[107.33968,16.05549],[107.25822,16.13587],[107.14595,16.17816],[107.15035,16.26271],[107.09091,16.3092],[107.02597,16.31132],[106.97385,16.30204],[106.96638,16.34938],[106.88067,16.43594],[106.88727,16.52671],[106.84104,16.55415],[106.74418,16.41904],[106.65832,16.47816],[106.66052,16.56892],[106.61477,16.60713],[106.58267,16.6012],[106.59013,16.62259],[106.55485,16.68704],[106.55265,16.86831],[106.52183,16.87884],[106.51963,16.92097],[106.54824,16.92729],[106.55045,17.0031],[106.50862,16.9673],[106.43597,17.01362],[106.31929,17.20509],[106.29287,17.3018],[106.24444,17.24714],[106.18991,17.28227],[106.09019,17.36399],[105.85744,17.63221],[105.76612,17.67147],[105.60381,17.89356],[105.64784,17.96687],[105.46292,18.22008],[105.38366,18.15315],[105.15942,18.38691],[105.10408,18.43533],[105.1327,18.58355],[105.19654,18.64196],[105.12829,18.70453],[104.64617,18.85668],[104.5361,18.97747],[103.87125,19.31854],[104.06058,19.43484],[104.10832,19.51575],[104.05617,19.61743],[104.06498,19.66926],[104.23229,19.70242],[104.41281,19.70035],[104.53169,19.61743],[104.64837,19.62365],[104.68359,19.72729],[104.8355,19.80395],[104.8465,19.91783],[104.9874,20.09573],[104.91695,20.15567],[104.86852,20.14121],[104.61315,20.24452],[104.62195,20.36633],[104.72102,20.40554],[104.66158,20.47774],[104.47886,20.37459],[104.40621,20.3849],[104.38199,20.47155],[104.63957,20.6653],[104.27412,20.91433],[104.11121,20.96779],[103.98024,20.91531],[103.82282,20.8732],[103.73478,20.6669],[103.68633,20.66324],[103.45737,20.82382],[103.38032,20.79501],[103.21497,20.89832],[103.12055,20.89994],[103.03469,21.05821],[102.97745,21.05821],[102.89825,21.24707],[102.80794,21.25736],[102.88939,21.3107],[102.94223,21.46034],[102.86297,21.4255],[102.98846,21.58936],[102.97965,21.74076],[102.86077,21.71213],[102.85637,21.84501],[102.81894,21.83888],[102.82115,21.73667],[102.74189,21.66713],[102.67145,21.65894],[102.62301,21.91447],[102.49092,21.99002],[102.51734,22.02676],[102.18712,22.30403],[102.14099,22.40092],[102.1245,22.43372]]]]}},{type:"Feature",properties:{iso1A2:"LB",iso1A3:"LBN",iso1N3:"422",wikidata:"Q822",nameEn:"Lebanon",aliases:["RL"],groups:["145","142"],callingCodes:["961"]},geometry:{type:"MultiPolygon",coordinates:[[[[35.94816,33.47886],[35.94465,33.52774],[36.05723,33.57904],[35.9341,33.6596],[36.06778,33.82927],[36.14517,33.85118],[36.3967,33.83365],[36.38263,33.86579],[36.28589,33.91981],[36.41078,34.05253],[36.50576,34.05982],[36.5128,34.09916],[36.62537,34.20251],[36.59195,34.2316],[36.58667,34.27667],[36.60778,34.31009],[36.56556,34.31881],[36.53039,34.3798],[36.55853,34.41609],[36.46179,34.46541],[36.4442,34.50165],[36.34745,34.5002],[36.3369,34.52629],[36.39846,34.55672],[36.41429,34.61175],[36.45299,34.59438],[36.46003,34.6378],[36.42941,34.62505],[36.35384,34.65447],[36.35135,34.68516],[36.32399,34.69334],[36.29165,34.62991],[35.98718,34.64977],[35.97386,34.63322],[35.48515,34.70851],[34.78515,33.20368],[35.10645,33.09318],[35.1924,33.08743],[35.31429,33.10515],[35.35223,33.05617],[35.43059,33.06659],[35.448,33.09264],[35.50272,33.09056],[35.50335,33.114],[35.52573,33.11921],[35.54228,33.19865],[35.5362,33.23196],[35.54808,33.236],[35.54544,33.25513],[35.55555,33.25844],[35.56523,33.28969],[35.58326,33.28381],[35.58502,33.26653],[35.62283,33.24226],[35.62019,33.27278],[35.77477,33.33609],[35.81324,33.36354],[35.82577,33.40479],[35.88668,33.43183],[35.94816,33.47886]]]]}},{type:"Feature",properties:{iso1A2:"LC",iso1A3:"LCA",iso1N3:"662",wikidata:"Q760",nameEn:"St. Lucia",aliases:["WL"],groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 758"]},geometry:{type:"MultiPolygon",coordinates:[[[[-60.5958,14.23076],[-61.26561,14.25664],[-61.43129,13.68336],[-60.70539,13.41452],[-60.5958,14.23076]]]]}},{type:"Feature",properties:{iso1A2:"LI",iso1A3:"LIE",iso1N3:"438",wikidata:"Q347",nameEn:"Liechtenstein",aliases:["FL"],groups:["155","150"],callingCodes:["423"]},geometry:{type:"MultiPolygon",coordinates:[[[[9.60717,47.06091],[9.61216,47.07732],[9.63395,47.08443],[9.62623,47.14685],[9.56539,47.17124],[9.58264,47.20673],[9.56981,47.21926],[9.55176,47.22585],[9.56766,47.24281],[9.53116,47.27029],[9.52406,47.24959],[9.50318,47.22153],[9.4891,47.19346],[9.48774,47.17402],[9.51044,47.13727],[9.52089,47.10019],[9.51362,47.08505],[9.47139,47.06402],[9.47548,47.05257],[9.54041,47.06495],[9.55721,47.04762],[9.60717,47.06091]]]]}},{type:"Feature",properties:{iso1A2:"LK",iso1A3:"LKA",iso1N3:"144",wikidata:"Q854",nameEn:"Sri Lanka",groups:["034","142"],driveSide:"left",callingCodes:["94"]},geometry:{type:"MultiPolygon",coordinates:[[[[76.25812,4.62435],[85.15017,5.21497],[80.48418,10.20786],[79.42124,9.80115],[79.50447,8.91876],[76.25812,4.62435]]]]}},{type:"Feature",properties:{iso1A2:"LR",iso1A3:"LBR",iso1N3:"430",wikidata:"Q1014",nameEn:"Liberia",groups:["011","202","002"],callingCodes:["231"]},geometry:{type:"MultiPolygon",coordinates:[[[[-8.47114,7.55676],[-8.55874,7.62525],[-8.55874,7.70167],[-8.67814,7.69428],[-8.72789,7.51429],[-8.8448,7.35149],[-8.85724,7.26019],[-8.93435,7.2824],[-9.09107,7.1985],[-9.18311,7.30461],[-9.20798,7.38109],[-9.305,7.42056],[-9.41943,7.41809],[-9.48161,7.37122],[-9.37465,7.62032],[-9.35724,7.74111],[-9.44928,7.9284],[-9.41445,8.02448],[-9.50898,8.18455],[-9.47415,8.35195],[-9.77763,8.54633],[-10.05873,8.42578],[-10.05375,8.50697],[-10.14579,8.52665],[-10.203,8.47991],[-10.27575,8.48711],[-10.30084,8.30008],[-10.31635,8.28554],[-10.29839,8.21283],[-10.35227,8.15223],[-10.45023,8.15627],[-10.51554,8.1393],[-10.57523,8.04829],[-10.60492,8.04072],[-10.60422,7.7739],[-11.29417,7.21576],[-11.4027,6.97746],[-11.50429,6.92704],[-12.15048,6.15992],[-7.52774,3.7105],[-7.53259,4.35145],[-7.59349,4.8909],[-7.53876,4.94294],[-7.55369,5.08667],[-7.48901,5.14118],[-7.46165,5.26256],[-7.36463,5.32944],[-7.43428,5.42355],[-7.37209,5.61173],[-7.43926,5.74787],[-7.43677,5.84687],[-7.46165,5.84934],[-7.48155,5.80974],[-7.67309,5.94337],[-7.70294,5.90625],[-7.78254,5.99037],[-7.79747,6.07696],[-7.8497,6.08932],[-7.83478,6.20309],[-7.90692,6.27728],[-8.00642,6.31684],[-8.17557,6.28222],[-8.3298,6.36381],[-8.38453,6.35887],[-8.45666,6.49977],[-8.48652,6.43797],[-8.59456,6.50612],[-8.31736,6.82837],[-8.29249,7.1691],[-8.37458,7.25794],[-8.41935,7.51203],[-8.47114,7.55676]]]]}},{type:"Feature",properties:{iso1A2:"LS",iso1A3:"LSO",iso1N3:"426",wikidata:"Q1013",nameEn:"Lesotho",groups:["018","202","002"],driveSide:"left",callingCodes:["266"]},geometry:{type:"MultiPolygon",coordinates:[[[[29.33204,-29.45598],[29.44883,-29.3772],[29.40524,-29.21246],[28.68043,-28.58744],[28.65091,-28.57025],[28.40612,-28.6215],[28.30518,-28.69531],[28.2348,-28.69471],[28.1317,-28.7293],[28.02503,-28.85991],[27.98675,-28.8787],[27.9392,-28.84864],[27.88933,-28.88156],[27.8907,-28.91612],[27.75458,-28.89839],[27.55974,-29.18954],[27.5158,-29.2261],[27.54258,-29.25575],[27.48679,-29.29349],[27.45125,-29.29708],[27.47254,-29.31968],[27.4358,-29.33465],[27.33464,-29.48161],[27.01016,-29.65439],[27.09489,-29.72796],[27.22719,-30.00718],[27.29603,-30.05473],[27.32555,-30.14785],[27.40778,-30.14577],[27.37293,-30.19401],[27.36649,-30.27246],[27.38108,-30.33456],[27.45452,-30.32239],[27.56901,-30.42504],[27.56781,-30.44562],[27.62137,-30.50509],[27.6521,-30.51707],[27.67819,-30.53437],[27.69467,-30.55862],[27.74814,-30.60635],[28.12073,-30.68072],[28.2319,-30.28476],[28.399,-30.1592],[28.68627,-30.12885],[28.80222,-30.10579],[28.9338,-30.05072],[29.16548,-29.91706],[29.12553,-29.76266],[29.28545,-29.58456],[29.33204,-29.45598]]]]}},{type:"Feature",properties:{iso1A2:"LT",iso1A3:"LTU",iso1N3:"440",wikidata:"Q37",nameEn:"Lithuania",groups:["EU","154","150"],callingCodes:["370"]},geometry:{type:"MultiPolygon",coordinates:[[[[24.89005,56.46666],[24.83686,56.41565],[24.70022,56.40483],[24.57353,56.31525],[24.58143,56.29125],[24.42746,56.26522],[24.32334,56.30226],[24.13139,56.24881],[24.02657,56.3231],[23.75726,56.37282],[23.49803,56.34307],[23.40486,56.37689],[23.31606,56.3827],[23.17312,56.36795],[23.09531,56.30511],[22.96988,56.41213],[22.83048,56.367],[22.69354,56.36284],[22.56441,56.39305],[22.3361,56.4016],[22.09728,56.42851],[22.00548,56.41508],[21.74558,56.33181],[21.57888,56.31406],[21.49736,56.29106],[21.24644,56.16917],[21.15016,56.07818],[20.68447,56.04073],[20.60454,55.40986],[20.95181,55.27994],[21.26425,55.24456],[21.35465,55.28427],[21.38446,55.29348],[21.46766,55.21115],[21.51095,55.18507],[21.55605,55.20311],[21.64954,55.1791],[21.85521,55.09493],[21.96505,55.07353],[21.99543,55.08691],[22.03984,55.07888],[22.02582,55.05078],[22.06087,55.02935],[22.11697,55.02131],[22.14267,55.05345],[22.31562,55.0655],[22.47688,55.04408],[22.58907,55.07085],[22.60075,55.01863],[22.65451,54.97037],[22.68723,54.9811],[22.76422,54.92521],[22.85083,54.88711],[22.87317,54.79492],[22.73631,54.72952],[22.73397,54.66604],[22.75467,54.6483],[22.74225,54.64339],[22.7522,54.63525],[22.68021,54.58486],[22.71293,54.56454],[22.67788,54.532],[22.70208,54.45312],[22.7253,54.41732],[22.79705,54.36264],[22.83756,54.40827],[23.00584,54.38514],[22.99649,54.35927],[23.05726,54.34565],[23.04323,54.31567],[23.104,54.29794],[23.13905,54.31567],[23.15526,54.31076],[23.15938,54.29894],[23.24656,54.25701],[23.3494,54.25155],[23.39525,54.21672],[23.42418,54.17911],[23.45223,54.17775],[23.49196,54.14764],[23.52702,54.04622],[23.48261,53.98855],[23.51284,53.95052],[23.61677,53.92691],[23.71726,53.93379],[23.80543,53.89558],[23.81309,53.94205],[23.95098,53.9613],[23.98837,53.92554],[24.19638,53.96405],[24.34128,53.90076],[24.44411,53.90076],[24.62275,54.00217],[24.69652,54.01901],[24.69185,53.96543],[24.74279,53.96663],[24.85311,54.02862],[24.77131,54.11091],[24.96894,54.17589],[24.991,54.14241],[25.0728,54.13419],[25.19199,54.219],[25.22705,54.26271],[25.35559,54.26544],[25.509,54.30267],[25.56823,54.25212],[25.51452,54.17799],[25.54724,54.14925],[25.64875,54.1259],[25.71084,54.16704],[25.78563,54.15747],[25.78553,54.23327],[25.68513,54.31727],[25.55425,54.31591],[25.5376,54.33158],[25.63371,54.42075],[25.62203,54.4656],[25.64813,54.48704],[25.68045,54.5321],[25.75977,54.57252],[25.74122,54.80108],[25.89462,54.93438],[25.99129,54.95705],[26.05907,54.94631],[26.13386,54.98924],[26.20397,54.99729],[26.26941,55.08032],[26.23202,55.10439],[26.30628,55.12536],[26.35121,55.1525],[26.46249,55.12814],[26.51481,55.16051],[26.54753,55.14181],[26.69243,55.16718],[26.68075,55.19787],[26.72983,55.21788],[26.73017,55.24226],[26.835,55.28182],[26.83266,55.30444],[26.80929,55.31642],[26.6714,55.33902],[26.5709,55.32572],[26.44937,55.34832],[26.5522,55.40277],[26.55094,55.5093],[26.63167,55.57887],[26.63231,55.67968],[26.58248,55.6754],[26.46661,55.70375],[26.39561,55.71156],[26.18509,55.86813],[26.03815,55.95884],[25.90047,56.0013],[25.85893,56.00188],[25.81773,56.05444],[25.69246,56.08892],[25.68588,56.14725],[25.53621,56.16663],[25.39751,56.15707],[25.23099,56.19147],[25.09325,56.1878],[25.05762,56.26742],[24.89005,56.46666]]]]}},{type:"Feature",properties:{iso1A2:"LU",iso1A3:"LUX",iso1N3:"442",wikidata:"Q32",nameEn:"Luxembourg",groups:["EU","155","150"],callingCodes:["352"]},geometry:{type:"MultiPolygon",coordinates:[[[[6.1379,50.12964],[6.1137,50.13668],[6.12028,50.16374],[6.08577,50.17246],[6.06406,50.15344],[6.03093,50.16362],[6.02488,50.18283],[5.96453,50.17259],[5.95929,50.13295],[5.89488,50.11476],[5.8857,50.07824],[5.85474,50.06342],[5.86904,50.04614],[5.8551,50.02683],[5.81866,50.01286],[5.82331,49.99662],[5.83968,49.9892],[5.83467,49.97823],[5.81163,49.97142],[5.80833,49.96451],[5.77291,49.96056],[5.77314,49.93646],[5.73621,49.89796],[5.78415,49.87922],[5.75269,49.8711],[5.75861,49.85631],[5.74567,49.85368],[5.75884,49.84811],[5.74953,49.84709],[5.74975,49.83933],[5.74076,49.83823],[5.7404,49.83452],[5.74844,49.82435],[5.74364,49.82058],[5.74953,49.81428],[5.75409,49.79239],[5.78871,49.7962],[5.82245,49.75048],[5.83149,49.74729],[5.82562,49.72395],[5.84193,49.72161],[5.86503,49.72739],[5.88677,49.70951],[5.86527,49.69291],[5.86175,49.67862],[5.9069,49.66377],[5.90164,49.6511],[5.90599,49.63853],[5.88552,49.63507],[5.88393,49.62802],[5.87609,49.62047],[5.8762,49.60898],[5.84826,49.5969],[5.84971,49.58674],[5.86986,49.58756],[5.87256,49.57539],[5.8424,49.56082],[5.84692,49.55663],[5.84143,49.5533],[5.81838,49.54777],[5.80871,49.5425],[5.81664,49.53775],[5.83648,49.5425],[5.84466,49.53027],[5.83467,49.52717],[5.83389,49.52152],[5.86571,49.50015],[5.94128,49.50034],[5.94224,49.49608],[5.96876,49.49053],[5.97693,49.45513],[6.02648,49.45451],[6.02743,49.44845],[6.04176,49.44801],[6.05553,49.46663],[6.07887,49.46399],[6.08373,49.45594],[6.10072,49.45268],[6.09845,49.46351],[6.10325,49.4707],[6.12346,49.4735],[6.12814,49.49365],[6.14321,49.48796],[6.16115,49.49297],[6.15366,49.50226],[6.17386,49.50934],[6.19543,49.50536],[6.2409,49.51408],[6.25029,49.50609],[6.27875,49.503],[6.28818,49.48465],[6.3687,49.4593],[6.36778,49.46937],[6.36907,49.48931],[6.36788,49.50377],[6.35666,49.52931],[6.38072,49.55171],[6.38228,49.55855],[6.35825,49.57053],[6.36676,49.57813],[6.38024,49.57593],[6.38342,49.5799],[6.37464,49.58886],[6.385,49.59946],[6.39822,49.60081],[6.41861,49.61723],[6.4413,49.65722],[6.43768,49.66021],[6.42726,49.66078],[6.42937,49.66857],[6.44654,49.67799],[6.46048,49.69092],[6.48014,49.69767],[6.49785,49.71118],[6.50647,49.71353],[6.5042,49.71808],[6.49694,49.72205],[6.49535,49.72645],[6.50261,49.72718],[6.51397,49.72058],[6.51805,49.72425],[6.50193,49.73291],[6.50174,49.75292],[6.51646,49.75961],[6.51828,49.76855],[6.51056,49.77515],[6.51669,49.78336],[6.50534,49.78952],[6.52169,49.79787],[6.53122,49.80666],[6.52121,49.81338],[6.51215,49.80124],[6.50647,49.80916],[6.48718,49.81267],[6.47111,49.82263],[6.45425,49.81164],[6.44131,49.81443],[6.42905,49.81091],[6.42521,49.81591],[6.40022,49.82029],[6.36576,49.85032],[6.34267,49.84974],[6.33585,49.83785],[6.32098,49.83728],[6.32303,49.85133],[6.30963,49.87021],[6.29692,49.86685],[6.28874,49.87592],[6.26146,49.88203],[6.23496,49.89972],[6.22926,49.92096],[6.21882,49.92403],[6.22608,49.929],[6.22094,49.94955],[6.19856,49.95053],[6.19089,49.96991],[6.18045,49.96611],[6.18554,49.95622],[6.17872,49.9537],[6.16466,49.97086],[6.1701,49.98518],[6.14147,49.99563],[6.14948,50.00908],[6.13806,50.01056],[6.1295,50.01849],[6.13273,50.02019],[6.13794,50.01466],[6.14666,50.02207],[6.13044,50.02929],[6.13458,50.04141],[6.11274,50.05916],[6.12055,50.09171],[6.1379,50.12964]]]]}},{type:"Feature",properties:{iso1A2:"LV",iso1A3:"LVA",iso1N3:"428",wikidata:"Q211",nameEn:"Latvia",groups:["EU","154","150"],callingCodes:["371"]},geometry:{type:"MultiPolygon",coordinates:[[[[27.34698,57.52242],[26.90364,57.62823],[26.54675,57.51813],[26.46527,57.56885],[26.29253,57.59244],[26.1866,57.6849],[26.2029,57.7206],[26.08098,57.76619],[26.0543,57.76105],[26.03332,57.7718],[26.02415,57.76865],[26.02069,57.77169],[26.0266,57.77441],[26.027,57.78158],[26.02456,57.78342],[26.0324,57.79037],[26.05949,57.84744],[25.73499,57.90193],[25.29581,58.08288],[25.28237,57.98539],[25.19484,58.0831],[24.3579,57.87471],[24.26221,57.91787],[23.20055,57.56697],[22.80496,57.87798],[19.84909,57.57876],[19.64795,57.06466],[20.68447,56.04073],[21.15016,56.07818],[21.24644,56.16917],[21.49736,56.29106],[21.57888,56.31406],[21.74558,56.33181],[22.00548,56.41508],[22.09728,56.42851],[22.3361,56.4016],[22.56441,56.39305],[22.69354,56.36284],[22.83048,56.367],[22.96988,56.41213],[23.09531,56.30511],[23.17312,56.36795],[23.31606,56.3827],[23.40486,56.37689],[23.49803,56.34307],[23.75726,56.37282],[24.02657,56.3231],[24.13139,56.24881],[24.32334,56.30226],[24.42746,56.26522],[24.58143,56.29125],[24.57353,56.31525],[24.70022,56.40483],[24.83686,56.41565],[24.89005,56.46666],[25.05762,56.26742],[25.09325,56.1878],[25.23099,56.19147],[25.39751,56.15707],[25.53621,56.16663],[25.68588,56.14725],[25.69246,56.08892],[25.81773,56.05444],[25.85893,56.00188],[25.90047,56.0013],[26.03815,55.95884],[26.18509,55.86813],[26.39561,55.71156],[26.46661,55.70375],[26.58248,55.6754],[26.63231,55.67968],[26.64888,55.70515],[26.71802,55.70645],[26.76872,55.67658],[26.87448,55.7172],[26.97153,55.8102],[27.1559,55.85032],[27.27804,55.78299],[27.3541,55.8089],[27.61683,55.78558],[27.63065,55.89687],[27.97865,56.11849],[28.15217,56.16964],[28.23716,56.27588],[28.16599,56.37806],[28.19057,56.44637],[28.10069,56.524],[28.13526,56.57989],[28.04768,56.59004],[27.86101,56.88204],[27.66511,56.83921],[27.86101,57.29402],[27.52453,57.42826],[27.56832,57.53728],[27.34698,57.52242]]]]}},{type:"Feature",properties:{iso1A2:"LY",iso1A3:"LBY",iso1N3:"434",wikidata:"Q1016",nameEn:"Libya",groups:["015","002"],callingCodes:["218"]},geometry:{type:"MultiPolygon",coordinates:[[[[22.5213,33.45682],[11.66543,33.34642],[11.56255,33.16754],[11.55852,33.1409],[11.51549,33.09826],[11.46037,32.6307],[11.57828,32.48013],[11.53898,32.4138],[11.04234,32.2145],[10.7315,31.97235],[10.62788,31.96629],[10.48497,31.72956],[10.31364,31.72648],[10.12239,31.42098],[10.29516,30.90337],[9.88152,30.34074],[9.76848,30.34366],[9.55544,30.23971],[9.3876,30.16738],[9.78136,29.40961],[9.89569,26.57696],[9.51696,26.39148],[9.38834,26.19288],[10.03146,25.35635],[10.02432,24.98124],[10.33159,24.5465],[10.85323,24.5595],[11.41061,24.21456],[11.62498,24.26669],[11.96886,23.51735],[13.5631,23.16574],[14.22918,22.61719],[14.99751,23.00539],[15.99566,23.49639],[23.99539,19.49944],[23.99715,20.00038],[24.99794,19.99661],[24.99885,21.99535],[24.99968,29.24574],[24.71117,30.17441],[25.01077,30.73861],[24.83101,31.31921],[25.06041,31.57937],[25.14001,31.67534],[25.63787,31.9359],[22.5213,33.45682]]]]}},{type:"Feature",properties:{iso1A2:"MA",iso1A3:"MAR",iso1N3:"504",wikidata:"Q1028",nameEn:"Morocco",groups:["015","002"],callingCodes:["212"]},geometry:{type:"MultiPolygon",coordinates:[[[[-2.27707,35.35051],[-2.85819,35.63219],[-5.10878,36.05227],[-5.64962,35.93752],[-7.27694,35.93599],[-14.43883,27.02969],[-17.27295,21.93519],[-17.21511,21.34226],[-17.02707,21.34022],[-16.9978,21.36239],[-16.44269,21.39745],[-14.78487,21.36587],[-14.47329,21.63839],[-14.48112,22.00886],[-14.1291,22.41636],[-14.10361,22.75501],[-13.75627,23.77231],[-13.00628,24.01923],[-12.92147,24.39502],[-12.12281,25.13682],[-12.06001,26.04442],[-11.62052,26.05229],[-11.38635,26.611],[-11.23622,26.72023],[-11.35695,26.8505],[-10.68417,26.90984],[-9.81998,26.71379],[-9.56957,26.90042],[-9.08698,26.98639],[-8.71787,26.9898],[-8.77527,27.66663],[-8.66879,27.6666],[-8.6715,28.71194],[-7.61585,29.36252],[-6.95824,29.50924],[-6.78351,29.44634],[-6.69965,29.51623],[-5.75616,29.61407],[-5.72121,29.52322],[-5.58831,29.48103],[-5.21671,29.95253],[-4.6058,30.28343],[-4.31774,30.53229],[-3.64735,30.67539],[-3.65418,30.85566],[-3.54944,31.0503],[-3.77103,31.14984],[-3.77647,31.31912],[-3.66386,31.39202],[-3.66314,31.6339],[-2.82784,31.79459],[-2.93873,32.06557],[-2.46166,32.16603],[-1.22829,32.07832],[-1.15735,32.12096],[-1.24453,32.1917],[-1.24998,32.32993],[-0.9912,32.52467],[-1.37794,32.73628],[-1.54244,32.95499],[-1.46249,33.0499],[-1.67067,33.27084],[-1.59508,33.59929],[-1.73494,33.71721],[-1.64666,34.10405],[-1.78042,34.39018],[-1.69788,34.48056],[-1.84569,34.61907],[-1.73707,34.74226],[-1.97469,34.886],[-1.97833,34.93218],[-2.04734,34.93218],[-2.21445,35.04378],[-2.21248,35.08532],[-2.27707,35.35051]],[[-2.92224,35.3401],[-2.92181,35.28599],[-2.92674,35.27313],[-2.93893,35.26737],[-2.95065,35.26576],[-2.95431,35.2728],[-2.96516,35.27967],[-2.96826,35.28296],[-2.96507,35.28801],[-2.97035,35.28852],[-2.96978,35.29459],[-2.96648,35.30475],[-2.96038,35.31609],[-2.92224,35.3401]],[[-3.90602,35.21494],[-3.90288,35.22024],[-3.88617,35.21406],[-3.88926,35.20841],[-3.90602,35.21494]],[[-4.30191,35.17419],[-4.29436,35.17149],[-4.30112,35.17058],[-4.30191,35.17419]],[[-2.41312,35.17111],[-2.44887,35.17075],[-2.44896,35.18777],[-2.41265,35.1877],[-2.41312,35.17111]],[[-5.38491,35.92591],[-5.27635,35.91222],[-5.27056,35.88794],[-5.34379,35.8711],[-5.35844,35.87375],[-5.37338,35.88417],[-5.38491,35.92591]]]]}},{type:"Feature",properties:{iso1A2:"MC",iso1A3:"MCO",iso1N3:"492",wikidata:"Q235",nameEn:"Monaco",groups:["155","150"],callingCodes:["377"]},geometry:{type:"MultiPolygon",coordinates:[[[[7.47823,43.73341],[7.4379,43.74963],[7.4389,43.75151],[7.43708,43.75197],[7.43624,43.75014],[7.43013,43.74895],[7.42809,43.74396],[7.42443,43.74087],[7.42299,43.74176],[7.42062,43.73977],[7.41233,43.73439],[7.41298,43.73311],[7.41291,43.73168],[7.41113,43.73156],[7.40903,43.7296],[7.42422,43.72209],[7.47823,43.73341]]]]}},{type:"Feature",properties:{iso1A2:"MD",iso1A3:"MDA",iso1N3:"498",wikidata:"Q217",nameEn:"Moldova",groups:["151","150"],callingCodes:["373"]},geometry:{type:"MultiPolygon",coordinates:[[[[27.74422,48.45926],[27.6658,48.44034],[27.59027,48.46311],[27.5889,48.49224],[27.46942,48.454],[27.44333,48.41209],[27.37741,48.41026],[27.37604,48.44398],[27.32159,48.4434],[27.27855,48.37534],[27.13434,48.37288],[27.08078,48.43214],[27.0231,48.42485],[27.03821,48.37653],[26.93384,48.36558],[26.85556,48.41095],[26.71274,48.40388],[26.82809,48.31629],[26.79239,48.29071],[26.6839,48.35828],[26.62823,48.25804],[26.81161,48.25049],[26.87708,48.19919],[26.94265,48.1969],[26.98042,48.15752],[26.96119,48.13003],[27.04118,48.12522],[27.02985,48.09083],[27.15622,47.98538],[27.1618,47.92391],[27.29069,47.73722],[27.25519,47.71366],[27.32202,47.64009],[27.3979,47.59473],[27.47942,47.48113],[27.55731,47.46637],[27.60263,47.32507],[27.68706,47.28962],[27.73172,47.29248],[27.81892,47.1381],[28.09095,46.97621],[28.12173,46.82283],[28.24808,46.64305],[28.22281,46.50481],[28.25769,46.43334],[28.18902,46.35283],[28.19864,46.31869],[28.10937,46.22852],[28.13684,46.18099],[28.08612,46.01105],[28.13111,45.92819],[28.16568,45.6421],[28.08927,45.6051],[28.18741,45.47358],[28.21139,45.46895],[28.30201,45.54744],[28.41836,45.51715],[28.43072,45.48538],[28.51449,45.49982],[28.49252,45.56716],[28.54196,45.58062],[28.51587,45.6613],[28.47879,45.66994],[28.52823,45.73803],[28.70401,45.78019],[28.69852,45.81753],[28.78503,45.83475],[28.74383,45.96664],[28.98004,46.00385],[29.00613,46.04962],[28.94643,46.09176],[29.06656,46.19716],[28.94953,46.25852],[28.98478,46.31803],[29.004,46.31495],[28.9306,46.45699],[29.01241,46.46177],[29.02409,46.49582],[29.23547,46.55435],[29.24886,46.37912],[29.35357,46.49505],[29.49914,46.45889],[29.5939,46.35472],[29.6763,46.36041],[29.66359,46.4215],[29.74496,46.45605],[29.88329,46.35851],[29.94114,46.40114],[30.09103,46.38694],[30.16794,46.40967],[30.02511,46.45132],[29.88916,46.54302],[29.94409,46.56002],[29.9743,46.75325],[29.94522,46.80055],[29.98814,46.82358],[29.87405,46.88199],[29.75458,46.8604],[29.72986,46.92234],[29.57056,46.94766],[29.62137,47.05069],[29.61038,47.09932],[29.53044,47.07851],[29.49732,47.12878],[29.57696,47.13581],[29.54996,47.24962],[29.59665,47.25521],[29.5733,47.36508],[29.48678,47.36043],[29.47854,47.30366],[29.39889,47.30179],[29.3261,47.44664],[29.18603,47.43387],[29.11743,47.55001],[29.22414,47.60012],[29.22242,47.73607],[29.27255,47.79953],[29.20663,47.80367],[29.27804,47.88893],[29.19839,47.89261],[29.1723,47.99013],[28.9306,47.96255],[28.8414,48.03392],[28.85232,48.12506],[28.69896,48.13106],[28.53921,48.17453],[28.48428,48.0737],[28.42454,48.12047],[28.43701,48.15832],[28.38712,48.17567],[28.34009,48.13147],[28.30609,48.14018],[28.30586,48.1597],[28.34912,48.1787],[28.36996,48.20543],[28.35519,48.24957],[28.32508,48.23384],[28.2856,48.23202],[28.19314,48.20749],[28.17666,48.25963],[28.07504,48.23494],[28.09873,48.3124],[28.04527,48.32661],[27.95883,48.32368],[27.88391,48.36699],[27.87533,48.4037],[27.81902,48.41874],[27.79225,48.44244],[27.74422,48.45926]]]]}},{type:"Feature",properties:{iso1A2:"ME",iso1A3:"MNE",iso1N3:"499",wikidata:"Q236",nameEn:"Montenegro",groups:["039","150"],callingCodes:["382"]},geometry:{type:"MultiPolygon",coordinates:[[[[19.22807,43.5264],[19.15685,43.53943],[19.13933,43.5282],[19.04934,43.50384],[19.01078,43.55806],[18.91379,43.50299],[18.95469,43.49367],[18.96053,43.45042],[19.01078,43.43854],[19.04071,43.397],[19.08673,43.31453],[19.08206,43.29668],[19.04233,43.30008],[19.00844,43.24988],[18.95001,43.29327],[18.95819,43.32899],[18.90911,43.36383],[18.83912,43.34795],[18.84794,43.33735],[18.85342,43.32426],[18.76538,43.29838],[18.6976,43.25243],[18.71747,43.2286],[18.66605,43.2056],[18.64735,43.14766],[18.66254,43.03928],[18.52232,43.01451],[18.49076,42.95553],[18.49661,42.89306],[18.4935,42.86433],[18.47633,42.85829],[18.45921,42.81682],[18.47324,42.74992],[18.56789,42.72074],[18.55221,42.69045],[18.54603,42.69171],[18.54841,42.68328],[18.57373,42.64429],[18.52232,42.62279],[18.55504,42.58409],[18.53751,42.57376],[18.49778,42.58409],[18.43735,42.55921],[18.44307,42.51077],[18.43588,42.48556],[18.52152,42.42302],[18.54128,42.39171],[18.45131,42.21682],[19.26406,41.74971],[19.37597,41.84849],[19.37451,41.8842],[19.33812,41.90669],[19.34601,41.95675],[19.37691,41.96977],[19.36867,42.02564],[19.37548,42.06835],[19.40687,42.10024],[19.28623,42.17745],[19.42,42.33019],[19.42352,42.36546],[19.4836,42.40831],[19.65972,42.62774],[19.73244,42.66299],[19.77375,42.58517],[19.74731,42.57422],[19.76549,42.50237],[19.82333,42.46581],[19.9324,42.51699],[20.00842,42.5109],[20.01834,42.54622],[20.07761,42.55582],[20.0969,42.65559],[20.02915,42.71147],[20.02088,42.74789],[20.04898,42.77701],[20.2539,42.76245],[20.27869,42.81945],[20.35692,42.8335],[20.34528,42.90676],[20.16415,42.97177],[20.14896,42.99058],[20.12325,42.96237],[20.05431,42.99571],[20.04729,43.02732],[19.98887,43.0538],[19.96549,43.11098],[19.92576,43.08539],[19.79255,43.11951],[19.76918,43.16044],[19.64063,43.19027],[19.62661,43.2286],[19.54598,43.25158],[19.52962,43.31623],[19.48171,43.32644],[19.44315,43.38846],[19.22229,43.47926],[19.22807,43.5264]]]]}},{type:"Feature",properties:{iso1A2:"MF",iso1A3:"MAF",iso1N3:"663",wikidata:"Q126125",nameEn:"Saint-Martin",country:"FR",groups:["EU","029","003","419","019"],callingCodes:["590"]},geometry:{type:"MultiPolygon",coordinates:[[[[-62.93924,18.02904],[-62.75637,18.13489],[-62.86666,18.19278],[-63.35989,18.06012],[-63.33064,17.9615],[-63.13584,18.0541],[-63.11096,18.05368],[-63.09686,18.04608],[-63.07759,18.04943],[-63.0579,18.06614],[-63.04039,18.05619],[-63.02323,18.05757],[-62.93924,18.02904]]]]}},{type:"Feature",properties:{iso1A2:"MG",iso1A3:"MDG",iso1N3:"450",wikidata:"Q1019",nameEn:"Madagascar",aliases:["RM"],groups:["014","202","002"],callingCodes:["261"]},geometry:{type:"MultiPolygon",coordinates:[[[[51.94557,-12.74579],[49.10033,-10.96054],[43.72277,-16.09877],[40.40841,-23.17181],[45.90777,-29.77366],[51.94557,-12.74579]]]]}},{type:"Feature",properties:{iso1A2:"MH",iso1A3:"MHL",iso1N3:"584",wikidata:"Q709",nameEn:"Marshall Islands",groups:["057","009"],roadSpeedUnit:"mph",callingCodes:["692"]},geometry:{type:"MultiPolygon",coordinates:[[[[169,3.9],[173.53711,5.70687],[169.29099,15.77133],[159.04653,10.59067],[169,3.9]]]]}},{type:"Feature",properties:{iso1A2:"MK",iso1A3:"MKD",iso1N3:"807",wikidata:"Q221",nameEn:"North Macedonia",groups:["039","150"],callingCodes:["389"]},geometry:{type:"MultiPolygon",coordinates:[[[[22.34773,42.31725],[22.29275,42.34913],[22.29605,42.37477],[22.16384,42.32103],[22.02908,42.29848],[21.94405,42.34669],[21.91595,42.30392],[21.84654,42.3247],[21.77176,42.2648],[21.70111,42.23789],[21.58992,42.25915],[21.52145,42.24465],[21.50823,42.27156],[21.43882,42.2789],[21.43882,42.23609],[21.38428,42.24465],[21.30496,42.1418],[21.29913,42.13954],[21.31983,42.10993],[21.22728,42.08909],[21.16614,42.19815],[21.11491,42.20794],[20.75464,42.05229],[20.76786,41.91839],[20.68523,41.85318],[20.59524,41.8818],[20.55976,41.87068],[20.57144,41.7897],[20.53405,41.78099],[20.51301,41.72433],[20.52937,41.69292],[20.51769,41.65975],[20.55508,41.58113],[20.52103,41.56473],[20.45809,41.5549],[20.45331,41.51436],[20.49039,41.49277],[20.51301,41.442],[20.55976,41.4087],[20.52119,41.34381],[20.49432,41.33679],[20.51068,41.2323],[20.59715,41.13644],[20.58546,41.11179],[20.59832,41.09066],[20.63454,41.0889],[20.65558,41.08009],[20.71634,40.91781],[20.73504,40.9081],[20.81567,40.89662],[20.83671,40.92752],[20.94305,40.92399],[20.97693,40.90103],[20.97887,40.85475],[21.15262,40.85546],[21.21105,40.8855],[21.25779,40.86165],[21.35595,40.87578],[21.41555,40.9173],[21.53007,40.90759],[21.57448,40.86076],[21.69601,40.9429],[21.7556,40.92525],[21.91102,41.04786],[21.90869,41.09191],[22.06527,41.15617],[22.1424,41.12449],[22.17629,41.15969],[22.26744,41.16409],[22.42285,41.11921],[22.5549,41.13065],[22.58295,41.11568],[22.62852,41.14385],[22.65306,41.18168],[22.71266,41.13945],[22.74538,41.16321],[22.76408,41.32225],[22.81199,41.3398],[22.93334,41.34104],[22.96331,41.35782],[22.95513,41.63265],[23.03342,41.71034],[23.01239,41.76527],[22.96682,41.77137],[22.90254,41.87587],[22.86749,42.02275],[22.67701,42.06614],[22.51224,42.15457],[22.50289,42.19527],[22.47251,42.20393],[22.38136,42.30339],[22.34773,42.31725]]]]}},{type:"Feature",properties:{iso1A2:"ML",iso1A3:"MLI",iso1N3:"466",wikidata:"Q912",nameEn:"Mali",groups:["011","202","002"],callingCodes:["223"]},geometry:{type:"MultiPolygon",coordinates:[[[[-4.83423,24.99935],[-6.57191,25.0002],[-5.60725,16.49919],[-5.33435,16.33354],[-5.50165,15.50061],[-9.32979,15.50032],[-9.31106,15.69412],[-9.33314,15.7044],[-9.44673,15.60553],[-9.40447,15.4396],[-10.71721,15.4223],[-10.90932,15.11001],[-11.43483,15.62339],[-11.70705,15.51558],[-11.94903,14.76143],[-12.23936,14.76324],[-11.93043,13.84505],[-12.06897,13.71049],[-11.83345,13.33333],[-11.63025,13.39174],[-11.39935,12.97808],[-11.37536,12.40788],[-11.50006,12.17826],[-11.24136,12.01286],[-10.99758,12.24634],[-10.80355,12.1053],[-10.71897,11.91552],[-10.30604,12.24634],[-9.714,12.0226],[-9.63938,12.18312],[-9.32097,12.29009],[-9.38067,12.48446],[-9.13689,12.50875],[-8.94784,12.34842],[-8.80854,11.66715],[-8.40058,11.37466],[-8.66923,10.99397],[-8.35083,11.06234],[-8.2667,10.91762],[-8.32614,10.69273],[-8.22711,10.41722],[-8.10207,10.44649],[-7.9578,10.2703],[-7.97971,10.17117],[-7.92107,10.15577],[-7.63048,10.46334],[-7.54462,10.40921],[-7.52261,10.4655],[-7.44555,10.44602],[-7.3707,10.24677],[-7.13331,10.24877],[-7.0603,10.14711],[-7.00966,10.15794],[-6.97444,10.21644],[-7.01186,10.25111],[-6.93921,10.35291],[-6.68164,10.35074],[-6.63541,10.66893],[-6.52974,10.59104],[-6.42847,10.5694],[-6.40646,10.69922],[-6.325,10.68624],[-6.24795,10.74248],[-6.1731,10.46983],[-6.18851,10.24244],[-5.99478,10.19694],[-5.78124,10.43952],[-5.65135,10.46767],[-5.51058,10.43177],[-5.46643,10.56074],[-5.47083,10.75329],[-5.41579,10.84628],[-5.49284,11.07538],[-5.32994,11.13371],[-5.32553,11.21578],[-5.25949,11.24816],[-5.25509,11.36905],[-5.20665,11.43811],[-5.22867,11.60421],[-5.29251,11.61715],[-5.26389,11.75728],[-5.40258,11.8327],[-5.26389,11.84778],[-5.07897,11.97918],[-4.72893,12.01579],[-4.70692,12.06746],[-4.62987,12.06531],[-4.62546,12.13204],[-4.54841,12.1385],[-4.57703,12.19875],[-4.41412,12.31922],[-4.47356,12.71252],[-4.238,12.71467],[-4.21819,12.95722],[-4.34477,13.12927],[-3.96501,13.49778],[-3.90558,13.44375],[-3.96282,13.38164],[-3.7911,13.36665],[-3.54454,13.1781],[-3.4313,13.1588],[-3.43507,13.27272],[-3.23599,13.29035],[-3.28396,13.5422],[-3.26407,13.70699],[-2.88189,13.64921],[-2.90831,13.81174],[-2.84667,14.05532],[-2.66175,14.14713],[-2.47587,14.29671],[-2.10223,14.14878],[-1.9992,14.19011],[-1.97945,14.47709],[-1.68083,14.50023],[-1.32166,14.72774],[-1.05875,14.7921],[-0.72004,15.08655],[-0.24673,15.07805],[0.06588,14.96961],[0.23859,15.00135],[0.72632,14.95898],[0.96711,14.98275],[1.31275,15.27978],[3.01806,15.34571],[3.03134,15.42221],[3.50368,15.35934],[4.19893,16.39923],[4.21787,17.00118],[4.26762,17.00432],[4.26651,19.14224],[3.36082,18.9745],[3.12501,19.1366],[3.24648,19.81703],[1.20992,20.73533],[1.15698,21.12843],[-4.83423,24.99935]]]]}},{type:"Feature",properties:{iso1A2:"MM",iso1A3:"MMR",iso1N3:"104",wikidata:"Q836",nameEn:"Myanmar",aliases:["Burma","BU"],groups:["035","142"],callingCodes:["95"]},geometry:{type:"MultiPolygon",coordinates:[[[[92.62187,21.87037],[92.59775,21.6092],[92.68152,21.28454],[92.60187,21.24615],[92.55105,21.3856],[92.43158,21.37025],[92.37939,21.47764],[92.20087,21.337],[92.17752,21.17445],[92.26071,21.05697],[92.37665,20.72172],[92.28464,20.63179],[92.31348,20.57137],[92.4302,20.5688],[92.39837,20.38919],[92.61042,13.76986],[94.6371,13.81803],[97.63455,9.60854],[98.12555,9.44056],[98.33094,9.91973],[98.47298,9.95782],[98.52291,9.92389],[98.55174,9.92804],[98.7391,10.31488],[98.81944,10.52761],[98.77275,10.62548],[98.78511,10.68351],[98.86819,10.78336],[99.0069,10.85485],[98.99701,10.92962],[99.02337,10.97217],[99.06938,10.94857],[99.32756,11.28545],[99.31573,11.32081],[99.39485,11.3925],[99.47598,11.62434],[99.5672,11.62732],[99.64108,11.78948],[99.64891,11.82699],[99.53424,12.02317],[99.56445,12.14805],[99.47519,12.1353],[99.409,12.60603],[99.29254,12.68921],[99.18905,12.84799],[99.18748,12.9898],[99.10646,13.05804],[99.12225,13.19847],[99.20617,13.20575],[99.16695,13.72621],[98.97356,14.04868],[98.56762,14.37701],[98.24874,14.83013],[98.18821,15.13125],[98.22,15.21327],[98.30446,15.30667],[98.40522,15.25268],[98.41906,15.27103],[98.39351,15.34177],[98.4866,15.39154],[98.56027,15.33471],[98.58598,15.46821],[98.541,15.65406],[98.59853,15.87197],[98.57019,16.04578],[98.69585,16.13353],[98.8376,16.11706],[98.92656,16.36425],[98.84485,16.42354],[98.68074,16.27068],[98.63817,16.47424],[98.57912,16.55983],[98.5695,16.62826],[98.51113,16.64503],[98.51833,16.676],[98.51472,16.68521],[98.51579,16.69433],[98.51043,16.70107],[98.49713,16.69022],[98.50253,16.7139],[98.46994,16.73613],[98.53833,16.81934],[98.49603,16.8446],[98.52624,16.89979],[98.39441,17.06266],[98.34566,17.04822],[98.10439,17.33847],[98.11185,17.36829],[97.91829,17.54504],[97.76407,17.71595],[97.66794,17.88005],[97.73723,17.97912],[97.60841,18.23846],[97.64116,18.29778],[97.56219,18.33885],[97.50383,18.26844],[97.34522,18.54596],[97.36444,18.57138],[97.5258,18.4939],[97.76752,18.58097],[97.73836,18.88478],[97.66487,18.9371],[97.73654,18.9812],[97.73797,19.04261],[97.83479,19.09972],[97.84024,19.22217],[97.78606,19.26769],[97.84186,19.29526],[97.78769,19.39429],[97.88423,19.5041],[97.84715,19.55782],[98.04364,19.65755],[98.03314,19.80941],[98.13829,19.78541],[98.24884,19.67876],[98.51182,19.71303],[98.56065,19.67807],[98.83661,19.80931],[98.98679,19.7419],[99.0735,20.10298],[99.20328,20.12877],[99.416,20.08614],[99.52943,20.14811],[99.5569,20.20676],[99.46077,20.36198],[99.46008,20.39673],[99.68255,20.32077],[99.81096,20.33687],[99.86383,20.44371],[99.88211,20.44488],[99.88451,20.44596],[99.89168,20.44548],[99.89301,20.44311],[99.89692,20.44789],[99.90499,20.4487],[99.91616,20.44986],[99.95721,20.46301],[100.08404,20.36626],[100.1957,20.68247],[100.36375,20.82783],[100.51079,20.82194],[100.60112,20.8347],[100.64628,20.88279],[100.50974,20.88574],[100.55281,21.02796],[100.63578,21.05639],[100.72716,21.31786],[100.80173,21.2934],[101.00234,21.39612],[101.16198,21.52808],[101.15156,21.56129],[101.11744,21.77659],[100.87265,21.67396],[100.72143,21.51898],[100.57861,21.45637],[100.4811,21.46148],[100.42892,21.54325],[100.35201,21.53176],[100.25863,21.47043],[100.18447,21.51898],[100.1625,21.48704],[100.12542,21.50365],[100.10757,21.59945],[100.17486,21.65306],[100.12679,21.70539],[100.04956,21.66843],[99.98654,21.71064],[99.94003,21.82782],[99.99084,21.97053],[99.96612,22.05965],[99.85351,22.04183],[99.47585,22.13345],[99.33166,22.09656],[99.1552,22.15874],[99.19176,22.16983],[99.17318,22.18025],[99.28771,22.4105],[99.37972,22.50188],[99.38247,22.57544],[99.31243,22.73893],[99.45654,22.85726],[99.43537,22.94086],[99.54218,22.90014],[99.52214,23.08218],[99.34127,23.13099],[99.25741,23.09025],[99.04601,23.12215],[99.05975,23.16382],[98.88597,23.18656],[98.92515,23.29535],[98.93958,23.31414],[98.87573,23.33038],[98.92104,23.36946],[98.87683,23.48995],[98.82877,23.47908],[98.80294,23.5345],[98.88396,23.59555],[98.81775,23.694],[98.82933,23.72921],[98.79607,23.77947],[98.68209,23.80492],[98.67797,23.9644],[98.89632,24.10612],[98.87998,24.15624],[98.85319,24.13042],[98.59256,24.08371],[98.54476,24.13119],[98.20666,24.11406],[98.07806,24.07988],[98.06703,24.08028],[98.0607,24.07812],[98.05671,24.07961],[98.05302,24.07408],[98.04709,24.07616],[97.99583,24.04932],[97.98691,24.03897],[97.93951,24.01953],[97.90998,24.02094],[97.88616,24.00463],[97.88414,23.99405],[97.88814,23.98605],[97.89683,23.98389],[97.89676,23.97931],[97.8955,23.97758],[97.88811,23.97446],[97.86545,23.97723],[97.84328,23.97603],[97.79416,23.95663],[97.79456,23.94836],[97.72302,23.89288],[97.64667,23.84574],[97.5247,23.94032],[97.62363,24.00506],[97.72903,24.12606],[97.75305,24.16902],[97.72799,24.18883],[97.72998,24.2302],[97.76799,24.26365],[97.71941,24.29652],[97.66723,24.30027],[97.65624,24.33781],[97.7098,24.35658],[97.66998,24.45288],[97.60029,24.4401],[97.52757,24.43748],[97.56286,24.54535],[97.56525,24.72838],[97.54675,24.74202],[97.5542,24.74943],[97.56383,24.75535],[97.56648,24.76475],[97.64354,24.79171],[97.70181,24.84557],[97.73127,24.83015],[97.76481,24.8289],[97.79949,24.85655],[97.72903,24.91332],[97.72216,25.08508],[97.77023,25.11492],[97.83614,25.2715],[97.92541,25.20815],[98.14925,25.41547],[98.12591,25.50722],[98.18084,25.56298],[98.16848,25.62739],[98.25774,25.6051],[98.31268,25.55307],[98.40606,25.61129],[98.54064,25.85129],[98.63128,25.79937],[98.70818,25.86241],[98.60763,26.01512],[98.57085,26.11547],[98.63128,26.15492],[98.66884,26.09165],[98.7329,26.17218],[98.67797,26.24487],[98.72741,26.36183],[98.77547,26.61994],[98.7333,26.85615],[98.69582,27.56499],[98.43353,27.67086],[98.42529,27.55404],[98.32641,27.51385],[98.13964,27.9478],[98.15337,28.12114],[97.90069,28.3776],[97.79632,28.33168],[97.70705,28.5056],[97.56835,28.55628],[97.50518,28.49716],[97.47085,28.2688],[97.41729,28.29783],[97.34547,28.21385],[97.31292,28.06784],[97.35412,28.06663],[97.38845,28.01329],[97.35824,27.87256],[97.29919,27.92233],[96.90112,27.62149],[96.91431,27.45752],[97.17422,27.14052],[97.14675,27.09041],[96.89132,27.17474],[96.85287,27.2065],[96.88445,27.25046],[96.73888,27.36638],[96.55761,27.29928],[96.40779,27.29818],[96.15591,27.24572],[96.04949,27.19428],[95.93002,27.04149],[95.81603,27.01335],[95.437,26.7083],[95.30339,26.65372],[95.23513,26.68499],[95.05798,26.45408],[95.12801,26.38397],[95.11428,26.1019],[95.18556,26.07338],[94.80117,25.49359],[94.68032,25.47003],[94.57458,25.20318],[94.74212,25.13606],[94.73937,25.00545],[94.60204,24.70889],[94.5526,24.70764],[94.50729,24.59281],[94.45279,24.56656],[94.32362,24.27692],[94.30215,24.23752],[94.14081,23.83333],[93.92089,23.95812],[93.80279,23.92549],[93.75952,24.0003],[93.62871,24.00922],[93.50616,23.94432],[93.46633,23.97067],[93.41415,24.07854],[93.34735,24.10151],[93.32351,24.04468],[93.36059,23.93176],[93.3908,23.92925],[93.3908,23.7622],[93.43475,23.68299],[93.38805,23.4728],[93.39981,23.38828],[93.38781,23.36139],[93.36862,23.35426],[93.38478,23.13698],[93.2878,23.00464],[93.12988,23.05772],[93.134,22.92498],[93.09417,22.69459],[93.134,22.59573],[93.11477,22.54374],[93.13537,22.45873],[93.18206,22.43716],[93.19991,22.25425],[93.14224,22.24535],[93.15734,22.18687],[93.04885,22.20595],[92.99255,22.05965],[92.99804,21.98964],[92.93899,22.02656],[92.89504,21.95143],[92.86208,22.05456],[92.70416,22.16017],[92.67532,22.03547],[92.60949,21.97638],[92.62187,21.87037]]]]}},{type:"Feature",properties:{iso1A2:"MN",iso1A3:"MNG",iso1N3:"496",wikidata:"Q711",nameEn:"Mongolia",groups:["030","142"],callingCodes:["976"]},geometry:{type:"MultiPolygon",coordinates:[[[[102.14032,51.35566],[101.5044,51.50467],[101.39085,51.45753],[100.61116,51.73028],[99.89203,51.74903],[99.75578,51.90108],[99.27888,51.96876],[98.87768,52.14563],[98.74142,51.8637],[98.33222,51.71832],[98.22053,51.46579],[98.05257,51.46696],[97.83305,51.00248],[98.01472,50.86652],[97.9693,50.78044],[98.06393,50.61262],[98.31373,50.4996],[98.29481,50.33561],[97.85197,49.91339],[97.76871,49.99861],[97.56432,49.92801],[97.56811,49.84265],[97.24639,49.74737],[96.97388,49.88413],[95.80056,50.04239],[95.74757,49.97915],[95.02465,49.96941],[94.97166,50.04725],[94.6121,50.04239],[94.49477,50.17832],[94.39258,50.22193],[94.30823,50.57498],[92.99595,50.63183],[93.01109,50.79001],[92.44714,50.78762],[92.07173,50.69585],[91.86048,50.73734],[89.59711,49.90851],[89.70687,49.72535],[88.82499,49.44808],[88.42449,49.48821],[88.17223,49.46934],[88.15543,49.30314],[87.98977,49.18147],[87.81333,49.17354],[87.88171,48.95853],[87.73822,48.89582],[88.0788,48.71436],[87.96361,48.58478],[88.58939,48.34531],[88.58316,48.21893],[88.8011,48.11302],[88.93186,48.10263],[89.0711,47.98528],[89.55453,48.0423],[89.76624,47.82745],[90.06512,47.88177],[90.10871,47.7375],[90.33598,47.68303],[90.48854,47.41826],[90.48542,47.30438],[90.76108,46.99399],[90.84035,46.99525],[91.03649,46.72916],[91.0147,46.58171],[91.07696,46.57315],[90.89639,46.30711],[90.99672,46.14207],[91.03026,46.04194],[90.70907,45.73437],[90.65114,45.49314],[90.89169,45.19667],[91.64048,45.07408],[93.51161,44.95964],[94.10003,44.71016],[94.71959,44.35284],[95.01191,44.25274],[95.39772,44.2805],[95.32891,44.02407],[95.52594,43.99353],[95.89543,43.2528],[96.35658,42.90363],[96.37926,42.72055],[97.1777,42.7964],[99.50671,42.56535],[100.33297,42.68231],[100.84979,42.67087],[101.28833,42.58524],[101.80515,42.50074],[102.07645,42.22519],[102.42826,42.15137],[102.72403,42.14675],[103.3685,41.89696],[103.92804,41.78246],[104.52258,41.8706],[104.51667,41.66113],[104.91272,41.64619],[105.01119,41.58382],[105.24708,41.7442],[106.76517,42.28741],[107.24774,42.36107],[107.29755,42.41395],[107.49681,42.46221],[107.57258,42.40898],[108.23156,42.45532],[108.84489,42.40246],[109.00679,42.45302],[109.452,42.44842],[109.89402,42.63111],[110.08401,42.6411],[110.4327,42.78293],[111.0149,43.3289],[111.59087,43.51207],[111.79758,43.6637],[111.93776,43.68709],[111.96289,43.81596],[111.40498,44.3461],[111.76275,44.98032],[111.98695,45.09074],[112.4164,45.06858],[112.74662,44.86297],[113.63821,44.74326],[113.909,44.91444],[114.08071,44.92847],[114.5166,45.27189],[114.54801,45.38337],[114.74612,45.43585],[114.94546,45.37377],[115.35757,45.39106],[115.69688,45.45761],[115.91898,45.6227],[116.16989,45.68603],[116.27366,45.78637],[116.24012,45.8778],[116.26678,45.96479],[116.58612,46.30211],[116.75551,46.33083],[116.83166,46.38637],[117.07252,46.35818],[117.36609,46.36335],[117.41782,46.57862],[117.60748,46.59771],[117.69554,46.50991],[118.30534,46.73519],[118.78747,46.68689],[118.8337,46.77742],[118.89974,46.77139],[118.92616,46.72765],[119.00541,46.74273],[119.10448,46.65516],[119.24978,46.64761],[119.30261,46.6083],[119.37306,46.61132],[119.42827,46.63783],[119.65265,46.62342],[119.68127,46.59015],[119.77373,46.62947],[119.80455,46.67631],[119.89261,46.66423],[119.91242,46.90091],[119.85518,46.92196],[119.71209,47.19192],[119.62403,47.24575],[119.56019,47.24874],[119.54918,47.29505],[119.31964,47.42617],[119.35892,47.48104],[119.13995,47.53997],[119.12343,47.66458],[118.7564,47.76947],[118.55766,47.99277],[118.29654,48.00246],[118.22677,48.03853],[118.11009,48.04],[118.03676,48.00982],[117.80196,48.01661],[117.50181,47.77216],[117.37875,47.63627],[117.08918,47.82242],[116.87527,47.88836],[116.67405,47.89039],[116.4465,47.83662],[116.2527,47.87766],[116.08431,47.80693],[115.94296,47.67741],[115.57128,47.91988],[115.52082,48.15367],[115.811,48.25699],[115.78876,48.51781],[116.06565,48.81716],[116.03781,48.87014],[116.71193,49.83813],[116.62502,49.92919],[116.22402,50.04477],[115.73602,49.87688],[115.26068,49.97367],[114.9703,50.19254],[114.325,50.28098],[113.20216,49.83356],[113.02647,49.60772],[110.64493,49.1816],[110.39891,49.25083],[110.24373,49.16676],[109.51325,49.22859],[109.18017,49.34709],[108.53969,49.32325],[108.27937,49.53167],[107.95387,49.66659],[107.96116,49.93191],[107.36407,49.97612],[107.1174,50.04239],[107.00007,50.1977],[106.80326,50.30177],[106.58373,50.34044],[106.51122,50.34408],[106.49628,50.32436],[106.47156,50.31909],[106.07865,50.33474],[106.05562,50.40582],[105.32528,50.4648],[103.70343,50.13952],[102.71178,50.38873],[102.32194,50.67982],[102.14032,51.35566]]]]}},{type:"Feature",properties:{iso1A2:"MO",iso1A3:"MAC",iso1N3:"446",wikidata:"Q14773",nameEn:"Macau",aliases:["Macao"],country:"CN",groups:["030","142"],driveSide:"left",callingCodes:["853"]},geometry:{type:"MultiPolygon",coordinates:[[[[113.54942,22.14519],[113.54839,22.10909],[113.57191,22.07696],[113.63011,22.10782],[113.60504,22.20464],[113.57123,22.20416],[113.56865,22.20973],[113.5508,22.21672],[113.54333,22.21688],[113.54093,22.21314],[113.53593,22.2137],[113.53301,22.21235],[113.53552,22.20607],[113.52659,22.18271],[113.54093,22.15497],[113.54942,22.14519]]]]}},{type:"Feature",properties:{iso1A2:"MP",iso1A3:"MNP",iso1N3:"580",wikidata:"Q16644",nameEn:"Northern Mariana Islands",country:"US",groups:["057","009"],roadSpeedUnit:"mph",callingCodes:["1 670"]},geometry:{type:"MultiPolygon",coordinates:[[[[143.82485,13.92273],[146.25931,13.85876],[146.6755,21.00809],[144.18594,21.03576],[143.82485,13.92273]]]]}},{type:"Feature",properties:{iso1A2:"MQ",iso1A3:"MTQ",iso1N3:"474",wikidata:"Q17054",nameEn:"Martinique",country:"FR",groups:["EU","029","003","419","019"],callingCodes:["596"]},geometry:{type:"MultiPolygon",coordinates:[[[[-60.5958,14.23076],[-60.69955,15.22234],[-61.51867,14.96709],[-61.26561,14.25664],[-60.5958,14.23076]]]]}},{type:"Feature",properties:{iso1A2:"MR",iso1A3:"MRT",iso1N3:"478",wikidata:"Q1025",nameEn:"Mauritania",groups:["011","202","002"],callingCodes:["222"]},geometry:{type:"MultiPolygon",coordinates:[[[[-5.60725,16.49919],[-6.57191,25.0002],[-4.83423,24.99935],[-8.66674,27.31569],[-8.66721,25.99918],[-12.0002,25.9986],[-12.00251,23.4538],[-12.14969,23.41935],[-12.36213,23.3187],[-12.5741,23.28975],[-13.00412,23.02297],[-13.10753,22.89493],[-13.15313,22.75649],[-13.08438,22.53866],[-13.01525,21.33343],[-16.95474,21.33997],[-16.99806,21.12142],[-17.0357,21.05368],[-17.0396,20.9961],[-17.06781,20.92697],[-17.0695,20.85742],[-17.0471,20.76408],[-17.15288,16.07139],[-16.50854,16.09032],[-16.48967,16.0496],[-16.44814,16.09753],[-16.4429,16.20605],[-16.27016,16.51565],[-15.6509,16.50315],[-15.00557,16.64997],[-14.32144,16.61495],[-13.80075,16.13961],[-13.43135,16.09022],[-13.11029,15.52116],[-12.23936,14.76324],[-11.94903,14.76143],[-11.70705,15.51558],[-11.43483,15.62339],[-10.90932,15.11001],[-10.71721,15.4223],[-9.40447,15.4396],[-9.44673,15.60553],[-9.33314,15.7044],[-9.31106,15.69412],[-9.32979,15.50032],[-5.50165,15.50061],[-5.33435,16.33354],[-5.60725,16.49919]]]]}},{type:"Feature",properties:{iso1A2:"MS",iso1A3:"MSR",iso1N3:"500",wikidata:"Q13353",nameEn:"Montserrat",country:"GB",groups:["029","003","419","019"],driveSide:"left",callingCodes:["1 664"]},geometry:{type:"MultiPolygon",coordinates:[[[[-61.83929,16.66647],[-62.14123,17.02632],[-62.52079,16.69392],[-62.17275,16.35721],[-61.83929,16.66647]]]]}},{type:"Feature",properties:{iso1A2:"MT",iso1A3:"MLT",iso1N3:"470",wikidata:"Q233",nameEn:"Malta",groups:["EU","039","150"],driveSide:"left",callingCodes:["356"]},geometry:{type:"MultiPolygon",coordinates:[[[[15.70991,35.79901],[14.07544,36.41525],[13.27636,35.20764],[15.70991,35.79901]]]]}},{type:"Feature",properties:{iso1A2:"MU",iso1A3:"MUS",iso1N3:"480",wikidata:"Q1027",nameEn:"Mauritius",groups:["014","202","002"],driveSide:"left",callingCodes:["230"]},geometry:{type:"MultiPolygon",coordinates:[[[[56.73473,-21.9174],[64.11105,-21.5783],[63.47388,-9.1938],[56.09755,-9.55401],[56.73473,-21.9174]]]]}},{type:"Feature",properties:{iso1A2:"MV",iso1A3:"MDV",iso1N3:"462",wikidata:"Q826",nameEn:"Maldives",groups:["034","142"],driveSide:"left",callingCodes:["960"]},geometry:{type:"MultiPolygon",coordinates:[[[[71.27292,7.36038],[73.37814,-3.88401],[74.6203,7.39289],[71.27292,7.36038]]]]}},{type:"Feature",properties:{iso1A2:"MW",iso1A3:"MWI",iso1N3:"454",wikidata:"Q1020",nameEn:"Malawi",groups:["014","202","002"],driveSide:"left",callingCodes:["265"]},geometry:{type:"MultiPolygon",coordinates:[[[[33.48052,-9.62442],[33.31581,-9.48554],[33.14925,-9.49322],[32.99397,-9.36712],[32.95389,-9.40138],[33.00476,-9.5133],[33.00256,-9.63053],[33.05485,-9.61316],[33.10163,-9.66525],[33.12144,-9.58929],[33.2095,-9.61099],[33.31517,-9.82364],[33.36581,-9.81063],[33.37902,-9.9104],[33.31297,-10.05133],[33.53863,-10.20148],[33.54797,-10.36077],[33.70675,-10.56896],[33.47636,-10.78465],[33.28022,-10.84428],[33.25998,-10.88862],[33.39697,-11.15296],[33.29267,-11.3789],[33.29267,-11.43536],[33.23663,-11.40637],[33.24252,-11.59302],[33.32692,-11.59248],[33.33937,-11.91252],[33.25998,-12.14242],[33.3705,-12.34931],[33.47636,-12.32498],[33.54485,-12.35996],[33.37517,-12.54085],[33.28177,-12.54692],[33.18837,-12.61377],[33.05917,-12.59554],[32.94397,-12.76868],[32.96733,-12.88251],[33.02181,-12.88707],[32.98289,-13.12671],[33.0078,-13.19492],[32.86113,-13.47292],[32.84176,-13.52794],[32.73683,-13.57682],[32.68436,-13.55769],[32.66468,-13.60019],[32.68654,-13.64268],[32.7828,-13.64805],[32.84528,-13.71576],[32.76962,-13.77224],[32.79015,-13.80755],[32.88985,-13.82956],[32.99042,-13.95689],[33.02977,-14.05022],[33.07568,-13.98447],[33.16749,-13.93992],[33.24249,-14.00019],[33.66677,-14.61306],[33.7247,-14.4989],[33.88503,-14.51652],[33.92898,-14.47929],[34.08588,-14.48893],[34.18733,-14.43823],[34.22355,-14.43607],[34.34453,-14.3985],[34.35843,-14.38652],[34.39277,-14.39467],[34.4192,-14.43191],[34.44641,-14.47746],[34.45053,-14.49873],[34.47628,-14.53363],[34.48932,-14.53646],[34.49636,-14.55091],[34.52366,-14.5667],[34.53962,-14.59776],[34.55112,-14.64494],[34.53516,-14.67782],[34.52057,-14.68263],[34.54503,-14.74672],[34.567,-14.77345],[34.61522,-14.99583],[34.57503,-15.30619],[34.43126,-15.44778],[34.44981,-15.60864],[34.25195,-15.90321],[34.43126,-16.04737],[34.40344,-16.20923],[35.04805,-16.83167],[35.13771,-16.81687],[35.17017,-16.93521],[35.04805,-17.00027],[35.0923,-17.13235],[35.3062,-17.1244],[35.27065,-16.93817],[35.30929,-16.82871],[35.27219,-16.69402],[35.14235,-16.56812],[35.25828,-16.4792],[35.30157,-16.2211],[35.43355,-16.11371],[35.52365,-16.15414],[35.70107,-16.10147],[35.80487,-16.03907],[35.85303,-15.41913],[35.78799,-15.17428],[35.91812,-14.89514],[35.87212,-14.89478],[35.86945,-14.67481],[35.5299,-14.27714],[35.47989,-14.15594],[34.86229,-13.48958],[34.60253,-13.48487],[34.37831,-12.17408],[34.46088,-12.0174],[34.70739,-12.15652],[34.82903,-12.04837],[34.57917,-11.87849],[34.64241,-11.57499],[34.96296,-11.57354],[34.91153,-11.39799],[34.79375,-11.32245],[34.63305,-11.11731],[34.61161,-11.01611],[34.67047,-10.93796],[34.65946,-10.6828],[34.57581,-10.56271],[34.51911,-10.12279],[34.54499,-10.0678],[34.03865,-9.49398],[33.95829,-9.54066],[33.9638,-9.62206],[33.93298,-9.71647],[33.76677,-9.58516],[33.48052,-9.62442]]]]}},{type:"Feature",properties:{iso1A2:"MX",iso1A3:"MEX",iso1N3:"484",wikidata:"Q96",nameEn:"Mexico",groups:["013","003","419","019"],callingCodes:["52"]},geometry:{type:"MultiPolygon",coordinates:[[[[-117.1243,32.53427],[-118.48109,32.5991],[-120.12904,18.41089],[-92.37213,14.39277],[-92.2261,14.53423],[-92.1454,14.6804],[-92.18161,14.84147],[-92.1423,14.88647],[-92.1454,14.98143],[-92.0621,15.07406],[-92.20983,15.26077],[-91.73182,16.07371],[-90.44567,16.07573],[-90.40499,16.40524],[-90.61212,16.49832],[-90.69064,16.70697],[-91.04436,16.92175],[-91.43809,17.25373],[-90.99199,17.25192],[-90.98678,17.81655],[-89.14985,17.81563],[-89.15105,17.95104],[-89.03839,18.0067],[-88.8716,17.89535],[-88.71505,18.0707],[-88.48242,18.49164],[-88.3268,18.49048],[-88.29909,18.47591],[-88.26593,18.47617],[-88.03238,18.41778],[-88.03165,18.16657],[-87.90671,18.15213],[-87.87604,18.18313],[-87.86657,18.19971],[-87.85693,18.18266],[-87.84815,18.18511],[-86.92368,17.61462],[-85.9092,21.8218],[-96.92418,25.97377],[-97.13927,25.96583],[-97.35946,25.92189],[-97.37332,25.83854],[-97.42511,25.83969],[-97.45669,25.86874],[-97.49828,25.89877],[-97.52025,25.88518],[-97.66511,26.01708],[-97.95155,26.0625],[-97.97017,26.05232],[-98.24603,26.07191],[-98.27075,26.09457],[-98.30491,26.10475],[-98.35126,26.15129],[-99.00546,26.3925],[-99.03053,26.41249],[-99.08477,26.39849],[-99.53573,27.30926],[-99.49744,27.43746],[-99.482,27.47128],[-99.48045,27.49016],[-99.50208,27.50021],[-99.52955,27.49747],[-99.51478,27.55836],[-99.55409,27.61314],[-100.50029,28.66117],[-100.51222,28.70679],[-100.5075,28.74066],[-100.52313,28.75598],[-100.59809,28.88197],[-100.63689,28.90812],[-100.67294,29.09744],[-100.79696,29.24688],[-100.87982,29.296],[-100.94056,29.33371],[-100.94579,29.34523],[-100.96725,29.3477],[-101.01128,29.36947],[-101.05686,29.44738],[-101.47277,29.7744],[-102.60596,29.8192],[-103.15787,28.93865],[-104.37752,29.54255],[-104.39363,29.55396],[-104.3969,29.57105],[-104.5171,29.64671],[-104.77674,30.4236],[-106.00363,31.39181],[-106.09025,31.40569],[-106.20346,31.46305],[-106.23711,31.51262],[-106.24612,31.54193],[-106.28084,31.56173],[-106.30305,31.62154],[-106.33419,31.66303],[-106.34864,31.69663],[-106.3718,31.71165],[-106.38003,31.73151],[-106.41773,31.75196],[-106.43419,31.75478],[-106.45244,31.76523],[-106.46726,31.75998],[-106.47298,31.75054],[-106.48815,31.74769],[-106.50111,31.75714],[-106.50962,31.76155],[-106.51251,31.76922],[-106.52266,31.77509],[-106.529,31.784],[-108.20899,31.78534],[-108.20979,31.33316],[-109.05235,31.3333],[-111.07523,31.33232],[-112.34553,31.7357],[-114.82011,32.49609],[-114.79524,32.55731],[-114.81141,32.55543],[-114.80584,32.62028],[-114.76736,32.64094],[-114.71871,32.71894],[-115.88053,32.63624],[-117.1243,32.53427]]]]}},{type:"Feature",properties:{iso1A2:"MY",iso1A3:"MYS",iso1N3:"458",wikidata:"Q833",nameEn:"Malaysia",groups:["035","142"],driveSide:"left",callingCodes:["60"]},geometry:{type:"MultiPolygon",coordinates:[[[[114.08532,4.64632],[109.55486,8.10026],[104.81582,8.03101],[102.46318,7.22462],[102.09086,6.23546],[102.08127,6.22679],[102.07732,6.193],[102.09182,6.14161],[102.01835,6.05407],[101.99209,6.04075],[101.97114,6.01992],[101.9714,6.00575],[101.94712,5.98421],[101.92819,5.85511],[101.91776,5.84269],[101.89188,5.8386],[101.80144,5.74505],[101.75074,5.79091],[101.69773,5.75881],[101.58019,5.93534],[101.25524,5.78633],[101.25755,5.71065],[101.14062,5.61613],[100.98815,5.79464],[101.02708,5.91013],[101.087,5.9193],[101.12388,6.11411],[101.06165,6.14161],[101.12618,6.19431],[101.10313,6.25617],[100.85884,6.24929],[100.81045,6.45086],[100.74822,6.46231],[100.74361,6.50811],[100.66986,6.45086],[100.43027,6.52389],[100.42351,6.51762],[100.41791,6.5189],[100.41152,6.52299],[100.35413,6.54932],[100.31929,6.65413],[100.32607,6.65933],[100.32671,6.66526],[100.31884,6.66423],[100.31618,6.66781],[100.30828,6.66462],[100.29651,6.68439],[100.19511,6.72559],[100.12,6.42105],[100.0756,6.4045],[99.91873,6.50233],[99.50117,6.44501],[99.31854,5.99868],[99.75778,3.86466],[103.03657,1.30383],[103.56591,1.19719],[103.62738,1.35255],[103.67468,1.43166],[103.7219,1.46108],[103.74161,1.4502],[103.76395,1.45183],[103.81181,1.47953],[103.86383,1.46288],[103.89565,1.42841],[103.93384,1.42926],[104.00131,1.42405],[104.02277,1.4438],[104.04622,1.44691],[104.07348,1.43322],[104.08871,1.42015],[104.09162,1.39694],[104.08072,1.35998],[104.12282,1.27714],[104.34728,1.33529],[104.56723,1.44271],[105.01437,3.24936],[108.10426,5.42408],[109.71058,2.32059],[109.64506,2.08014],[109.62558,1.99182],[109.53794,1.91771],[109.57923,1.80624],[109.66397,1.79972],[109.66397,1.60425],[110.35354,0.98869],[110.49182,0.88088],[110.62374,0.873],[111.22979,1.08326],[111.55434,0.97864],[111.82846,0.99349],[111.94553,1.12016],[112.15679,1.17004],[112.2127,1.44135],[112.48648,1.56516],[113.021,1.57819],[113.01448,1.42832],[113.64677,1.23933],[114.03788,1.44787],[114.57892,1.5],[114.80706,1.92351],[114.80706,2.21665],[115.1721,2.49671],[115.11343,2.82879],[115.53713,3.14776],[115.58276,3.93499],[115.90217,4.37708],[117.25801,4.35108],[117.47313,4.18857],[117.67641,4.16535],[117.89538,4.16637],[118.07935,4.15511],[118.8663,4.44172],[118.75416,4.59798],[119.44841,5.09568],[119.34756,5.53889],[117.89159,6.25755],[117.43832,7.3895],[117.17735,7.52841],[116.79524,7.43869],[115.02521,5.35005],[115.16236,5.01011],[115.15092,4.87604],[115.20737,4.8256],[115.27819,4.63661],[115.2851,4.42295],[115.36346,4.33563],[115.31275,4.30806],[115.09978,4.39123],[115.07737,4.53418],[115.04064,4.63706],[115.02278,4.74137],[115.02955,4.82087],[115.05038,4.90275],[114.99417,4.88201],[114.96982,4.81146],[114.88841,4.81905],[114.8266,4.75062],[114.77303,4.72871],[114.83189,4.42387],[114.88039,4.4257],[114.78539,4.12205],[114.64211,4.00694],[114.49922,4.13108],[114.4416,4.27588],[114.32176,4.2552],[114.32176,4.34942],[114.26876,4.49878],[114.15813,4.57],[114.07448,4.58441],[114.08532,4.64632]]]]}},{type:"Feature",properties:{iso1A2:"MZ",iso1A3:"MOZ",iso1N3:"508",wikidata:"Q1029",nameEn:"Mozambique",groups:["014","202","002"],driveSide:"left",callingCodes:["258"]},geometry:{type:"MultiPolygon",coordinates:[[[[40.74206,-10.25691],[40.44265,-10.4618],[40.00295,-10.80255],[39.58249,-10.96043],[39.24395,-11.17433],[38.88996,-11.16978],[38.47258,-11.4199],[38.21598,-11.27289],[37.93618,-11.26228],[37.8388,-11.3123],[37.76614,-11.53352],[37.3936,-11.68949],[36.80309,-11.56836],[36.62068,-11.72884],[36.19094,-11.70008],[36.19094,-11.57593],[35.82767,-11.41081],[35.63599,-11.55927],[34.96296,-11.57354],[34.64241,-11.57499],[34.57917,-11.87849],[34.82903,-12.04837],[34.70739,-12.15652],[34.46088,-12.0174],[34.37831,-12.17408],[34.60253,-13.48487],[34.86229,-13.48958],[35.47989,-14.15594],[35.5299,-14.27714],[35.86945,-14.67481],[35.87212,-14.89478],[35.91812,-14.89514],[35.78799,-15.17428],[35.85303,-15.41913],[35.80487,-16.03907],[35.70107,-16.10147],[35.52365,-16.15414],[35.43355,-16.11371],[35.30157,-16.2211],[35.25828,-16.4792],[35.14235,-16.56812],[35.27219,-16.69402],[35.30929,-16.82871],[35.27065,-16.93817],[35.3062,-17.1244],[35.0923,-17.13235],[35.04805,-17.00027],[35.17017,-16.93521],[35.13771,-16.81687],[35.04805,-16.83167],[34.40344,-16.20923],[34.43126,-16.04737],[34.25195,-15.90321],[34.44981,-15.60864],[34.43126,-15.44778],[34.57503,-15.30619],[34.61522,-14.99583],[34.567,-14.77345],[34.54503,-14.74672],[34.52057,-14.68263],[34.53516,-14.67782],[34.55112,-14.64494],[34.53962,-14.59776],[34.52366,-14.5667],[34.49636,-14.55091],[34.48932,-14.53646],[34.47628,-14.53363],[34.45053,-14.49873],[34.44641,-14.47746],[34.4192,-14.43191],[34.39277,-14.39467],[34.35843,-14.38652],[34.34453,-14.3985],[34.22355,-14.43607],[34.18733,-14.43823],[34.08588,-14.48893],[33.92898,-14.47929],[33.88503,-14.51652],[33.7247,-14.4989],[33.66677,-14.61306],[33.24249,-14.00019],[30.22098,-14.99447],[30.41902,-15.62269],[30.42568,-15.9962],[30.91597,-15.99924],[30.97761,-16.05848],[31.13171,-15.98019],[31.30563,-16.01193],[31.42451,-16.15154],[31.67988,-16.19595],[31.90223,-16.34388],[31.91324,-16.41569],[32.02772,-16.43892],[32.28529,-16.43892],[32.42838,-16.4727],[32.71017,-16.59932],[32.69917,-16.66893],[32.78943,-16.70267],[32.97655,-16.70689],[32.91051,-16.89446],[32.84113,-16.92259],[32.96554,-17.11971],[33.00517,-17.30477],[33.0426,-17.3468],[32.96554,-17.48964],[32.98536,-17.55891],[33.0492,-17.60298],[32.94133,-17.99705],[33.03159,-18.35054],[33.02278,-18.4696],[32.88629,-18.51344],[32.88629,-18.58023],[32.95013,-18.69079],[32.9017,-18.7992],[32.82465,-18.77419],[32.70137,-18.84712],[32.73439,-18.92628],[32.69917,-18.94293],[32.72118,-19.02204],[32.84006,-19.0262],[32.87088,-19.09279],[32.85107,-19.29238],[32.77966,-19.36098],[32.78282,-19.47513],[32.84446,-19.48343],[32.84666,-19.68462],[32.95013,-19.67219],[33.06461,-19.77787],[33.01178,-20.02007],[32.93032,-20.03868],[32.85987,-20.16686],[32.85987,-20.27841],[32.66174,-20.56106],[32.55167,-20.56312],[32.48122,-20.63319],[32.51644,-20.91929],[32.37115,-21.133],[32.48236,-21.32873],[32.41234,-21.31246],[31.38336,-22.36919],[31.30611,-22.422],[31.55779,-23.176],[31.56539,-23.47268],[31.67942,-23.60858],[31.70223,-23.72695],[31.77445,-23.90082],[31.87707,-23.95293],[31.90368,-24.18892],[31.9835,-24.29983],[32.03196,-25.10785],[32.01676,-25.38117],[31.97875,-25.46356],[32.00631,-25.65044],[31.92649,-25.84216],[31.974,-25.95387],[32.00916,-25.999],[32.08599,-26.00978],[32.10435,-26.15656],[32.07352,-26.40185],[32.13409,-26.5317],[32.13315,-26.84345],[32.19409,-26.84032],[32.22302,-26.84136],[32.29584,-26.852],[32.35222,-26.86027],[34.51034,-26.91792],[42.99868,-12.65261],[40.74206,-10.25691]]]]}},{type:"Feature",properties:{iso1A2:"NA",iso1A3:"NAM",iso1N3:"516",wikidata:"Q1030",nameEn:"Namibia",groups:["018","202","002"],driveSide:"left",callingCodes:["264"]},geometry:{type:"MultiPolygon",coordinates:[[[[14.28743,-17.38814],[13.95896,-17.43141],[13.36212,-16.98048],[12.97145,-16.98567],[12.52111,-17.24495],[12.07076,-17.15165],[11.75063,-17.25013],[10.5065,-17.25284],[12.51595,-32.27486],[16.45332,-28.63117],[16.46592,-28.57126],[16.59922,-28.53246],[16.90446,-28.057],[17.15405,-28.08573],[17.4579,-28.68718],[18.99885,-28.89165],[19.99882,-28.42622],[19.99817,-24.76768],[19.99912,-21.99991],[20.99751,-22.00026],[20.99904,-18.31743],[21.45556,-18.31795],[23.0996,-18.00075],[23.29618,-17.99855],[23.61088,-18.4881],[24.19416,-18.01919],[24.40577,-17.95726],[24.57485,-18.07151],[24.6303,-17.9863],[24.71887,-17.9218],[24.73364,-17.89338],[24.95586,-17.79674],[25.05895,-17.84452],[25.16882,-17.78253],[25.26433,-17.79571],[25.00198,-17.58221],[24.70864,-17.49501],[24.5621,-17.52963],[24.38712,-17.46818],[24.32811,-17.49082],[24.23619,-17.47489],[23.47474,-17.62877],[21.42741,-18.02787],[21.14283,-17.94318],[18.84226,-17.80375],[18.39229,-17.38927],[14.28743,-17.38814]]]]}},{type:"Feature",properties:{iso1A2:"NC",iso1A3:"NCL",iso1N3:"540",wikidata:"Q33788",nameEn:"New Caledonia",country:"FR",groups:["054","009"],callingCodes:["687"]},geometry:{type:"MultiPolygon",coordinates:[[[[158.65519,-23.4036],[174.90025,-23.53966],[162.93363,-17.28904],[157.83842,-18.82563],[158.65519,-23.4036]]]]}},{type:"Feature",properties:{iso1A2:"NE",iso1A3:"NER",iso1N3:"562",wikidata:"Q1032",nameEn:"Niger",aliases:["RN"],groups:["011","202","002"],callingCodes:["227"]},geometry:{type:"MultiPolygon",coordinates:[[[[14.22918,22.61719],[13.5631,23.16574],[11.96886,23.51735],[7.48273,20.87258],[7.38361,20.79165],[5.8153,19.45101],[4.26651,19.14224],[4.26762,17.00432],[4.21787,17.00118],[4.19893,16.39923],[3.50368,15.35934],[3.03134,15.42221],[3.01806,15.34571],[1.31275,15.27978],[0.96711,14.98275],[0.72632,14.95898],[0.23859,15.00135],[0.16936,14.51654],[0.38051,14.05575],[0.61924,13.68491],[0.77377,13.6866],[0.77637,13.64442],[0.99514,13.5668],[1.02813,13.46635],[1.20088,13.38951],[1.24429,13.39373],[1.28509,13.35488],[1.24516,13.33968],[1.21217,13.37853],[1.18873,13.31771],[0.99253,13.37515],[0.99167,13.10727],[2.26349,12.41915],[2.05785,12.35539],[2.39723,11.89473],[2.45824,11.98672],[2.39657,12.10952],[2.37783,12.24804],[2.6593,12.30631],[2.83978,12.40585],[3.25352,12.01467],[3.31613,11.88495],[3.48187,11.86092],[3.59375,11.70269],[3.61075,11.69181],[3.67988,11.75429],[3.67122,11.80865],[3.63063,11.83042],[3.61955,11.91847],[3.67775,11.97599],[3.63136,12.11826],[3.66364,12.25884],[3.65111,12.52223],[3.94339,12.74979],[4.10006,12.98862],[4.14367,13.17189],[4.14186,13.47586],[4.23456,13.47725],[4.4668,13.68286],[4.87425,13.78],[4.9368,13.7345],[5.07396,13.75052],[5.21026,13.73627],[5.27797,13.75474],[5.35437,13.83567],[5.52957,13.8845],[6.15771,13.64564],[6.27411,13.67835],[6.43053,13.6006],[6.69617,13.34057],[6.94445,12.99825],[7.0521,13.00076],[7.12676,13.02445],[7.22399,13.1293],[7.39241,13.09717],[7.81085,13.34902],[8.07997,13.30847],[8.25185,13.20369],[8.41853,13.06166],[8.49493,13.07519],[8.60431,13.01768],[8.64251,12.93985],[8.97413,12.83661],[9.65995,12.80614],[10.00373,13.18171],[10.19993,13.27129],[10.46731,13.28819],[10.66004,13.36422],[11.4535,13.37773],[11.88236,13.2527],[12.04209,13.14452],[12.16189,13.10056],[12.19315,13.12423],[12.47095,13.06673],[12.58033,13.27805],[12.6793,13.29157],[12.87376,13.48919],[13.05085,13.53984],[13.19844,13.52802],[13.33213,13.71195],[13.6302,13.71094],[13.47559,14.40881],[13.48259,14.46704],[13.68573,14.55276],[13.67878,14.64013],[13.809,14.72915],[13.78991,14.87519],[13.86301,15.04043],[14.37425,15.72591],[15.50373,16.89649],[15.6032,18.77402],[15.75098,19.93002],[15.99632,20.35364],[15.6721,20.70069],[15.59841,20.74039],[15.56004,20.79488],[15.55382,20.86507],[15.57248,20.92138],[15.62515,20.95395],[15.28332,21.44557],[15.20213,21.49365],[15.19692,21.99339],[14.99751,23.00539],[14.22918,22.61719]]]]}},{type:"Feature",properties:{iso1A2:"NF",iso1A3:"NFK",iso1N3:"574",wikidata:"Q31057",nameEn:"Norfolk Island",country:"AU",groups:["053","009"],driveSide:"left",callingCodes:["672 3"]},geometry:{type:"MultiPolygon",coordinates:[[[[169.82316,-28.16667],[166.29505,-28.29175],[167.94076,-30.60745],[169.82316,-28.16667]]]]}},{type:"Feature",properties:{iso1A2:"NG",iso1A3:"NGA",iso1N3:"566",wikidata:"Q1033",nameEn:"Nigeria",groups:["011","202","002"],callingCodes:["234"]},geometry:{type:"MultiPolygon",coordinates:[[[[6.15771,13.64564],[5.52957,13.8845],[5.35437,13.83567],[5.27797,13.75474],[5.21026,13.73627],[5.07396,13.75052],[4.9368,13.7345],[4.87425,13.78],[4.4668,13.68286],[4.23456,13.47725],[4.14186,13.47586],[4.14367,13.17189],[4.10006,12.98862],[3.94339,12.74979],[3.65111,12.52223],[3.66364,12.25884],[3.63136,12.11826],[3.67775,11.97599],[3.61955,11.91847],[3.63063,11.83042],[3.67122,11.80865],[3.67988,11.75429],[3.61075,11.69181],[3.59375,11.70269],[3.49175,11.29765],[3.71505,11.13015],[3.84243,10.59316],[3.78292,10.40538],[3.6844,10.46351],[3.57275,10.27185],[3.66908,10.18136],[3.54429,9.87739],[3.35383,9.83641],[3.32099,9.78032],[3.34726,9.70696],[3.25093,9.61632],[3.13928,9.47167],[3.14147,9.28375],[3.08017,9.10006],[2.77907,9.06924],[2.67523,7.87825],[2.73095,7.7755],[2.73405,7.5423],[2.78668,7.5116],[2.79442,7.43486],[2.74489,7.42565],[2.76965,7.13543],[2.71702,6.95722],[2.74024,6.92802],[2.73405,6.78508],[2.78823,6.76356],[2.78204,6.70514],[2.7325,6.64057],[2.74334,6.57291],[2.70464,6.50831],[2.70566,6.38038],[2.74181,6.13349],[5.87055,3.78489],[8.34397,4.30689],[8.60302,4.87353],[8.78027,5.1243],[8.92029,5.58403],[8.83687,5.68483],[8.88156,5.78857],[8.84209,5.82562],[9.51757,6.43874],[9.70674,6.51717],[9.77824,6.79088],[9.86314,6.77756],[10.15135,7.03781],[10.21466,6.88996],[10.53639,6.93432],[10.57214,7.16345],[10.59746,7.14719],[10.60789,7.06885],[10.83727,6.9358],[10.8179,6.83377],[10.94302,6.69325],[11.09644,6.68437],[11.09495,6.51717],[11.42041,6.53789],[11.42264,6.5882],[11.51499,6.60892],[11.57755,6.74059],[11.55818,6.86186],[11.63117,6.9905],[11.87396,7.09398],[11.84864,7.26098],[11.93205,7.47812],[12.01844,7.52981],[11.99908,7.67302],[12.20909,7.97553],[12.19271,8.10826],[12.24782,8.17904],[12.26123,8.43696],[12.4489,8.52536],[12.44146,8.6152],[12.68722,8.65938],[12.71701,8.7595],[12.79,8.75361],[12.81085,8.91992],[12.90022,9.11411],[12.91958,9.33905],[12.85628,9.36698],[13.02385,9.49334],[13.22642,9.57266],[13.25472,9.76795],[13.29941,9.8296],[13.25025,9.86042],[13.24132,9.91031],[13.27409,9.93232],[13.286,9.9822],[13.25323,10.00127],[13.25025,10.03647],[13.34111,10.12299],[13.43644,10.13326],[13.5705,10.53183],[13.54964,10.61236],[13.73434,10.9255],[13.70753,10.94451],[13.7403,11.00593],[13.78945,11.00154],[13.97489,11.30258],[14.17821,11.23831],[14.6124,11.51283],[14.64591,11.66166],[14.55207,11.72001],[14.61612,11.7798],[14.6474,12.17466],[14.4843,12.35223],[14.22215,12.36533],[14.17523,12.41916],[14.20204,12.53405],[14.08251,13.0797],[13.6302,13.71094],[13.33213,13.71195],[13.19844,13.52802],[13.05085,13.53984],[12.87376,13.48919],[12.6793,13.29157],[12.58033,13.27805],[12.47095,13.06673],[12.19315,13.12423],[12.16189,13.10056],[12.04209,13.14452],[11.88236,13.2527],[11.4535,13.37773],[10.66004,13.36422],[10.46731,13.28819],[10.19993,13.27129],[10.00373,13.18171],[9.65995,12.80614],[8.97413,12.83661],[8.64251,12.93985],[8.60431,13.01768],[8.49493,13.07519],[8.41853,13.06166],[8.25185,13.20369],[8.07997,13.30847],[7.81085,13.34902],[7.39241,13.09717],[7.22399,13.1293],[7.12676,13.02445],[7.0521,13.00076],[6.94445,12.99825],[6.69617,13.34057],[6.43053,13.6006],[6.27411,13.67835],[6.15771,13.64564]]]]}},{type:"Feature",properties:{iso1A2:"NI",iso1A3:"NIC",iso1N3:"558",wikidata:"Q811",nameEn:"Nicaragua",groups:["013","003","419","019"],callingCodes:["505"]},geometry:{type:"MultiPolygon",coordinates:[[[[-83.13724,15.00002],[-83.49268,15.01158],[-83.62101,14.89448],[-83.89551,14.76697],[-84.10584,14.76353],[-84.48373,14.63249],[-84.70119,14.68078],[-84.82596,14.82212],[-84.90082,14.80489],[-85.1575,14.53934],[-85.18602,14.24929],[-85.32149,14.2562],[-85.45762,14.11304],[-85.73964,13.9698],[-85.75477,13.8499],[-86.03458,13.99181],[-86.00685,14.08474],[-86.14801,14.04317],[-86.35219,13.77157],[-86.76812,13.79605],[-86.71267,13.30348],[-86.87066,13.30641],[-86.93383,13.18677],[-86.93197,13.05313],[-87.03785,12.98682],[-87.06306,13.00892],[-87.37107,12.98646],[-87.55124,13.12523],[-87.7346,13.13228],[-88.11443,12.63306],[-86.14524,11.09059],[-85.71223,11.06868],[-85.60529,11.22607],[-84.92439,10.9497],[-84.68197,11.07568],[-83.90838,10.71161],[-83.66597,10.79916],[-83.68276,11.01562],[-82.56142,11.91792],[-82.06974,14.49418],[-83.04763,15.03256],[-83.13724,15.00002]]]]}},{type:"Feature",properties:{iso1A2:"NL",iso1A3:"NLD",iso1N3:"528",wikidata:"Q55",nameEn:"Netherlands",groups:["EU","155","150"],callingCodes:["31"]},geometry:{type:"MultiPolygon",coordinates:[[[[5.45168,54.20039],[2.56575,51.85301],[3.36263,51.37112],[3.38696,51.33436],[3.35847,51.31572],[3.38289,51.27331],[3.41704,51.25933],[3.43488,51.24135],[3.52698,51.2458],[3.51502,51.28697],[3.58939,51.30064],[3.78999,51.25766],[3.78783,51.2151],[3.90125,51.20371],[3.97889,51.22537],[4.01957,51.24504],[4.05165,51.24171],[4.16721,51.29348],[4.24024,51.35371],[4.21923,51.37443],[4.33265,51.37687],[4.34086,51.35738],[4.39292,51.35547],[4.43777,51.36989],[4.38064,51.41965],[4.39747,51.43316],[4.38122,51.44905],[4.47736,51.4778],[4.5388,51.48184],[4.54675,51.47265],[4.52846,51.45002],[4.53521,51.4243],[4.57489,51.4324],[4.65442,51.42352],[4.72935,51.48424],[4.74578,51.48937],[4.77321,51.50529],[4.78803,51.50284],[4.84139,51.4799],[4.82409,51.44736],[4.82946,51.4213],[4.78314,51.43319],[4.76577,51.43046],[4.77229,51.41337],[4.78941,51.41102],[4.84988,51.41502],[4.90016,51.41404],[4.92152,51.39487],[5.00393,51.44406],[5.0106,51.47167],[5.03281,51.48679],[5.04774,51.47022],[5.07891,51.4715],[5.10456,51.43163],[5.07102,51.39469],[5.13105,51.34791],[5.13377,51.31592],[5.16222,51.31035],[5.2002,51.32243],[5.24244,51.30495],[5.22542,51.26888],[5.23814,51.26064],[5.26461,51.26693],[5.29716,51.26104],[5.33886,51.26314],[5.347,51.27502],[5.41672,51.26248],[5.4407,51.28169],[5.46519,51.2849],[5.48476,51.30053],[5.515,51.29462],[5.5569,51.26544],[5.5603,51.22249],[5.65145,51.19788],[5.65528,51.18736],[5.70344,51.1829],[5.74617,51.18928],[5.77735,51.17845],[5.77697,51.1522],[5.82564,51.16753],[5.85508,51.14445],[5.80798,51.11661],[5.8109,51.10861],[5.83226,51.10585],[5.82921,51.09328],[5.79903,51.09371],[5.79835,51.05834],[5.77258,51.06196],[5.75961,51.03113],[5.77688,51.02483],[5.76242,50.99703],[5.71864,50.96092],[5.72875,50.95428],[5.74752,50.96202],[5.75927,50.95601],[5.74644,50.94723],[5.72545,50.92312],[5.72644,50.91167],[5.71626,50.90796],[5.69858,50.91046],[5.67886,50.88142],[5.64504,50.87107],[5.64009,50.84742],[5.65259,50.82309],[5.70118,50.80764],[5.68995,50.79641],[5.70107,50.7827],[5.68091,50.75804],[5.69469,50.75529],[5.72216,50.76398],[5.73904,50.75674],[5.74356,50.7691],[5.76533,50.78159],[5.77513,50.78308],[5.80673,50.7558],[5.84548,50.76542],[5.84888,50.75448],[5.88734,50.77092],[5.89129,50.75125],[5.89132,50.75124],[5.95942,50.7622],[5.97545,50.75441],[6.01976,50.75398],[6.02624,50.77453],[5.97497,50.79992],[5.98404,50.80988],[6.00462,50.80065],[6.02328,50.81694],[6.01921,50.84435],[6.05623,50.8572],[6.05702,50.85179],[6.07431,50.84674],[6.07693,50.86025],[6.08805,50.87223],[6.07486,50.89307],[6.09297,50.92066],[6.01615,50.93367],[6.02697,50.98303],[5.95282,50.98728],[5.90296,50.97356],[5.90493,51.00198],[5.87849,51.01969],[5.86735,51.05182],[5.9134,51.06736],[5.9541,51.03496],[5.98292,51.07469],[6.16706,51.15677],[6.17384,51.19589],[6.07889,51.17038],[6.07889,51.24432],[6.16977,51.33169],[6.22674,51.36135],[6.22641,51.39948],[6.20654,51.40049],[6.21724,51.48568],[6.18017,51.54096],[6.09055,51.60564],[6.11759,51.65609],[6.02767,51.6742],[6.04091,51.71821],[5.95003,51.7493],[5.98665,51.76944],[5.94568,51.82786],[5.99848,51.83195],[6.06705,51.86136],[6.10337,51.84829],[6.16902,51.84094],[6.11551,51.89769],[6.15349,51.90439],[6.21443,51.86801],[6.29872,51.86801],[6.30593,51.84998],[6.40704,51.82771],[6.38815,51.87257],[6.47179,51.85395],[6.50231,51.86313],[6.58556,51.89386],[6.68386,51.91861],[6.72319,51.89518],[6.82357,51.96711],[6.83035,51.9905],[6.68128,52.05052],[6.76117,52.11895],[6.83984,52.11728],[6.97189,52.20329],[6.9897,52.2271],[7.03729,52.22695],[7.06365,52.23789],[7.02703,52.27941],[7.07044,52.37805],[7.03417,52.40237],[6.99041,52.47235],[6.94293,52.43597],[6.69507,52.488],[6.71641,52.62905],[6.77307,52.65375],[7.04557,52.63318],[7.07253,52.81083],[7.21694,53.00742],[7.17898,53.13817],[7.22681,53.18165],[7.21679,53.20058],[7.19052,53.31866],[7.00198,53.32672],[6.91025,53.44221],[5.45168,54.20039]],[[4.93295,51.44945],[4.95244,51.45207],[4.9524,51.45014],[4.93909,51.44632],[4.93295,51.44945]],[[4.91493,51.4353],[4.91935,51.43634],[4.92227,51.44252],[4.91811,51.44621],[4.92287,51.44741],[4.92811,51.4437],[4.92566,51.44273],[4.92815,51.43856],[4.92879,51.44161],[4.93544,51.44634],[4.94025,51.44193],[4.93416,51.44185],[4.93471,51.43861],[4.94265,51.44003],[4.93986,51.43064],[4.92952,51.42984],[4.92652,51.43329],[4.91493,51.4353]]]]}},{type:"Feature",properties:{iso1A2:"NO",iso1A3:"NOR",iso1N3:"578",wikidata:"Q20",nameEn:"Norway",groups:["154","150"],callingCodes:["47"]},geometry:{type:"MultiPolygon",coordinates:[[[[10.40861,58.38489],[10.64958,58.89391],[11.08911,58.98745],[11.15367,59.07862],[11.34459,59.11672],[11.4601,58.99022],[11.45199,58.89604],[11.65732,58.90177],[11.8213,59.24985],[11.69297,59.59442],[11.92112,59.69531],[11.87121,59.86039],[12.15641,59.8926],[12.36317,59.99259],[12.52003,60.13846],[12.59133,60.50559],[12.2277,61.02442],[12.69115,61.06584],[12.86939,61.35427],[12.57707,61.56547],[12.40595,61.57226],[12.14746,61.7147],[12.29187,62.25699],[12.07085,62.6297],[12.19919,63.00104],[11.98529,63.27487],[12.19919,63.47935],[12.14928,63.59373],[12.74105,64.02171],[13.23411,64.09087],[13.98222,64.00953],[14.16051,64.18725],[14.11117,64.46674],[13.64276,64.58402],[14.50926,65.31786],[14.53778,66.12399],[15.05113,66.15572],[15.49318,66.28509],[15.37197,66.48217],[16.35589,67.06419],[16.39154,67.21653],[16.09922,67.4364],[16.12774,67.52106],[16.38441,67.52923],[16.7409,67.91037],[17.30416,68.11591],[17.90787,67.96537],[18.13836,68.20874],[18.1241,68.53721],[18.39503,68.58672],[18.63032,68.50849],[18.97255,68.52416],[19.93508,68.35911],[20.22027,68.48759],[19.95647,68.55546],[20.22027,68.67246],[20.33435,68.80174],[20.28444,68.93283],[20.0695,69.04469],[20.55258,69.06069],[20.72171,69.11874],[21.05775,69.0356],[21.11099,69.10291],[20.98641,69.18809],[21.00732,69.22755],[21.27827,69.31281],[21.63833,69.27485],[22.27276,68.89514],[22.38367,68.71561],[22.53321,68.74393],[23.13064,68.64684],[23.68017,68.70276],[23.781,68.84514],[24.02299,68.81601],[24.18432,68.73936],[24.74898,68.65143],[24.90023,68.55579],[24.93048,68.61102],[25.10189,68.63307],[25.12206,68.78684],[25.42455,68.90328],[25.61613,68.89602],[25.75729,68.99383],[25.69679,69.27039],[25.96904,69.68397],[26.40261,69.91377],[26.64461,69.96565],[27.05802,69.92069],[27.57226,70.06215],[27.95542,70.0965],[27.97558,69.99671],[28.32849,69.88605],[28.36883,69.81658],[29.12697,69.69193],[29.31664,69.47994],[28.8629,69.22395],[28.81248,69.11997],[28.91738,69.04774],[29.0444,69.0119],[29.26623,69.13794],[29.27631,69.2811],[29.97205,69.41623],[30.16363,69.65244],[30.52662,69.54699],[30.95011,69.54699],[30.84095,69.80584],[31.59909,70.16571],[32.07813,72.01005],[18.46509,71.28681],[-0.3751,61.32236],[7.28637,57.35913],[10.40861,58.38489]]]]}},{type:"Feature",properties:{iso1A2:"NP",iso1A3:"NPL",iso1N3:"524",wikidata:"Q837",nameEn:"Nepal",groups:["034","142"],driveSide:"left",callingCodes:["977"]},geometry:{type:"MultiPolygon",coordinates:[[[[88.13378,27.88015],[87.82681,27.95248],[87.72718,27.80938],[87.56996,27.84517],[87.11696,27.84104],[87.03757,27.94835],[86.75582,28.04182],[86.74181,28.10638],[86.56265,28.09569],[86.51609,27.96623],[86.42736,27.91122],[86.22966,27.9786],[86.18607,28.17364],[86.088,28.09264],[86.08333,28.02121],[86.12069,27.93047],[86.06309,27.90021],[85.94946,27.9401],[85.97813,27.99023],[85.90743,28.05144],[85.84672,28.18187],[85.74864,28.23126],[85.71907,28.38064],[85.69105,28.38475],[85.60854,28.25045],[85.59765,28.30529],[85.4233,28.32996],[85.38127,28.28336],[85.10729,28.34092],[85.18668,28.54076],[85.19135,28.62825],[85.06059,28.68562],[84.85511,28.58041],[84.62317,28.73887],[84.47528,28.74023],[84.2231,28.89571],[84.24801,29.02783],[84.18107,29.23451],[83.97559,29.33091],[83.82303,29.30513],[83.63156,29.16249],[83.44787,29.30513],[83.28131,29.56813],[83.07116,29.61957],[82.73024,29.81695],[82.5341,29.9735],[82.38622,30.02608],[82.16984,30.0692],[82.19475,30.16884],[82.10757,30.23745],[82.10135,30.35439],[81.99082,30.33423],[81.62033,30.44703],[81.41018,30.42153],[81.39928,30.21862],[81.33355,30.15303],[81.2623,30.14596],[81.29032,30.08806],[81.24362,30.0126],[81.12842,30.01395],[81.03953,30.20059],[80.93695,30.18229],[80.8778,30.13384],[80.67076,29.95732],[80.60226,29.95732],[80.56957,29.88176],[80.56247,29.86661],[80.48997,29.79566],[80.43458,29.80466],[80.41554,29.79451],[80.36803,29.73865],[80.38428,29.68513],[80.41858,29.63581],[80.37939,29.57098],[80.24322,29.44299],[80.31428,29.30784],[80.28626,29.20327],[80.24112,29.21414],[80.26602,29.13938],[80.23178,29.11626],[80.18085,29.13649],[80.05743,28.91479],[80.06957,28.82763],[80.12125,28.82346],[80.37188,28.63371],[80.44504,28.63098],[80.52443,28.54897],[80.50575,28.6706],[80.55142,28.69182],[80.89648,28.47237],[81.08507,28.38346],[81.19847,28.36284],[81.32923,28.13521],[81.38683,28.17638],[81.48179,28.12148],[81.47867,28.08303],[81.91223,27.84995],[81.97214,27.93322],[82.06554,27.92222],[82.46405,27.6716],[82.70378,27.72122],[82.74119,27.49838],[82.93261,27.50328],[82.94938,27.46036],[83.19413,27.45632],[83.27197,27.38309],[83.2673,27.36235],[83.29999,27.32778],[83.35136,27.33885],[83.38872,27.39276],[83.39495,27.4798],[83.61288,27.47013],[83.85595,27.35797],[83.86182,27.4241],[83.93306,27.44939],[84.02229,27.43836],[84.10791,27.52399],[84.21376,27.45218],[84.25735,27.44941],[84.29315,27.39],[84.62161,27.33885],[84.69166,27.21294],[84.64496,27.04669],[84.793,26.9968],[84.82913,27.01989],[84.85754,26.98984],[84.96687,26.95599],[84.97186,26.9149],[85.00536,26.89523],[85.05592,26.88991],[85.02635,26.85381],[85.15883,26.86966],[85.19291,26.86909],[85.18046,26.80519],[85.21159,26.75933],[85.34302,26.74954],[85.47752,26.79292],[85.56471,26.84133],[85.5757,26.85955],[85.59461,26.85161],[85.61621,26.86721],[85.66239,26.84822],[85.73483,26.79613],[85.72315,26.67471],[85.76907,26.63076],[85.83126,26.61134],[85.85126,26.60866],[85.8492,26.56667],[86.02729,26.66756],[86.13596,26.60651],[86.22513,26.58863],[86.26235,26.61886],[86.31564,26.61925],[86.49726,26.54218],[86.54258,26.53819],[86.57073,26.49825],[86.61313,26.48658],[86.62686,26.46891],[86.69124,26.45169],[86.74025,26.42386],[86.76797,26.45892],[86.82898,26.43919],[86.94543,26.52076],[86.95912,26.52076],[87.01559,26.53228],[87.04691,26.58685],[87.0707,26.58571],[87.09147,26.45039],[87.14751,26.40542],[87.18863,26.40558],[87.24682,26.4143],[87.26587,26.40592],[87.26568,26.37294],[87.34568,26.34787],[87.37314,26.40815],[87.46566,26.44058],[87.51571,26.43106],[87.55274,26.40596],[87.59175,26.38342],[87.66803,26.40294],[87.67893,26.43501],[87.76004,26.40711],[87.7918,26.46737],[87.84193,26.43663],[87.89085,26.48565],[87.90115,26.44923],[88.00895,26.36029],[88.09414,26.43732],[88.09963,26.54195],[88.16452,26.64111],[88.1659,26.68177],[88.19107,26.75516],[88.12302,26.95324],[88.13422,26.98705],[88.11719,26.98758],[87.9887,27.11045],[88.01587,27.21388],[88.01646,27.21612],[88.07277,27.43007],[88.04008,27.49223],[88.19107,27.79285],[88.1973,27.85067],[88.13378,27.88015]]]]}},{type:"Feature",properties:{iso1A2:"NR",iso1A3:"NRU",iso1N3:"520",wikidata:"Q697",nameEn:"Nauru",groups:["057","009"],driveSide:"left",callingCodes:["674"]},geometry:{type:"MultiPolygon",coordinates:[[[[166.95155,0.14829],[166.21778,-0.7977],[167.60042,-0.88259],[166.95155,0.14829]]]]}},{type:"Feature",properties:{iso1A2:"NU",iso1A3:"NIU",iso1N3:"570",wikidata:"Q34020",nameEn:"Niue",country:"NZ",groups:["061","009"],driveSide:"left",callingCodes:["683"]},geometry:{type:"MultiPolygon",coordinates:[[[[-173.13438,-14.94228],[-173.11048,-23.23027],[-167.73129,-23.22266],[-167.73854,-14.92809],[-171.14262,-14.93704],[-173.13438,-14.94228]]]]}},{type:"Feature",properties:{iso1A2:"NZ",iso1A3:"NZL",iso1N3:"554",wikidata:"Q664",nameEn:"New Zealand",groups:["053","009"],driveSide:"left",callingCodes:["64"]},geometry:{type:"MultiPolygon",coordinates:[[[[-180,-24.21376],[-179.93224,-45.18423],[-155.99562,-45.16785],[-180,-24.21376]]],[[[161.96603,-56.07661],[179.49541,-50.04657],[179.49541,-36.79303],[169.6687,-29.09191],[161.96603,-56.07661]]]]}},{type:"Feature",properties:{iso1A2:"OM",iso1A3:"OMN",iso1N3:"512",wikidata:"Q842",nameEn:"Oman",groups:["145","142"],callingCodes:["968"]},geometry:{type:"MultiPolygon",coordinates:[[[[56.82555,25.7713],[56.79239,26.41236],[56.68954,26.76645],[56.2644,26.58649],[55.81777,26.18798],[56.08666,26.05038],[56.15498,26.06828],[56.19334,25.9795],[56.13963,25.82765],[56.17416,25.77239],[56.13579,25.73524],[56.14826,25.66351],[56.18363,25.65508],[56.20473,25.61119],[56.25365,25.60211],[56.26636,25.60643],[56.25341,25.61443],[56.26534,25.62825],[56.82555,25.7713]]],[[[56.26062,25.33108],[56.23362,25.31253],[56.25008,25.28843],[56.24465,25.27505],[56.20838,25.25668],[56.20872,25.24104],[56.24341,25.22867],[56.27628,25.23404],[56.34438,25.26653],[56.35172,25.30681],[56.3111,25.30107],[56.3005,25.31815],[56.26062,25.33108]],[[56.28423,25.26344],[56.27086,25.26128],[56.2716,25.27916],[56.28102,25.28486],[56.29379,25.2754],[56.28423,25.26344]]],[[[61.45114,22.55394],[56.86325,25.03856],[56.3227,24.97284],[56.34873,24.93205],[56.30269,24.88334],[56.20568,24.85063],[56.20062,24.78565],[56.13684,24.73699],[56.06128,24.74457],[56.03535,24.81161],[55.97836,24.87673],[55.97467,24.89639],[56.05106,24.87461],[56.05715,24.95727],[55.96316,25.00857],[55.90849,24.96771],[55.85094,24.96858],[55.81116,24.9116],[55.81348,24.80102],[55.83408,24.77858],[55.83271,24.68567],[55.76461,24.5287],[55.83271,24.41521],[55.83395,24.32776],[55.80747,24.31069],[55.79145,24.27914],[55.76781,24.26209],[55.75939,24.26114],[55.75382,24.2466],[55.75257,24.23466],[55.76558,24.23227],[55.77658,24.23476],[55.83367,24.20193],[55.95472,24.2172],[56.01799,24.07426],[55.8308,24.01633],[55.73301,24.05994],[55.48677,23.94946],[55.57358,23.669],[55.22634,23.10378],[55.2137,22.71065],[55.66469,21.99658],[54.99756,20.00083],[52.00311,19.00083],[52.78009,17.35124],[52.74267,17.29519],[52.81185,17.28568],[53.09917,16.67084],[53.32998,16.16312],[56.66759,17.24021],[61.45114,22.55394]]]]}},{type:"Feature",properties:{iso1A2:"PA",iso1A3:"PAN",iso1N3:"591",wikidata:"Q804",nameEn:"Panama",groups:["013","003","419","019"],callingCodes:["507"]},geometry:{type:"MultiPolygon",coordinates:[[[[-77.32389,8.81247],[-77.58292,9.22278],[-78.79327,9.93766],[-82.51044,9.65379],[-82.56507,9.57279],[-82.61345,9.49881],[-82.66667,9.49746],[-82.77206,9.59573],[-82.87919,9.62645],[-82.84871,9.4973],[-82.93516,9.46741],[-82.93516,9.07687],[-82.72126,8.97125],[-82.88253,8.83331],[-82.91377,8.774],[-82.92068,8.74832],[-82.8794,8.6981],[-82.82739,8.60153],[-82.83975,8.54755],[-82.83322,8.52464],[-82.8382,8.48117],[-82.8679,8.44042],[-82.93056,8.43465],[-83.05209,8.33394],[-82.9388,8.26634],[-82.88641,8.10219],[-82.89137,8.05755],[-82.89978,8.04083],[-82.94503,7.93865],[-82.13751,6.97312],[-78.06168,7.07793],[-77.89178,7.22681],[-77.81426,7.48319],[-77.72157,7.47612],[-77.72514,7.72348],[-77.57185,7.51147],[-77.17257,7.97422],[-77.45064,8.49991],[-77.32389,8.81247]]]]}},{type:"Feature",properties:{iso1A2:"PE",iso1A3:"PER",iso1N3:"604",wikidata:"Q419",nameEn:"Peru",groups:["005","419","019"],callingCodes:["51"]},geometry:{type:"MultiPolygon",coordinates:[[[[-74.26675,-0.97229],[-74.42701,-0.50218],[-75.18513,-0.0308],[-75.25764,-0.11943],[-75.40192,-0.17196],[-75.61997,-0.10012],[-75.60169,-0.18708],[-75.53615,-0.19213],[-75.22862,-0.60048],[-75.22862,-0.95588],[-75.3872,-0.9374],[-75.57429,-1.55961],[-76.05203,-2.12179],[-76.6324,-2.58397],[-77.94147,-3.05454],[-78.19369,-3.36431],[-78.14324,-3.47653],[-78.22642,-3.51113],[-78.24589,-3.39907],[-78.34362,-3.38633],[-78.68394,-4.60754],[-78.85149,-4.66795],[-79.01659,-5.01481],[-79.1162,-4.97774],[-79.26248,-4.95167],[-79.59402,-4.46848],[-79.79722,-4.47558],[-80.13945,-4.29786],[-80.39256,-4.48269],[-80.46386,-4.41516],[-80.32114,-4.21323],[-80.45023,-4.20938],[-80.4822,-4.05477],[-80.46386,-4.01342],[-80.13232,-3.90317],[-80.19926,-3.68894],[-80.18741,-3.63994],[-80.19848,-3.59249],[-80.21642,-3.5888],[-80.20535,-3.51667],[-80.22629,-3.501],[-80.23651,-3.48652],[-80.24586,-3.48677],[-80.24475,-3.47846],[-80.24123,-3.46124],[-80.20647,-3.431],[-80.30602,-3.39149],[-84.52388,-3.36941],[-85.71054,-21.15413],[-70.59118,-18.35072],[-70.378,-18.3495],[-70.31267,-18.31258],[-70.16394,-18.31737],[-69.96732,-18.25992],[-69.81607,-18.12582],[-69.75305,-17.94605],[-69.82868,-17.72048],[-69.79087,-17.65563],[-69.66483,-17.65083],[-69.46897,-17.4988],[-69.46863,-17.37466],[-69.62883,-17.28142],[-69.16896,-16.72233],[-69.00853,-16.66769],[-69.04027,-16.57214],[-68.98358,-16.42165],[-68.79464,-16.33272],[-68.96238,-16.194],[-69.09986,-16.22693],[-69.20291,-16.16668],[-69.40336,-15.61358],[-69.14856,-15.23478],[-69.36254,-14.94634],[-68.88135,-14.18639],[-69.05265,-13.68546],[-68.8864,-13.40792],[-68.85615,-12.87769],[-68.65044,-12.50689],[-68.98115,-11.8979],[-69.57156,-10.94555],[-69.57835,-10.94051],[-69.90896,-10.92744],[-70.38791,-11.07096],[-70.51395,-10.92249],[-70.64134,-11.0108],[-70.62487,-9.80666],[-70.55429,-9.76692],[-70.58453,-9.58303],[-70.53373,-9.42628],[-71.23394,-9.9668],[-72.14742,-9.98049],[-72.31883,-9.5184],[-72.72216,-9.41397],[-73.21498,-9.40904],[-72.92886,-9.04074],[-73.76576,-7.89884],[-73.65485,-7.77897],[-73.96938,-7.58465],[-73.77011,-7.28944],[-73.73986,-6.87919],[-73.12983,-6.43852],[-73.24579,-6.05764],[-72.83973,-5.14765],[-72.64391,-5.0391],[-71.87003,-4.51661],[-70.96814,-4.36915],[-70.77601,-4.15717],[-70.33236,-4.15214],[-70.19582,-4.3607],[-70.11305,-4.27281],[-70.00888,-4.37833],[-69.94708,-4.2431],[-70.3374,-3.79505],[-70.52393,-3.87553],[-70.71396,-3.7921],[-70.04609,-2.73906],[-70.94377,-2.23142],[-71.75223,-2.15058],[-72.92587,-2.44514],[-73.65312,-1.26222],[-74.26675,-0.97229]]]]}},{type:"Feature",properties:{iso1A2:"PF",iso1A3:"PYF",iso1N3:"258",wikidata:"Q30971",nameEn:"French Polynesia",country:"FR",groups:["061","009"],callingCodes:["689"]},geometry:{type:"MultiPolygon",coordinates:[[[[-149.6249,-7.51261],[-149.61166,-12.30171],[-156.4957,-12.32002],[-156.46451,-23.21255],[-156.44843,-28.52556],[-133.59543,-28.4709],[-133.61511,-21.93325],[-133.65593,-7.46952],[-149.6249,-7.51261]]]]}},{type:"Feature",properties:{iso1A2:"PG",iso1A3:"PNG",iso1N3:"598",wikidata:"Q691",nameEn:"Papua New Guinea",groups:["054","009"],driveSide:"left",callingCodes:["675"]},geometry:{type:"MultiPolygon",coordinates:[[[[141.03157,2.12829],[140.99813,-6.3233],[140.85295,-6.72996],[141.01763,-6.90181],[141.00782,-9.1242],[140.88922,-9.34945],[142.0601,-9.56571],[142.0953,-9.23534],[142.1462,-9.19923],[142.23304,-9.19253],[142.31447,-9.24611],[142.5723,-9.35994],[142.81927,-9.31709],[144.30183,-9.48146],[155.22803,-12.9001],[154.74815,-7.33315],[155.60735,-6.92266],[155.69784,-6.92661],[155.92557,-6.84664],[156.03993,-6.65703],[156.03296,-6.55528],[160.43769,-4.17974],[141.03157,2.12829]]]]}},{type:"Feature",properties:{iso1A2:"PH",iso1A3:"PHL",iso1N3:"608",wikidata:"Q928",nameEn:"Philippines",aliases:["PI","RP"],groups:["035","142"],callingCodes:["63"]},geometry:{type:"MultiPolygon",coordinates:[[[[129.19694,7.84182],[121.8109,21.77688],[120.69238,21.52331],[118.82252,14.67191],[115.39742,10.92666],[116.79524,7.43869],[117.17735,7.52841],[117.43832,7.3895],[117.89159,6.25755],[119.34756,5.53889],[119.44841,5.09568],[118.75416,4.59798],[118.8663,4.44172],[118.07935,4.15511],[118.41402,3.99509],[124.97752,4.82064],[129.19694,7.84182]]]]}},{type:"Feature",properties:{iso1A2:"PK",iso1A3:"PAK",iso1N3:"586",wikidata:"Q843",nameEn:"Pakistan",groups:["034","142"],driveSide:"left",callingCodes:["92"]},geometry:{type:"MultiPolygon",coordinates:[[[[75.72737,36.7529],[75.45562,36.71971],[75.40481,36.95382],[75.13839,37.02622],[74.56453,37.03023],[74.53739,36.96224],[74.43389,37.00977],[74.04856,36.82648],[73.82685,36.91421],[72.6323,36.84601],[72.18135,36.71838],[71.80267,36.49924],[71.60491,36.39429],[71.19505,36.04134],[71.37969,35.95865],[71.55273,35.71483],[71.49917,35.6267],[71.65435,35.4479],[71.54294,35.31037],[71.5541,35.28776],[71.67495,35.21262],[71.52938,35.09023],[71.55273,35.02615],[71.49917,35.00478],[71.50329,34.97328],[71.29472,34.87728],[71.28356,34.80882],[71.08718,34.69034],[71.11602,34.63047],[71.0089,34.54568],[71.02401,34.44835],[71.17662,34.36769],[71.12815,34.26619],[71.13078,34.16503],[71.09453,34.13524],[71.09307,34.11961],[71.06933,34.10564],[71.07345,34.06242],[70.88119,33.97933],[70.54336,33.9463],[69.90203,34.04194],[69.87307,33.9689],[69.85671,33.93719],[70.00503,33.73528],[70.14236,33.71701],[70.14785,33.6553],[70.20141,33.64387],[70.17062,33.53535],[70.32775,33.34496],[70.13686,33.21064],[70.07369,33.22557],[70.02563,33.14282],[69.85259,33.09451],[69.79766,33.13247],[69.71526,33.09911],[69.57656,33.09911],[69.49004,33.01509],[69.49854,32.88843],[69.5436,32.8768],[69.47082,32.85834],[69.38018,32.76601],[69.43649,32.7302],[69.44747,32.6678],[69.38155,32.56601],[69.2868,32.53938],[69.23599,32.45946],[69.27932,32.29119],[69.27032,32.14141],[69.3225,31.93186],[69.20577,31.85957],[69.11514,31.70782],[69.00939,31.62249],[68.95995,31.64822],[68.91078,31.59687],[68.79997,31.61665],[68.6956,31.75687],[68.57475,31.83158],[68.44222,31.76446],[68.27605,31.75863],[68.25614,31.80357],[68.1655,31.82691],[68.00071,31.6564],[67.86887,31.63536],[67.72056,31.52304],[67.58323,31.52772],[67.62374,31.40473],[67.7748,31.4188],[67.78854,31.33203],[67.29964,31.19586],[67.03323,31.24519],[67.04147,31.31561],[66.83273,31.26867],[66.72561,31.20526],[66.68166,31.07597],[66.58175,30.97532],[66.42645,30.95309],[66.39194,30.9408],[66.28413,30.57001],[66.34869,30.404],[66.23609,30.06321],[66.36042,29.9583],[66.24175,29.85181],[65.04005,29.53957],[64.62116,29.58903],[64.19796,29.50407],[64.12966,29.39157],[63.5876,29.50456],[62.47751,29.40782],[60.87231,29.86514],[61.31508,29.38903],[61.53765,29.00507],[61.65978,28.77937],[61.93581,28.55284],[62.40259,28.42703],[62.59499,28.24842],[62.79412,28.28108],[62.7638,28.02992],[62.84905,27.47627],[62.79684,27.34381],[62.80604,27.22412],[63.19649,27.25674],[63.32283,27.14437],[63.25005,27.08692],[63.25005,26.84212],[63.18688,26.83844],[63.1889,26.65072],[62.77352,26.64099],[62.31484,26.528],[62.21304,26.26601],[62.05117,26.31647],[61.89391,26.26251],[61.83831,26.07249],[61.83968,25.7538],[61.683,25.66638],[61.6433,25.27541],[61.57592,25.0492],[61.5251,24.57287],[68.11329,23.53945],[68.20763,23.85849],[68.39339,23.96838],[68.74643,23.97027],[68.7416,24.31904],[68.90914,24.33156],[68.97781,24.26021],[69.07806,24.29777],[69.19341,24.25646],[69.29778,24.28712],[69.59579,24.29777],[69.73335,24.17007],[70.03428,24.172],[70.11712,24.30915],[70.5667,24.43787],[70.57906,24.27774],[70.71502,24.23517],[70.88393,24.27398],[70.85784,24.30903],[70.94985,24.3791],[71.04461,24.34657],[71.12838,24.42662],[71.00341,24.46038],[70.97594,24.60904],[71.09405,24.69017],[70.94002,24.92843],[70.89148,25.15064],[70.66695,25.39314],[70.67382,25.68186],[70.60378,25.71898],[70.53649,25.68928],[70.37444,25.67443],[70.2687,25.71156],[70.0985,25.93238],[70.08193,26.08094],[70.17532,26.24118],[70.17532,26.55362],[70.05584,26.60398],[69.88555,26.56836],[69.50904,26.74892],[69.58519,27.18109],[70.03136,27.56627],[70.12502,27.8057],[70.37307,28.01208],[70.60927,28.02178],[70.79054,27.68423],[71.89921,27.96035],[71.9244,28.11555],[72.20329,28.3869],[72.29495,28.66367],[72.40402,28.78283],[72.94272,29.02487],[73.01337,29.16422],[73.05886,29.1878],[73.28094,29.56646],[73.3962,29.94707],[73.58665,30.01848],[73.80299,30.06969],[73.97225,30.19829],[73.95736,30.28466],[73.88993,30.36305],[74.5616,31.04153],[74.67971,31.05479],[74.6852,31.12771],[74.60006,31.13711],[74.60281,31.10419],[74.56023,31.08303],[74.51629,31.13829],[74.53223,31.30321],[74.59773,31.4136],[74.64713,31.45605],[74.59319,31.50197],[74.61517,31.55698],[74.57498,31.60382],[74.47771,31.72227],[74.58907,31.87824],[74.79919,31.95983],[74.86236,32.04485],[74.9269,32.0658],[75.00793,32.03786],[75.25649,32.10187],[75.38046,32.26836],[75.28259,32.36556],[75.03265,32.49538],[74.97634,32.45367],[74.84725,32.49075],[74.68362,32.49298],[74.67431,32.56676],[74.65251,32.56416],[74.64424,32.60985],[74.69542,32.66792],[74.65345,32.71225],[74.7113,32.84219],[74.64675,32.82604],[74.6289,32.75561],[74.45312,32.77755],[74.41467,32.90563],[74.31227,32.92795],[74.34875,32.97823],[74.31854,33.02891],[74.17571,33.07495],[74.15374,33.13477],[74.02144,33.18908],[74.01366,33.25199],[74.08782,33.26232],[74.17983,33.3679],[74.18121,33.4745],[74.10115,33.56392],[74.03576,33.56718],[73.97367,33.64061],[73.98968,33.66155],[73.96423,33.73071],[74.00891,33.75437],[74.05898,33.82089],[74.14001,33.83002],[74.26086,33.92237],[74.25262,34.01577],[74.21554,34.03853],[73.91341,34.01235],[73.88732,34.05105],[73.90677,34.10504],[73.98208,34.2522],[73.90517,34.35317],[73.8475,34.32935],[73.74862,34.34183],[73.74999,34.3781],[73.88732,34.48911],[73.89419,34.54568],[73.93951,34.57169],[73.93401,34.63386],[73.96423,34.68244],[74.12897,34.70073],[74.31239,34.79626],[74.58083,34.77386],[74.6663,34.703],[75.01479,34.64629],[75.38009,34.55021],[75.75438,34.51827],[76.04614,34.67566],[76.15463,34.6429],[76.47186,34.78965],[76.67648,34.76371],[76.74377,34.84039],[76.74514,34.92488],[76.87193,34.96906],[76.99251,34.93349],[77.11796,35.05419],[76.93465,35.39866],[76.85088,35.39754],[76.75475,35.52617],[76.77323,35.66062],[76.50961,35.8908],[76.33453,35.84296],[76.14913,35.82848],[76.15325,35.9264],[75.93028,36.13136],[76.00906,36.17511],[76.0324,36.41198],[75.92391,36.56986],[75.72737,36.7529]]]]}},{type:"Feature",properties:{iso1A2:"PL",iso1A3:"POL",iso1N3:"616",wikidata:"Q36",nameEn:"Poland",groups:["EU","151","150"],callingCodes:["48"]},geometry:{type:"MultiPolygon",coordinates:[[[[18.57853,55.25302],[14.20811,54.12784],[14.22634,53.9291],[14.20647,53.91671],[14.18544,53.91258],[14.20823,53.90776],[14.21323,53.8664],[14.27249,53.74464],[14.26782,53.69866],[14.2836,53.67721],[14.27133,53.66613],[14.28477,53.65955],[14.2853,53.63392],[14.31904,53.61581],[14.30416,53.55499],[14.3273,53.50587],[14.35209,53.49506],[14.4215,53.27724],[14.44133,53.27427],[14.45125,53.26241],[14.40662,53.21098],[14.37853,53.20405],[14.36696,53.16444],[14.38679,53.13669],[14.35044,53.05829],[14.25954,53.00264],[14.14056,52.95786],[14.15873,52.87715],[14.12256,52.84311],[14.13806,52.82392],[14.22071,52.81175],[14.61073,52.59847],[14.6289,52.57136],[14.60081,52.53116],[14.63056,52.48993],[14.54423,52.42568],[14.55228,52.35264],[14.56378,52.33838],[14.58149,52.28007],[14.70139,52.25038],[14.71319,52.22144],[14.68344,52.19612],[14.70616,52.16927],[14.67683,52.13936],[14.6917,52.10283],[14.72971,52.09167],[14.76026,52.06624],[14.71339,52.00337],[14.70488,51.97679],[14.7139,51.95643],[14.71836,51.95606],[14.72163,51.95188],[14.7177,51.94048],[14.70601,51.92944],[14.6933,51.9044],[14.6588,51.88359],[14.59089,51.83302],[14.60493,51.80473],[14.64625,51.79472],[14.66386,51.73282],[14.69065,51.70842],[14.75392,51.67445],[14.75759,51.62318],[14.7727,51.61263],[14.71125,51.56209],[14.73047,51.54606],[14.72652,51.53902],[14.73219,51.52922],[14.94749,51.47155],[14.9652,51.44793],[14.96899,51.38367],[14.98008,51.33449],[15.04288,51.28387],[15.01242,51.21285],[15.0047,51.16874],[14.99311,51.16249],[14.99414,51.15813],[15.00083,51.14974],[14.99646,51.14365],[14.99079,51.14284],[14.99689,51.12205],[14.98229,51.11354],[14.97938,51.07742],[14.95529,51.04552],[14.92942,50.99744],[14.89252,50.94999],[14.89681,50.9422],[14.81664,50.88148],[14.82803,50.86966],[14.99852,50.86817],[15.01088,50.97984],[14.96419,50.99108],[15.02433,51.0242],[15.03895,51.0123],[15.06218,51.02269],[15.10152,51.01095],[15.11937,50.99021],[15.16744,51.01959],[15.1743,50.9833],[15.2361,50.99886],[15.27043,50.97724],[15.2773,50.8907],[15.36656,50.83956],[15.3803,50.77187],[15.43798,50.80833],[15.73186,50.73885],[15.81683,50.75666],[15.87331,50.67188],[15.97219,50.69799],[16.0175,50.63009],[15.98317,50.61528],[16.02437,50.60046],[16.10265,50.66405],[16.20839,50.63096],[16.23174,50.67101],[16.33611,50.66579],[16.44597,50.58041],[16.34572,50.49575],[16.31413,50.50274],[16.19526,50.43291],[16.21585,50.40627],[16.22821,50.41054],[16.28118,50.36891],[16.30289,50.38292],[16.36495,50.37679],[16.3622,50.34875],[16.39379,50.3207],[16.42674,50.32509],[16.56407,50.21009],[16.55446,50.16613],[16.63137,50.1142],[16.7014,50.09659],[16.8456,50.20834],[16.98018,50.24172],[17.00353,50.21449],[17.02825,50.23118],[16.99803,50.25753],[17.02138,50.27772],[16.99803,50.30316],[16.94448,50.31281],[16.90877,50.38642],[16.85933,50.41093],[16.89229,50.45117],[17.1224,50.39494],[17.14498,50.38117],[17.19579,50.38817],[17.19991,50.3654],[17.27681,50.32246],[17.34273,50.32947],[17.34548,50.2628],[17.3702,50.28123],[17.58889,50.27837],[17.67764,50.28977],[17.69292,50.32859],[17.74648,50.29966],[17.72176,50.25665],[17.76296,50.23382],[17.70528,50.18812],[17.59404,50.16437],[17.66683,50.10275],[17.6888,50.12037],[17.7506,50.07896],[17.77669,50.02253],[17.86886,49.97452],[18.00191,50.01723],[18.04585,50.01194],[18.04585,50.03311],[18.00396,50.04954],[18.03212,50.06574],[18.07898,50.04535],[18.10628,50.00223],[18.20241,49.99958],[18.21752,49.97309],[18.27107,49.96779],[18.27794,49.93863],[18.31914,49.91565],[18.33278,49.92415],[18.33562,49.94747],[18.41604,49.93498],[18.53423,49.89906],[18.54495,49.9079],[18.54299,49.92537],[18.57697,49.91565],[18.57045,49.87849],[18.60341,49.86256],[18.57183,49.83334],[18.61278,49.7618],[18.61368,49.75426],[18.62645,49.75002],[18.62943,49.74603],[18.62676,49.71983],[18.69817,49.70473],[18.72838,49.68163],[18.80479,49.6815],[18.84786,49.5446],[18.84521,49.51672],[18.94536,49.52143],[18.97283,49.49914],[18.9742,49.39557],[19.18019,49.41165],[19.25435,49.53391],[19.36009,49.53747],[19.37795,49.574],[19.45348,49.61583],[19.52626,49.57311],[19.53313,49.52856],[19.57845,49.46077],[19.64162,49.45184],[19.6375,49.40897],[19.72127,49.39288],[19.78581,49.41701],[19.82237,49.27806],[19.75286,49.20751],[19.86409,49.19316],[19.90529,49.23532],[19.98494,49.22904],[20.08238,49.1813],[20.13738,49.31685],[20.21977,49.35265],[20.31453,49.34817],[20.31728,49.39914],[20.39939,49.3896],[20.46422,49.41612],[20.5631,49.375],[20.61666,49.41791],[20.72274,49.41813],[20.77971,49.35383],[20.9229,49.29626],[20.98733,49.30774],[21.09799,49.37176],[21.041,49.41791],[21.12477,49.43666],[21.19756,49.4054],[21.27858,49.45988],[21.43376,49.41433],[21.62328,49.4447],[21.77983,49.35443],[21.82927,49.39467],[21.96385,49.3437],[22.04427,49.22136],[22.56155,49.08865],[22.89122,49.00725],[22.86336,49.10513],[22.72009,49.20288],[22.748,49.32759],[22.69444,49.49378],[22.64534,49.53094],[22.78304,49.65543],[22.80261,49.69098],[22.83179,49.69875],[22.99329,49.84249],[23.28221,50.0957],[23.67635,50.33385],[23.71382,50.38248],[23.79445,50.40481],[23.99563,50.41289],[24.03668,50.44507],[24.07048,50.5071],[24.0996,50.60752],[24.0595,50.71625],[23.95925,50.79271],[23.99254,50.83847],[24.0952,50.83262],[24.14524,50.86128],[24.04576,50.90196],[23.92217,51.00836],[23.90376,51.07697],[23.80678,51.18405],[23.63858,51.32182],[23.69905,51.40871],[23.62751,51.50512],[23.56236,51.53673],[23.57053,51.55938],[23.53198,51.74298],[23.62691,51.78208],[23.61523,51.92066],[23.68733,51.9906],[23.64066,52.07626],[23.61,52.11264],[23.54314,52.12148],[23.47859,52.18215],[23.20071,52.22848],[23.18196,52.28812],[23.34141,52.44845],[23.45112,52.53774],[23.58296,52.59868],[23.73615,52.6149],[23.93763,52.71332],[23.91805,52.94016],[23.94689,52.95919],[23.92184,53.02079],[23.87548,53.0831],[23.91393,53.16469],[23.85657,53.22923],[23.81995,53.24131],[23.62004,53.60942],[23.51284,53.95052],[23.48261,53.98855],[23.52702,54.04622],[23.49196,54.14764],[23.45223,54.17775],[23.42418,54.17911],[23.39525,54.21672],[23.3494,54.25155],[23.24656,54.25701],[23.15938,54.29894],[23.15526,54.31076],[23.13905,54.31567],[23.104,54.29794],[23.04323,54.31567],[23.05726,54.34565],[22.99649,54.35927],[23.00584,54.38514],[22.83756,54.40827],[22.79705,54.36264],[21.41123,54.32395],[20.63871,54.3706],[19.8038,54.44203],[19.64312,54.45423],[18.57853,55.25302]]]]}},{type:"Feature",properties:{iso1A2:"PM",iso1A3:"SPM",iso1N3:"666",wikidata:"Q34617",nameEn:"Saint Pierre and Miquelon",country:"FR",groups:["021","003","019"],callingCodes:["508"]},geometry:{type:"MultiPolygon",coordinates:[[[[-56.72993,46.65575],[-55.90758,46.6223],[-56.27503,47.39728],[-56.72993,46.65575]]]]}},{type:"Feature",properties:{iso1A2:"PN",iso1A3:"PCN",iso1N3:"612",wikidata:"Q35672",nameEn:"Pitcairn Islands",country:"GB",groups:["061","009"],driveSide:"left",callingCodes:["64"]},geometry:{type:"MultiPolygon",coordinates:[[[[-133.59543,-28.4709],[-122.0366,-24.55017],[-133.61511,-21.93325],[-133.59543,-28.4709]]]]}},{type:"Feature",properties:{iso1A2:"PR",iso1A3:"PRI",iso1N3:"630",wikidata:"Q1183",nameEn:"Puerto Rico",country:"US",groups:["029","003","419","019"],roadSpeedUnit:"mph",callingCodes:["1 787","1 939"]},geometry:{type:"MultiPolygon",coordinates:[[[[-65.27974,17.56928],[-65.02435,18.73231],[-67.99519,18.97186],[-68.20301,17.83927],[-65.27974,17.56928]]]]}},{type:"Feature",properties:{iso1A2:"PS",iso1A3:"PSE",iso1N3:"275",wikidata:"Q23792",nameEn:"Palestine",country:"IL",groups:["145","142"],callingCodes:["970"]},geometry:{type:"MultiPolygon",coordinates:[[[[34.052,31.46619],[34.21853,31.32363],[34.23572,31.2966],[34.24012,31.29591],[34.26742,31.21998],[34.29417,31.24194],[34.36523,31.28963],[34.37381,31.30598],[34.36505,31.36404],[34.40077,31.40926],[34.48892,31.48365],[34.56797,31.54197],[34.48681,31.59711],[34.29262,31.70393],[34.052,31.46619]]],[[[35.47672,31.49578],[35.55941,31.76535],[35.52758,31.9131],[35.54375,31.96587],[35.52012,32.04076],[35.57111,32.21877],[35.55807,32.38674],[35.42078,32.41562],[35.41048,32.43706],[35.41598,32.45593],[35.42034,32.46009],[35.40224,32.50136],[35.35212,32.52047],[35.30685,32.51024],[35.29306,32.50947],[35.25049,32.52453],[35.2244,32.55289],[35.15937,32.50466],[35.10882,32.4757],[35.10024,32.47856],[35.09236,32.47614],[35.08564,32.46948],[35.07059,32.4585],[35.05423,32.41754],[35.05311,32.4024],[35.0421,32.38242],[35.05142,32.3667],[35.04243,32.35008],[35.01772,32.33863],[35.01119,32.28684],[35.02939,32.2671],[35.01841,32.23981],[34.98885,32.20758],[34.95703,32.19522],[34.96009,32.17503],[34.99039,32.14626],[34.98507,32.12606],[34.99437,32.10962],[34.9863,32.09551],[35.00261,32.027],[34.98682,31.96935],[35.00124,31.93264],[35.03489,31.92448],[35.03978,31.89276],[35.03489,31.85919],[34.99712,31.85569],[34.9724,31.83352],[35.01978,31.82944],[35.05617,31.85685],[35.07677,31.85627],[35.14174,31.81325],[35.18603,31.80901],[35.18169,31.82542],[35.19461,31.82687],[35.21469,31.81835],[35.216,31.83894],[35.21128,31.863],[35.20381,31.86716],[35.20673,31.88151],[35.20791,31.8821],[35.20945,31.8815],[35.21016,31.88237],[35.21276,31.88153],[35.2136,31.88241],[35.22014,31.88264],[35.22294,31.87889],[35.22567,31.86745],[35.22817,31.8638],[35.2249,31.85433],[35.2304,31.84222],[35.24816,31.8458],[35.25753,31.8387],[35.251,31.83085],[35.26404,31.82567],[35.25573,31.81362],[35.26058,31.79064],[35.25225,31.7678],[35.26319,31.74846],[35.25182,31.73945],[35.24981,31.72543],[35.2438,31.7201],[35.24315,31.71244],[35.23972,31.70896],[35.22392,31.71899],[35.21937,31.71578],[35.20538,31.72388],[35.18023,31.72067],[35.16478,31.73242],[35.15474,31.73352],[35.15119,31.73634],[35.13931,31.73012],[35.12933,31.7325],[35.11895,31.71454],[35.10782,31.71594],[35.08226,31.69107],[35.00879,31.65426],[34.95249,31.59813],[34.9415,31.55601],[34.94356,31.50743],[34.93258,31.47816],[34.89756,31.43891],[34.87833,31.39321],[34.88932,31.37093],[34.92571,31.34337],[35.02459,31.35979],[35.13033,31.3551],[35.22921,31.37445],[35.39675,31.49572],[35.47672,31.49578]]]]}},{type:"Feature",properties:{iso1A2:"PT",iso1A3:"PRT",iso1N3:"620",wikidata:"Q45",nameEn:"Portugal",groups:["EU","039","150"],callingCodes:["351"]},geometry:{type:"MultiPolygon",coordinates:[[[[-6.19128,41.57638],[-6.29863,41.66432],[-6.44204,41.68258],[-6.49907,41.65823],[-6.54633,41.68623],[-6.56426,41.74219],[-6.51374,41.8758],[-6.56752,41.88429],[-6.5447,41.94371],[-6.58544,41.96674],[-6.61967,41.94008],[-6.75004,41.94129],[-6.76959,41.98734],[-6.81196,41.99097],[-6.82174,41.94493],[-6.94396,41.94403],[-6.95537,41.96553],[-6.98144,41.9728],[-7.01078,41.94977],[-7.07596,41.94977],[-7.08574,41.97401],[-7.14115,41.98855],[-7.18549,41.97515],[-7.18677,41.88793],[-7.32366,41.8406],[-7.37092,41.85031],[-7.42864,41.80589],[-7.42854,41.83262],[-7.44759,41.84451],[-7.45566,41.86488],[-7.49803,41.87095],[-7.52737,41.83939],[-7.62188,41.83089],[-7.58603,41.87944],[-7.65774,41.88308],[-7.69848,41.90977],[-7.84188,41.88065],[-7.88055,41.84571],[-7.88751,41.92553],[-7.90707,41.92432],[-7.92336,41.8758],[-7.9804,41.87337],[-8.01136,41.83453],[-8.0961,41.81024],[-8.16455,41.81753],[-8.16944,41.87944],[-8.19551,41.87459],[-8.2185,41.91237],[-8.16232,41.9828],[-8.08796,42.01398],[-8.08847,42.05767],[-8.11729,42.08537],[-8.18178,42.06436],[-8.19406,42.12141],[-8.18947,42.13853],[-8.1986,42.15402],[-8.22406,42.1328],[-8.24681,42.13993],[-8.2732,42.12396],[-8.29809,42.106],[-8.32161,42.10218],[-8.33912,42.08358],[-8.36353,42.09065],[-8.38323,42.07683],[-8.40143,42.08052],[-8.42512,42.07199],[-8.44123,42.08218],[-8.48185,42.0811],[-8.52837,42.07658],[-8.5252,42.06264],[-8.54563,42.0537],[-8.58086,42.05147],[-8.59493,42.05708],[-8.63791,42.04691],[-8.64626,42.03668],[-8.65832,42.02972],[-8.6681,41.99703],[-8.69071,41.98862],[-8.7478,41.96282],[-8.74606,41.9469],[-8.75712,41.92833],[-8.81794,41.90375],[-8.87157,41.86488],[-9.14112,41.86623],[-36.43765,41.39418],[-15.92339,29.50503],[-7.37282,36.96896],[-7.39769,37.16868],[-7.41133,37.20314],[-7.41854,37.23813],[-7.43227,37.25152],[-7.43974,37.38913],[-7.46878,37.47127],[-7.51759,37.56119],[-7.41981,37.75729],[-7.33441,37.81193],[-7.27314,37.90145],[-7.24544,37.98884],[-7.12648,38.00296],[-7.10366,38.04404],[-7.05966,38.01966],[-7.00375,38.01914],[-6.93418,38.21454],[-7.09389,38.17227],[-7.15581,38.27597],[-7.32529,38.44336],[-7.265,38.61674],[-7.26174,38.72107],[-7.03848,38.87221],[-7.051,38.907],[-6.95211,39.0243],[-6.97004,39.07619],[-7.04011,39.11919],[-7.10692,39.10275],[-7.14929,39.11287],[-7.12811,39.17101],[-7.23566,39.20132],[-7.23403,39.27579],[-7.3149,39.34857],[-7.2927,39.45847],[-7.49477,39.58794],[-7.54121,39.66717],[-7.33507,39.64569],[-7.24707,39.66576],[-7.01613,39.66877],[-6.97492,39.81488],[-6.91463,39.86618],[-6.86737,40.01986],[-6.94233,40.10716],[-7.00589,40.12087],[-7.02544,40.18564],[-7.00426,40.23169],[-6.86085,40.26776],[-6.86085,40.2976],[-6.80218,40.33239],[-6.78426,40.36468],[-6.84618,40.42177],[-6.84944,40.46394],[-6.7973,40.51723],[-6.80218,40.55067],[-6.84292,40.56801],[-6.79567,40.65955],[-6.82826,40.74603],[-6.82337,40.84472],[-6.79892,40.84842],[-6.80707,40.88047],[-6.84292,40.89771],[-6.8527,40.93958],[-6.9357,41.02888],[-6.913,41.03922],[-6.88843,41.03027],[-6.84781,41.02692],[-6.80942,41.03629],[-6.79241,41.05397],[-6.75655,41.10187],[-6.77319,41.13049],[-6.69711,41.1858],[-6.68286,41.21641],[-6.65046,41.24725],[-6.55937,41.24417],[-6.38551,41.35274],[-6.38553,41.38655],[-6.3306,41.37677],[-6.26777,41.48796],[-6.19128,41.57638]]]]}},{type:"Feature",properties:{iso1A2:"PW",iso1A3:"PLW",iso1N3:"585",wikidata:"Q695",nameEn:"Palau",groups:["057","009"],roadSpeedUnit:"mph",callingCodes:["680"]},geometry:{type:"MultiPolygon",coordinates:[[[[128.97621,3.08804],[134.40878,1.79674],[136.27107,6.73747],[136.04605,12.45908],[128.97621,3.08804]]]]}},{type:"Feature",properties:{iso1A2:"PY",iso1A3:"PRY",iso1N3:"600",wikidata:"Q733",nameEn:"Paraguay",groups:["005","419","019"],callingCodes:["595"]},geometry:{type:"MultiPolygon",coordinates:[[[[-58.16225,-20.16193],[-58.23216,-19.80058],[-59.06965,-19.29148],[-60.00638,-19.2981],[-61.73723,-19.63958],[-61.93912,-20.10053],[-62.26883,-20.55311],[-62.2757,-21.06657],[-62.64455,-22.25091],[-62.51761,-22.37684],[-62.22768,-22.55807],[-61.9756,-23.0507],[-61.0782,-23.62932],[-60.99754,-23.80934],[-60.28163,-24.04436],[-60.03367,-24.00701],[-59.45482,-24.34787],[-59.33886,-24.49935],[-58.33055,-24.97099],[-58.25492,-24.92528],[-57.80821,-25.13863],[-57.57431,-25.47269],[-57.87176,-25.93604],[-58.1188,-26.16704],[-58.3198,-26.83443],[-58.65321,-27.14028],[-58.59549,-27.29973],[-58.04205,-27.2387],[-56.85337,-27.5165],[-56.18313,-27.29851],[-55.89195,-27.3467],[-55.74475,-27.44485],[-55.59094,-27.32444],[-55.62322,-27.1941],[-55.39611,-26.97679],[-55.25243,-26.93808],[-55.16948,-26.96068],[-55.06351,-26.80195],[-55.00584,-26.78754],[-54.80868,-26.55669],[-54.70732,-26.45099],[-54.69333,-26.37705],[-54.67359,-25.98607],[-54.60664,-25.9691],[-54.62063,-25.91213],[-54.59398,-25.59224],[-54.59509,-25.53696],[-54.60196,-25.48397],[-54.62033,-25.46026],[-54.4423,-25.13381],[-54.28207,-24.07305],[-54.32807,-24.01865],[-54.6238,-23.83078],[-55.02691,-23.97317],[-55.0518,-23.98666],[-55.12292,-23.99669],[-55.41784,-23.9657],[-55.44117,-23.9185],[-55.43585,-23.87157],[-55.5555,-23.28237],[-55.52288,-23.2595],[-55.5446,-23.22811],[-55.63849,-22.95122],[-55.62493,-22.62765],[-55.68742,-22.58407],[-55.6986,-22.56268],[-55.72366,-22.5519],[-55.741,-22.52018],[-55.74941,-22.46436],[-55.8331,-22.29008],[-56.23206,-22.25347],[-56.45893,-22.08072],[-56.5212,-22.11556],[-56.6508,-22.28387],[-57.98625,-22.09157],[-57.94642,-21.73799],[-57.88239,-21.6868],[-57.93492,-21.65505],[-57.84536,-20.93155],[-58.16225,-20.16193]]]]}},{type:"Feature",properties:{iso1A2:"QA",iso1A3:"QAT",iso1N3:"634",wikidata:"Q846",nameEn:"Qatar",groups:["145","142"],callingCodes:["974"]},geometry:{type:"MultiPolygon",coordinates:[[[[50.92992,24.54396],[51.09638,24.46907],[51.29972,24.50747],[51.39468,24.62785],[51.58834,24.66608],[51.83108,24.71675],[51.83682,26.70231],[50.93865,26.30758],[50.81266,25.88946],[50.86149,25.6965],[50.7801,25.595],[50.80824,25.54641],[50.57069,25.57887],[50.8133,24.74049],[50.92992,24.54396]]]]}},{type:"Feature",properties:{iso1A2:"RE",iso1A3:"REU",iso1N3:"638",wikidata:"Q17070",nameEn:"Réunion",country:"FR",groups:["EU","014","202","002"],callingCodes:["262"]},geometry:{type:"MultiPolygon",coordinates:[[[[53.37984,-21.23941],[56.73473,-21.9174],[56.62373,-20.2711],[53.37984,-21.23941]]]]}},{type:"Feature",properties:{iso1A2:"RO",iso1A3:"ROU",iso1N3:"642",wikidata:"Q218",nameEn:"Romania",groups:["EU","151","150"],callingCodes:["40"]},geometry:{type:"MultiPolygon",coordinates:[[[[27.15622,47.98538],[27.02985,48.09083],[27.04118,48.12522],[26.96119,48.13003],[26.98042,48.15752],[26.94265,48.1969],[26.87708,48.19919],[26.81161,48.25049],[26.62823,48.25804],[26.55202,48.22445],[26.33504,48.18418],[26.17711,47.99246],[26.05901,47.9897],[25.77723,47.93919],[25.63878,47.94924],[25.23778,47.89403],[25.11144,47.75203],[24.88896,47.7234],[24.81893,47.82031],[24.70632,47.84428],[24.61994,47.95062],[24.43578,47.97131],[24.34926,47.9244],[24.22566,47.90231],[24.11281,47.91487],[24.06466,47.95317],[24.02999,47.95087],[24.00801,47.968],[23.98553,47.96076],[23.96337,47.96672],[23.94192,47.94868],[23.89352,47.94512],[23.8602,47.9329],[23.80904,47.98142],[23.75188,47.99705],[23.66262,47.98786],[23.63894,48.00293],[23.5653,48.00499],[23.52803,48.01818],[23.4979,47.96858],[23.33577,48.0237],[23.27397,48.08245],[23.15999,48.12188],[23.1133,48.08061],[23.08858,48.00716],[23.0158,47.99338],[22.92241,48.02002],[22.94301,47.96672],[22.89849,47.95851],[22.77991,47.87211],[22.76617,47.8417],[22.67247,47.7871],[22.46559,47.76583],[22.41979,47.7391],[22.31816,47.76126],[22.00917,47.50492],[22.03389,47.42508],[22.01055,47.37767],[21.94463,47.38046],[21.78395,47.11104],[21.648,47.03902],[21.68645,46.99595],[21.59581,46.91628],[21.59307,46.86935],[21.52028,46.84118],[21.48935,46.7577],[21.5151,46.72147],[21.43926,46.65109],[21.33214,46.63035],[21.26929,46.4993],[21.28061,46.44941],[21.16872,46.30118],[21.06572,46.24897],[20.86797,46.28884],[20.74574,46.25467],[20.76085,46.21002],[20.63863,46.12728],[20.49718,46.18721],[20.45377,46.14405],[20.35573,46.16629],[20.28324,46.1438],[20.26068,46.12332],[20.35862,45.99356],[20.54818,45.89939],[20.65645,45.82801],[20.70069,45.7493],[20.77416,45.75601],[20.78446,45.78522],[20.82364,45.77738],[20.80361,45.65875],[20.76798,45.60969],[20.83321,45.53567],[20.77217,45.49788],[20.86026,45.47295],[20.87948,45.42743],[21.09894,45.30144],[21.17612,45.32566],[21.20392,45.2677],[21.29398,45.24148],[21.48278,45.19557],[21.51299,45.15345],[21.4505,45.04294],[21.35855,45.01941],[21.54938,44.9327],[21.56328,44.89502],[21.48202,44.87199],[21.44013,44.87613],[21.35643,44.86364],[21.38802,44.78133],[21.55007,44.77304],[21.60019,44.75208],[21.61942,44.67059],[21.67504,44.67107],[21.71692,44.65349],[21.7795,44.66165],[21.99364,44.63395],[22.08016,44.49844],[22.13234,44.47444],[22.18315,44.48179],[22.30844,44.6619],[22.45301,44.7194],[22.61917,44.61489],[22.69196,44.61587],[22.76749,44.54446],[22.70981,44.51852],[22.61368,44.55719],[22.56493,44.53419],[22.54021,44.47836],[22.45436,44.47258],[22.56012,44.30712],[22.68166,44.28206],[22.67173,44.21564],[23.04988,44.07694],[23.01674,44.01946],[22.87873,43.9844],[22.83753,43.88055],[22.85314,43.84452],[23.05288,43.79494],[23.26772,43.84843],[23.4507,43.84936],[23.61687,43.79289],[23.73978,43.80627],[24.18149,43.68218],[24.35364,43.70211],[24.50264,43.76314],[24.62281,43.74082],[24.73542,43.68523],[24.96682,43.72693],[25.10718,43.6831],[25.17144,43.70261],[25.39528,43.61866],[25.72792,43.69263],[25.94911,43.85745],[26.05584,43.90925],[26.10115,43.96908],[26.38764,44.04356],[26.62712,44.05698],[26.95141,44.13555],[27.26845,44.12602],[27.39757,44.0141],[27.60834,44.01206],[27.64542,44.04958],[27.73468,43.95326],[27.92008,44.00761],[27.99558,43.84193],[28.23293,43.76],[29.24336,43.70874],[30.04414,45.08461],[29.69272,45.19227],[29.65428,45.25629],[29.68175,45.26885],[29.59798,45.38857],[29.42632,45.44545],[29.24779,45.43388],[28.96077,45.33164],[28.94292,45.28045],[28.81383,45.3384],[28.78911,45.24179],[28.71358,45.22631],[28.5735,45.24759],[28.34554,45.32102],[28.28504,45.43907],[28.21139,45.46895],[28.18741,45.47358],[28.08927,45.6051],[28.16568,45.6421],[28.13111,45.92819],[28.08612,46.01105],[28.13684,46.18099],[28.10937,46.22852],[28.19864,46.31869],[28.18902,46.35283],[28.25769,46.43334],[28.22281,46.50481],[28.24808,46.64305],[28.12173,46.82283],[28.09095,46.97621],[27.81892,47.1381],[27.73172,47.29248],[27.68706,47.28962],[27.60263,47.32507],[27.55731,47.46637],[27.47942,47.48113],[27.3979,47.59473],[27.32202,47.64009],[27.25519,47.71366],[27.29069,47.73722],[27.1618,47.92391],[27.15622,47.98538]]]]}},{type:"Feature",properties:{iso1A2:"RS",iso1A3:"SRB",iso1N3:"688",wikidata:"Q403",nameEn:"Serbia",groups:["039","150"],callingCodes:["381"]},geometry:{type:"MultiPolygon",coordinates:[[[[19.66007,46.19005],[19.56113,46.16824],[19.52473,46.1171],[19.28826,45.99694],[19.14543,45.9998],[19.10388,46.04015],[19.0791,45.96458],[19.01284,45.96529],[18.99712,45.93537],[18.81394,45.91329],[18.85783,45.85493],[18.90305,45.71863],[18.96691,45.66731],[18.88776,45.57253],[18.94562,45.53712],[19.07471,45.53086],[19.08364,45.48804],[18.99918,45.49333],[18.97446,45.37528],[19.10774,45.29547],[19.28208,45.23813],[19.41941,45.23475],[19.43589,45.17137],[19.19144,45.17863],[19.14063,45.12972],[19.07952,45.14668],[19.1011,45.01191],[19.05205,44.97692],[19.15573,44.95409],[19.06853,44.89915],[19.02871,44.92541],[18.98957,44.90645],[19.01994,44.85493],[19.18183,44.92055],[19.36722,44.88164],[19.32543,44.74058],[19.26388,44.65412],[19.16699,44.52197],[19.13369,44.52521],[19.12278,44.50132],[19.14837,44.45253],[19.14681,44.41463],[19.11785,44.40313],[19.10749,44.39421],[19.10704,44.38249],[19.10365,44.37795],[19.10298,44.36924],[19.11865,44.36712],[19.1083,44.3558],[19.11547,44.34218],[19.13556,44.338],[19.13332,44.31492],[19.16741,44.28648],[19.18328,44.28383],[19.20508,44.2917],[19.23306,44.26097],[19.26945,44.26957],[19.32464,44.27185],[19.34773,44.23244],[19.3588,44.18353],[19.40927,44.16722],[19.43905,44.13088],[19.47338,44.15034],[19.48386,44.14332],[19.47321,44.1193],[19.51167,44.08158],[19.55999,44.06894],[19.57467,44.04716],[19.61991,44.05254],[19.61836,44.01464],[19.56498,43.99922],[19.52515,43.95573],[19.38439,43.96611],[19.24363,44.01502],[19.23465,43.98764],[19.3986,43.79668],[19.5176,43.71403],[19.50455,43.58385],[19.42696,43.57987],[19.41941,43.54056],[19.36653,43.60921],[19.33426,43.58833],[19.2553,43.5938],[19.24774,43.53061],[19.22807,43.5264],[19.22229,43.47926],[19.44315,43.38846],[19.48171,43.32644],[19.52962,43.31623],[19.54598,43.25158],[19.62661,43.2286],[19.64063,43.19027],[19.76918,43.16044],[19.79255,43.11951],[19.92576,43.08539],[19.96549,43.11098],[19.98887,43.0538],[20.04729,43.02732],[20.05431,42.99571],[20.12325,42.96237],[20.14896,42.99058],[20.16415,42.97177],[20.34528,42.90676],[20.35692,42.8335],[20.40594,42.84853],[20.43734,42.83157],[20.53484,42.8885],[20.48692,42.93208],[20.59929,43.01067],[20.64557,43.00826],[20.69515,43.09641],[20.59929,43.20492],[20.68688,43.21335],[20.73811,43.25068],[20.82145,43.26769],[20.88685,43.21697],[20.83727,43.17842],[20.96287,43.12416],[21.00749,43.13984],[21.05378,43.10707],[21.08952,43.13471],[21.14465,43.11089],[21.16734,42.99694],[21.2041,43.02277],[21.23877,43.00848],[21.23534,42.95523],[21.2719,42.8994],[21.32974,42.90424],[21.36941,42.87397],[21.44047,42.87276],[21.39045,42.74888],[21.47498,42.74695],[21.59154,42.72643],[21.58755,42.70418],[21.6626,42.67813],[21.75025,42.70125],[21.79413,42.65923],[21.75672,42.62695],[21.7327,42.55041],[21.70522,42.54176],[21.7035,42.51899],[21.62556,42.45106],[21.64209,42.41081],[21.62887,42.37664],[21.59029,42.38042],[21.57021,42.3647],[21.53467,42.36809],[21.5264,42.33634],[21.56772,42.30946],[21.58992,42.25915],[21.70111,42.23789],[21.77176,42.2648],[21.84654,42.3247],[21.91595,42.30392],[21.94405,42.34669],[22.02908,42.29848],[22.16384,42.32103],[22.29605,42.37477],[22.29275,42.34913],[22.34773,42.31725],[22.45919,42.33822],[22.47498,42.3915],[22.51961,42.3991],[22.55669,42.50144],[22.43983,42.56851],[22.4997,42.74144],[22.43309,42.82057],[22.54302,42.87774],[22.74826,42.88701],[22.78397,42.98253],[22.89521,43.03625],[22.98104,43.11199],[23.00806,43.19279],[22.89727,43.22417],[22.82036,43.33665],[22.53397,43.47225],[22.47582,43.6558],[22.41043,43.69566],[22.35558,43.81281],[22.41449,44.00514],[22.61688,44.06534],[22.61711,44.16938],[22.67173,44.21564],[22.68166,44.28206],[22.56012,44.30712],[22.45436,44.47258],[22.54021,44.47836],[22.56493,44.53419],[22.61368,44.55719],[22.70981,44.51852],[22.76749,44.54446],[22.69196,44.61587],[22.61917,44.61489],[22.45301,44.7194],[22.30844,44.6619],[22.18315,44.48179],[22.13234,44.47444],[22.08016,44.49844],[21.99364,44.63395],[21.7795,44.66165],[21.71692,44.65349],[21.67504,44.67107],[21.61942,44.67059],[21.60019,44.75208],[21.55007,44.77304],[21.38802,44.78133],[21.35643,44.86364],[21.44013,44.87613],[21.48202,44.87199],[21.56328,44.89502],[21.54938,44.9327],[21.35855,45.01941],[21.4505,45.04294],[21.51299,45.15345],[21.48278,45.19557],[21.29398,45.24148],[21.20392,45.2677],[21.17612,45.32566],[21.09894,45.30144],[20.87948,45.42743],[20.86026,45.47295],[20.77217,45.49788],[20.83321,45.53567],[20.76798,45.60969],[20.80361,45.65875],[20.82364,45.77738],[20.78446,45.78522],[20.77416,45.75601],[20.70069,45.7493],[20.65645,45.82801],[20.54818,45.89939],[20.35862,45.99356],[20.26068,46.12332],[20.09713,46.17315],[20.03533,46.14509],[20.01816,46.17696],[19.93508,46.17553],[19.81491,46.1313],[19.66007,46.19005]]]]}},{type:"Feature",properties:{iso1A2:"RU",iso1A3:"RUS",iso1N3:"643",wikidata:"Q159",nameEn:"Russia",groups:["151","150"],callingCodes:["7"]},geometry:{type:"MultiPolygon",coordinates:[[[[-179.99933,64.74703],[-172.76104,63.77445],[-169.03888,65.48473],[-168.95635,65.98512],[-168.25765,71.99091],[-179.9843,71.90735],[-179.99933,64.74703]]],[[[39.81147,43.06294],[40.0078,43.38551],[40.00853,43.40578],[40.01552,43.42025],[40.01007,43.42411],[40.03312,43.44262],[40.04445,43.47776],[40.10657,43.57344],[40.65957,43.56212],[41.64935,43.22331],[42.40563,43.23226],[42.66667,43.13917],[42.75889,43.19651],[43.03322,43.08883],[43.0419,43.02413],[43.81453,42.74297],[43.73119,42.62043],[43.95517,42.55396],[44.54202,42.75699],[44.70002,42.74679],[44.80941,42.61277],[44.88754,42.74934],[45.15318,42.70598],[45.36501,42.55268],[45.78692,42.48358],[45.61676,42.20768],[46.42738,41.91323],[46.5332,41.87389],[46.58924,41.80547],[46.75269,41.8623],[46.8134,41.76252],[47.00955,41.63583],[46.99554,41.59743],[47.03757,41.55434],[47.10762,41.59044],[47.34579,41.27884],[47.49004,41.26366],[47.54504,41.20275],[47.62288,41.22969],[47.75831,41.19455],[47.87973,41.21798],[48.07587,41.49957],[48.22064,41.51472],[48.2878,41.56221],[48.40277,41.60441],[48.42301,41.65444],[48.55078,41.77917],[48.5867,41.84306],[48.80971,41.95365],[49.2134,44.84989],[49.88945,46.04554],[49.32259,46.26944],[49.16518,46.38542],[48.54988,46.56267],[48.51142,46.69268],[49.01136,46.72716],[48.52326,47.4102],[48.45173,47.40818],[48.15348,47.74545],[47.64973,47.76559],[47.41689,47.83687],[47.38731,47.68176],[47.12107,47.83687],[47.11516,48.27188],[46.49011,48.43019],[46.78392,48.95352],[46.91104,48.99715],[47.01458,49.07085],[47.04416,49.17152],[46.98795,49.23531],[46.78398,49.34026],[46.9078,49.86707],[47.18319,49.93721],[47.34589,50.09308],[47.30448,50.30894],[47.58551,50.47867],[48.10044,50.09242],[48.24519,49.86099],[48.42564,49.82283],[48.68352,49.89546],[48.90782,50.02281],[48.57946,50.63278],[48.86936,50.61589],[49.12673,50.78639],[49.41959,50.85927],[49.39001,51.09396],[49.76866,51.11067],[49.97277,51.2405],[50.26859,51.28677],[50.59695,51.61859],[51.26254,51.68466],[51.301,51.48799],[51.77431,51.49536],[51.8246,51.67916],[52.36119,51.74161],[52.54329,51.48444],[53.46165,51.49445],[53.69299,51.23466],[54.12248,51.11542],[54.46331,50.85554],[54.41894,50.61214],[54.55797,50.52006],[54.71476,50.61214],[54.56685,51.01958],[54.72067,51.03261],[55.67774,50.54508],[56.11398,50.7471],[56.17906,50.93204],[57.17302,51.11253],[57.44221,50.88354],[57.74986,50.93017],[57.75578,51.13852],[58.3208,51.15151],[58.87974,50.70852],[59.48928,50.64216],[59.51886,50.49937],[59.81172,50.54451],[60.01288,50.8163],[60.17262,50.83312],[60.31914,50.67705],[60.81833,50.6629],[61.4431,50.80679],[61.56889,51.23679],[61.6813,51.25716],[61.55114,51.32746],[61.50677,51.40687],[60.95655,51.48615],[60.92401,51.61124],[60.5424,51.61675],[60.36787,51.66815],[60.50986,51.7964],[60.09867,51.87135],[59.99809,51.98263],[60.19925,51.99173],[60.48915,52.15175],[60.72581,52.15538],[60.78201,52.22067],[61.05417,52.35096],[60.98021,52.50068],[60.84709,52.52228],[60.84118,52.63912],[60.71693,52.66245],[60.71989,52.75923],[61.05842,52.92217],[61.23462,53.03227],[62.0422,52.96105],[62.12799,52.99133],[62.14574,53.09626],[61.19024,53.30536],[61.14291,53.41481],[61.29082,53.50992],[61.37957,53.45887],[61.57185,53.50112],[61.55706,53.57144],[60.90626,53.62937],[61.22574,53.80268],[61.14283,53.90063],[60.99796,53.93699],[61.26863,53.92797],[61.3706,54.08464],[61.47603,54.08048],[61.56941,53.95703],[61.65318,54.02445],[62.03913,53.94768],[62.00966,54.04134],[62.38535,54.03961],[62.45931,53.90737],[62.56876,53.94047],[62.58651,54.05871],[63.80604,54.27079],[63.91224,54.20013],[64.02715,54.22679],[63.97686,54.29763],[64.97216,54.4212],[65.11033,54.33028],[65.24663,54.35721],[65.20174,54.55216],[68.21308,54.98645],[68.26661,55.09226],[68.19206,55.18823],[68.90865,55.38148],[69.34224,55.36344],[69.74917,55.35545],[70.19179,55.1476],[70.76493,55.3027],[70.96009,55.10558],[71.08288,54.71253],[71.24185,54.64965],[71.08706,54.33376],[71.10379,54.13326],[71.96141,54.17736],[72.17477,54.36303],[72.43415,53.92685],[72.71026,54.1161],[73.37963,53.96132],[73.74778,54.07194],[73.68921,53.86522],[73.25412,53.61532],[73.39218,53.44623],[75.07405,53.80831],[75.43398,53.98652],[75.3668,54.07439],[76.91052,54.4677],[76.82266,54.1798],[76.44076,54.16017],[76.54243,53.99329],[77.90383,53.29807],[79.11255,52.01171],[80.08138,50.77658],[80.4127,50.95581],[80.44819,51.20855],[80.80318,51.28262],[81.16999,51.15662],[81.06091,50.94833],[81.41248,50.97524],[81.46581,50.77658],[81.94999,50.79307],[82.55443,50.75412],[83.14607,51.00796],[83.8442,50.87375],[84.29385,50.27257],[84.99198,50.06793],[85.24047,49.60239],[86.18709,49.50259],[86.63674,49.80136],[86.79056,49.74787],[86.61307,49.60239],[86.82606,49.51796],[87.03071,49.25142],[87.31465,49.23603],[87.28386,49.11626],[87.478,49.07403],[87.48983,49.13794],[87.81333,49.17354],[87.98977,49.18147],[88.15543,49.30314],[88.17223,49.46934],[88.42449,49.48821],[88.82499,49.44808],[89.70687,49.72535],[89.59711,49.90851],[91.86048,50.73734],[92.07173,50.69585],[92.44714,50.78762],[93.01109,50.79001],[92.99595,50.63183],[94.30823,50.57498],[94.39258,50.22193],[94.49477,50.17832],[94.6121,50.04239],[94.97166,50.04725],[95.02465,49.96941],[95.74757,49.97915],[95.80056,50.04239],[96.97388,49.88413],[97.24639,49.74737],[97.56811,49.84265],[97.56432,49.92801],[97.76871,49.99861],[97.85197,49.91339],[98.29481,50.33561],[98.31373,50.4996],[98.06393,50.61262],[97.9693,50.78044],[98.01472,50.86652],[97.83305,51.00248],[98.05257,51.46696],[98.22053,51.46579],[98.33222,51.71832],[98.74142,51.8637],[98.87768,52.14563],[99.27888,51.96876],[99.75578,51.90108],[99.89203,51.74903],[100.61116,51.73028],[101.39085,51.45753],[101.5044,51.50467],[102.14032,51.35566],[102.32194,50.67982],[102.71178,50.38873],[103.70343,50.13952],[105.32528,50.4648],[106.05562,50.40582],[106.07865,50.33474],[106.47156,50.31909],[106.49628,50.32436],[106.51122,50.34408],[106.58373,50.34044],[106.80326,50.30177],[107.00007,50.1977],[107.1174,50.04239],[107.36407,49.97612],[107.96116,49.93191],[107.95387,49.66659],[108.27937,49.53167],[108.53969,49.32325],[109.18017,49.34709],[109.51325,49.22859],[110.24373,49.16676],[110.39891,49.25083],[110.64493,49.1816],[113.02647,49.60772],[113.20216,49.83356],[114.325,50.28098],[114.9703,50.19254],[115.26068,49.97367],[115.73602,49.87688],[116.22402,50.04477],[116.62502,49.92919],[116.71193,49.83813],[117.07142,49.68482],[117.27597,49.62544],[117.48208,49.62324],[117.82343,49.52696],[118.61623,49.93809],[119.11003,50.00276],[119.27996,50.13348],[119.38598,50.35162],[119.13553,50.37412],[120.10963,51.671],[120.65907,51.93544],[120.77337,52.20805],[120.61346,52.32447],[120.71673,52.54099],[120.46454,52.63811],[120.04049,52.58773],[120.0451,52.7359],[120.85633,53.28499],[121.39213,53.31888],[122.35063,53.49565],[122.85966,53.47395],[123.26989,53.54843],[123.86158,53.49391],[124.46078,53.21881],[125.17522,53.20225],[125.6131,53.07229],[126.558,52.13738],[126.44606,51.98254],[126.68349,51.70607],[126.90369,51.3238],[126.93135,51.0841],[127.14586,50.91152],[127.28165,50.72075],[127.36335,50.58306],[127.28765,50.46585],[127.36009,50.43787],[127.37384,50.28393],[127.60515,50.23503],[127.49299,50.01251],[127.53516,49.84306],[127.83476,49.5748],[128.72896,49.58676],[129.11153,49.36813],[129.23232,49.40353],[129.35317,49.3481],[129.40398,49.44194],[129.50685,49.42398],[129.67598,49.29596],[129.85416,49.11067],[130.2355,48.86741],[130.43232,48.90844],[130.66946,48.88251],[130.52147,48.61745],[130.84462,48.30942],[130.65103,48.10052],[130.90915,47.90623],[130.95985,47.6957],[131.09871,47.6852],[131.2635,47.73325],[131.90448,47.68011],[132.57309,47.71741],[132.66989,47.96491],[134.49516,48.42884],[134.75328,48.36763],[134.67098,48.1564],[134.55508,47.98651],[134.7671,47.72051],[134.50898,47.4812],[134.20016,47.33458],[134.03538,46.75668],[133.84104,46.46681],[133.91496,46.4274],[133.88097,46.25066],[133.68047,46.14697],[133.72695,46.05576],[133.67569,45.9759],[133.60442,45.90053],[133.48457,45.86203],[133.41083,45.57723],[133.19419,45.51913],[133.09279,45.25693],[133.12293,45.1332],[132.96373,45.0212],[132.83978,45.05916],[131.99417,45.2567],[131.86903,45.33636],[131.76532,45.22609],[131.66852,45.2196],[131.68466,45.12374],[131.48415,44.99513],[130.95639,44.85154],[131.1108,44.70266],[131.30365,44.04262],[131.25484,44.03131],[131.23583,43.96085],[131.26176,43.94011],[131.21105,43.82383],[131.19492,43.53047],[131.29402,43.46695],[131.30324,43.39498],[131.19031,43.21385],[131.20414,43.13654],[131.10274,43.04734],[131.135,42.94114],[131.02668,42.91246],[131.02438,42.86518],[130.66524,42.84753],[130.44361,42.76205],[130.40213,42.70788],[130.56576,42.68925],[130.62107,42.58413],[130.55143,42.52158],[130.56835,42.43281],[130.60805,42.4317],[130.64181,42.41422],[130.66367,42.38024],[130.65022,42.32281],[131.95041,41.5445],[140.9182,45.92937],[145.82343,44.571],[145.23667,43.76813],[153.94307,38.42848],[180,62.52334],[180,71.53642],[155.31937,81.93282],[36.48095,82.16765],[32.07813,72.01005],[31.59909,70.16571],[30.84095,69.80584],[30.95011,69.54699],[30.52662,69.54699],[30.16363,69.65244],[29.97205,69.41623],[29.27631,69.2811],[29.26623,69.13794],[29.0444,69.0119],[28.91738,69.04774],[28.45957,68.91417],[28.78224,68.86696],[28.43941,68.53366],[28.62982,68.19816],[29.34179,68.06655],[29.66955,67.79872],[30.02041,67.67523],[29.91155,67.51507],[28.9839,66.94139],[29.91155,66.13863],[30.16363,65.66935],[29.97205,65.70256],[29.74013,65.64025],[29.84096,65.56945],[29.68972,65.31803],[29.61914,65.23791],[29.8813,65.22101],[29.84096,65.1109],[29.61914,65.05993],[29.68972,64.80789],[30.05271,64.79072],[30.12329,64.64862],[30.01238,64.57513],[30.06279,64.35782],[30.4762,64.25728],[30.55687,64.09036],[30.25437,63.83364],[29.98213,63.75795],[30.49637,63.46666],[31.23244,63.22239],[31.29294,63.09035],[31.58535,62.91642],[31.38369,62.66284],[31.10136,62.43042],[29.01829,61.17448],[28.82816,61.1233],[28.47974,60.93365],[27.77352,60.52722],[27.71177,60.3893],[27.44953,60.22766],[26.32936,60.00121],[26.90044,59.63819],[27.85643,59.58538],[28.04187,59.47017],[28.19061,59.39962],[28.21137,59.38058],[28.20537,59.36491],[28.19284,59.35791],[28.14215,59.28934],[28.00689,59.28351],[27.90911,59.24353],[27.87978,59.18097],[27.80482,59.1116],[27.74429,58.98351],[27.36366,58.78381],[27.55489,58.39525],[27.48541,58.22615],[27.62393,58.09462],[27.67282,57.92627],[27.81841,57.89244],[27.78526,57.83963],[27.56689,57.83356],[27.50171,57.78842],[27.52615,57.72843],[27.3746,57.66834],[27.40393,57.62125],[27.31919,57.57672],[27.34698,57.52242],[27.56832,57.53728],[27.52453,57.42826],[27.86101,57.29402],[27.66511,56.83921],[27.86101,56.88204],[28.04768,56.59004],[28.13526,56.57989],[28.10069,56.524],[28.19057,56.44637],[28.16599,56.37806],[28.23716,56.27588],[28.15217,56.16964],[28.30571,56.06035],[28.36888,56.05805],[28.37987,56.11399],[28.43068,56.09407],[28.5529,56.11705],[28.68337,56.10173],[28.63668,56.07262],[28.73418,55.97131],[29.08299,56.03427],[29.21717,55.98971],[29.44692,55.95978],[29.3604,55.75862],[29.51283,55.70294],[29.61446,55.77716],[29.80672,55.79569],[29.97975,55.87281],[30.12136,55.8358],[30.27776,55.86819],[30.30987,55.83592],[30.48257,55.81066],[30.51346,55.78982],[30.51037,55.76568],[30.63344,55.73079],[30.67464,55.64176],[30.72957,55.66268],[30.7845,55.58514],[30.86003,55.63169],[30.93419,55.6185],[30.95204,55.50667],[30.90123,55.46621],[30.93144,55.3914],[30.8257,55.3313],[30.81946,55.27931],[30.87944,55.28223],[30.97369,55.17134],[31.02071,55.06167],[31.00972,55.02783],[30.94243,55.03964],[30.9081,55.02232],[30.95754,54.98609],[30.93144,54.9585],[30.81759,54.94064],[30.8264,54.90062],[30.75165,54.80699],[30.95479,54.74346],[30.97127,54.71967],[31.0262,54.70698],[30.98226,54.68872],[30.99187,54.67046],[31.19339,54.66947],[31.21399,54.63113],[31.08543,54.50361],[31.22945,54.46585],[31.3177,54.34067],[31.30791,54.25315],[31.57002,54.14535],[31.89599,54.0837],[31.88744,54.03653],[31.85019,53.91801],[31.77028,53.80015],[31.89137,53.78099],[32.12621,53.81586],[32.36663,53.7166],[32.45717,53.74039],[32.50112,53.68594],[32.40499,53.6656],[32.47777,53.5548],[32.74968,53.45597],[32.73257,53.33494],[32.51725,53.28431],[32.40773,53.18856],[32.15368,53.07594],[31.82373,53.10042],[31.787,53.18033],[31.62496,53.22886],[31.56316,53.19432],[31.40523,53.21406],[31.36403,53.13504],[31.3915,53.09712],[31.33519,53.08805],[31.32283,53.04101],[31.24147,53.031],[31.35667,52.97854],[31.592,52.79011],[31.57277,52.71613],[31.50406,52.69707],[31.63869,52.55361],[31.56316,52.51518],[31.61397,52.48843],[31.62084,52.33849],[31.57971,52.32146],[31.70735,52.26711],[31.6895,52.1973],[31.77877,52.18636],[31.7822,52.11406],[31.81722,52.09955],[31.85018,52.11305],[31.96141,52.08015],[31.92159,52.05144],[32.08813,52.03319],[32.23331,52.08085],[32.2777,52.10266],[32.34044,52.1434],[32.33083,52.23685],[32.38988,52.24946],[32.3528,52.32842],[32.54781,52.32423],[32.69475,52.25535],[32.85405,52.27888],[32.89937,52.2461],[33.18913,52.3754],[33.51323,52.35779],[33.48027,52.31499],[33.55718,52.30324],[33.78789,52.37204],[34.05239,52.20132],[34.11199,52.14087],[34.09413,52.00835],[34.41136,51.82793],[34.42922,51.72852],[34.07765,51.67065],[34.17599,51.63253],[34.30562,51.5205],[34.22048,51.4187],[34.33446,51.363],[34.23009,51.26429],[34.31661,51.23936],[34.38802,51.2746],[34.6613,51.25053],[34.6874,51.18],[34.82472,51.17483],[34.97304,51.2342],[35.14058,51.23162],[35.12685,51.16191],[35.20375,51.04723],[35.31774,51.08434],[35.40837,51.04119],[35.32598,50.94524],[35.39307,50.92145],[35.41367,50.80227],[35.47704,50.77274],[35.48116,50.66405],[35.39464,50.64751],[35.47463,50.49247],[35.58003,50.45117],[35.61711,50.35707],[35.73659,50.35489],[35.80388,50.41356],[35.8926,50.43829],[36.06893,50.45205],[36.20763,50.3943],[36.30101,50.29088],[36.47817,50.31457],[36.58371,50.28563],[36.56655,50.2413],[36.64571,50.218],[36.69377,50.26982],[36.91762,50.34963],[37.08468,50.34935],[37.48204,50.46079],[37.47243,50.36277],[37.62486,50.29966],[37.62879,50.24481],[37.61113,50.21976],[37.75807,50.07896],[37.79515,50.08425],[37.90776,50.04194],[38.02999,49.94482],[38.02999,49.90592],[38.21675,49.98104],[38.18517,50.08161],[38.32524,50.08866],[38.35408,50.00664],[38.65688,49.97176],[38.68677,50.00904],[38.73311,49.90238],[38.90477,49.86787],[38.9391,49.79524],[39.1808,49.88911],[39.27968,49.75976],[39.44496,49.76067],[39.59142,49.73758],[39.65047,49.61761],[39.84548,49.56064],[40.13249,49.61672],[40.16683,49.56865],[40.03636,49.52321],[40.03087,49.45452],[40.1141,49.38798],[40.14912,49.37681],[40.18331,49.34996],[40.22176,49.25683],[40.01988,49.1761],[39.93437,49.05709],[39.6836,49.05121],[39.6683,48.99454],[39.71353,48.98959],[39.72649,48.9754],[39.74874,48.98675],[39.78368,48.91596],[39.98967,48.86901],[40.03636,48.91957],[40.08168,48.87443],[39.97182,48.79398],[39.79466,48.83739],[39.73104,48.7325],[39.71765,48.68673],[39.67226,48.59368],[39.79764,48.58668],[39.84548,48.57821],[39.86196,48.46633],[39.88794,48.44226],[39.94847,48.35055],[39.84136,48.33321],[39.84273,48.30947],[39.90041,48.3049],[39.91465,48.26743],[39.95248,48.29972],[39.9693,48.29904],[39.97325,48.31399],[39.99241,48.31768],[40.00752,48.22445],[39.94847,48.22811],[39.83724,48.06501],[39.88256,48.04482],[39.77544,48.04206],[39.82213,47.96396],[39.73935,47.82876],[38.87979,47.87719],[38.79628,47.81109],[38.76379,47.69346],[38.35062,47.61631],[38.28679,47.53552],[38.28954,47.39255],[38.22225,47.30788],[38.33074,47.30508],[38.32112,47.2585],[38.23049,47.2324],[38.22955,47.12069],[38.3384,46.98085],[38.12112,46.86078],[37.62608,46.82615],[35.23066,45.79231],[34.96015,45.75634],[34.79905,45.81009],[34.80153,45.90047],[34.75479,45.90705],[34.66679,45.97136],[34.60861,45.99347],[34.55889,45.99347],[34.52011,45.95097],[34.48729,45.94267],[34.44155,45.95995],[34.41221,46.00245],[34.33912,46.06114],[34.25111,46.0532],[34.181,46.06804],[34.12929,46.10494],[34.07311,46.11769],[34.05272,46.10838],[33.91549,46.15938],[33.85234,46.19863],[33.79715,46.20482],[33.74047,46.18555],[33.646,46.23028],[33.61517,46.22615],[33.63854,46.14147],[33.61467,46.13561],[33.57318,46.10317],[33.59087,46.06013],[33.54017,46.0123],[31.62627,45.50633],[32.99857,44.48323],[33.66142,43.9825],[39.81147,43.06294]]],[[[21.46766,55.21115],[21.38446,55.29348],[21.35465,55.28427],[21.26425,55.24456],[20.95181,55.27994],[20.60454,55.40986],[18.57853,55.25302],[19.64312,54.45423],[19.8038,54.44203],[20.63871,54.3706],[21.41123,54.32395],[22.79705,54.36264],[22.7253,54.41732],[22.70208,54.45312],[22.67788,54.532],[22.71293,54.56454],[22.68021,54.58486],[22.7522,54.63525],[22.74225,54.64339],[22.75467,54.6483],[22.73397,54.66604],[22.73631,54.72952],[22.87317,54.79492],[22.85083,54.88711],[22.76422,54.92521],[22.68723,54.9811],[22.65451,54.97037],[22.60075,55.01863],[22.58907,55.07085],[22.47688,55.04408],[22.31562,55.0655],[22.14267,55.05345],[22.11697,55.02131],[22.06087,55.02935],[22.02582,55.05078],[22.03984,55.07888],[21.99543,55.08691],[21.96505,55.07353],[21.85521,55.09493],[21.64954,55.1791],[21.55605,55.20311],[21.51095,55.18507],[21.46766,55.21115]]]]}},{type:"Feature",properties:{iso1A2:"RW",iso1A3:"RWA",iso1N3:"646",wikidata:"Q1037",nameEn:"Rwanda",groups:["014","202","002"],callingCodes:["250"]},geometry:{type:"MultiPolygon",coordinates:[[[[30.47194,-1.0555],[30.35212,-1.06896],[30.16369,-1.34303],[29.912,-1.48269],[29.82657,-1.31187],[29.59061,-1.39016],[29.53062,-1.40499],[29.45038,-1.5054],[29.36322,-1.50887],[29.24323,-1.66826],[29.24458,-1.69663],[29.11847,-1.90576],[29.17562,-2.12278],[29.105,-2.27043],[29.00051,-2.29001],[28.95642,-2.37321],[28.89601,-2.37321],[28.86826,-2.41888],[28.86846,-2.44866],[28.89132,-2.47557],[28.89342,-2.49017],[28.88846,-2.50493],[28.87497,-2.50887],[28.86209,-2.5231],[28.86193,-2.53185],[28.87943,-2.55165],[28.89288,-2.55848],[28.90226,-2.62385],[28.89793,-2.66111],[28.94346,-2.69124],[29.00357,-2.70596],[29.04081,-2.7416],[29.0562,-2.58632],[29.32234,-2.6483],[29.36805,-2.82933],[29.88237,-2.75105],[29.95911,-2.33348],[30.14034,-2.43626],[30.42933,-2.31064],[30.54501,-2.41404],[30.83915,-2.35795],[30.89303,-2.08223],[30.80802,-1.91477],[30.84079,-1.64652],[30.71974,-1.43244],[30.57123,-1.33264],[30.50889,-1.16412],[30.45116,-1.10641],[30.47194,-1.0555]]]]}},{type:"Feature",properties:{iso1A2:"SA",iso1A3:"SAU",iso1N3:"682",wikidata:"Q851",nameEn:"Saudi Arabia",groups:["145","142"],callingCodes:["966"]},geometry:{type:"MultiPolygon",coordinates:[[[[40.01521,32.05667],[39.29903,32.23259],[38.99233,31.99721],[36.99791,31.50081],[37.99354,30.49998],[37.66395,30.33245],[37.4971,29.99949],[36.75083,29.86903],[36.50005,29.49696],[36.07081,29.18469],[34.95987,29.35727],[34.88293,29.37455],[34.46254,27.99552],[34.51305,27.70027],[37.8565,22.00903],[39.63762,18.37348],[41.37609,16.19728],[42.15205,16.40211],[42.76801,16.40371],[42.94625,16.39721],[42.94351,16.49467],[42.97215,16.51093],[43.11601,16.53166],[43.15274,16.67248],[43.22066,16.65179],[43.21325,16.74416],[43.25857,16.75304],[43.26303,16.79479],[43.24801,16.80613],[43.22956,16.80613],[43.22012,16.83932],[43.18338,16.84852],[43.1398,16.90696],[43.19328,16.94703],[43.1813,16.98438],[43.18233,17.02673],[43.23967,17.03428],[43.17787,17.14717],[43.20156,17.25901],[43.32653,17.31179],[43.22533,17.38343],[43.29185,17.53224],[43.43005,17.56148],[43.70631,17.35762],[44.50126,17.47475],[46.31018,17.20464],[46.76494,17.29151],[47.00571,16.94765],[47.48245,17.10808],[47.58351,17.50366],[48.19996,18.20584],[49.04884,18.59899],[52.00311,19.00083],[54.99756,20.00083],[55.66469,21.99658],[55.2137,22.71065],[55.13599,22.63334],[52.56622,22.94341],[51.59617,24.12041],[51.58871,24.27256],[51.41644,24.39615],[51.58834,24.66608],[51.39468,24.62785],[51.29972,24.50747],[51.09638,24.46907],[50.92992,24.54396],[50.8133,24.74049],[50.57069,25.57887],[50.302,25.87592],[50.26923,26.08243],[50.38162,26.53976],[50.71771,26.73086],[50.37726,27.89227],[49.98877,27.87827],[49.00421,28.81495],[48.42991,28.53628],[47.70561,28.5221],[47.59863,28.66798],[47.58376,28.83382],[47.46202,29.0014],[46.5527,29.10283],[46.42415,29.05947],[44.72255,29.19736],[42.97796,30.48295],[42.97601,30.72204],[40.01521,32.05667]]]]}},{type:"Feature",properties:{iso1A2:"SB",iso1A3:"SLB",iso1N3:"090",wikidata:"Q685",nameEn:"Solomon Islands",groups:["054","009"],driveSide:"left",callingCodes:["677"]},geometry:{type:"MultiPolygon",coordinates:[[[[174,-12.72535],[160.43769,-4.17974],[156.03296,-6.55528],[156.03993,-6.65703],[155.92557,-6.84664],[155.69784,-6.92661],[155.60735,-6.92266],[154.74815,-7.33315],[160.04026,-13.08769],[174,-12.72535]]]]}},{type:"Feature",properties:{iso1A2:"SC",iso1A3:"SYC",iso1N3:"690",wikidata:"Q1042",nameEn:"Seychelles",groups:["014","202","002"],driveSide:"left",callingCodes:["248"]},geometry:{type:"MultiPolygon",coordinates:[[[[43.75112,-10.38913],[54.83239,-10.93575],[66.3222,5.65313],[43.75112,-10.38913]]]]}},{type:"Feature",properties:{iso1A2:"SD",iso1A3:"SDN",iso1N3:"729",wikidata:"Q1049",nameEn:"Sudan",groups:["015","002"],callingCodes:["249"]},geometry:{type:"MultiPolygon",coordinates:[[[[37.8565,22.00903],[34.0765,22.00501],[33.99686,21.76784],[33.57251,21.72406],[33.17563,22.00405],[24.99885,21.99535],[24.99794,19.99661],[23.99715,20.00038],[23.99539,19.49944],[23.99997,15.69575],[23.62785,15.7804],[23.38812,15.69649],[23.10792,15.71297],[22.93201,15.55107],[22.92579,15.47007],[22.99584,15.40105],[22.99584,15.22989],[22.66115,14.86308],[22.70474,14.69149],[22.38562,14.58907],[22.44944,14.24986],[22.55997,14.23024],[22.5553,14.11704],[22.22995,13.96754],[22.08674,13.77863],[22.29689,13.3731],[22.1599,13.19281],[22.02914,13.13976],[21.94819,13.05637],[21.81432,12.81362],[21.89371,12.68001],[21.98711,12.63292],[22.15679,12.66634],[22.22684,12.74682],[22.46345,12.61925],[22.38873,12.45514],[22.50548,12.16769],[22.48369,12.02766],[22.64092,12.07485],[22.54907,11.64372],[22.7997,11.40424],[22.93124,11.41645],[22.97249,11.21955],[22.87758,10.91915],[23.02221,10.69235],[23.3128,10.45214],[23.67164,9.86923],[23.69155,9.67566],[24.09319,9.66572],[24.12744,9.73784],[24.49389,9.79962],[24.84653,9.80643],[24.97739,9.9081],[25.05688,10.06776],[25.0918,10.33718],[25.78141,10.42599],[25.93163,10.38159],[25.93241,10.17941],[26.21338,9.91545],[26.35815,9.57946],[26.70685,9.48735],[27.14427,9.62858],[27.90704,9.61323],[28.99983,9.67155],[29.06988,9.74826],[29.53844,9.75133],[29.54,10.07949],[29.94629,10.29245],[30.00389,10.28633],[30.53005,9.95992],[30.82893,9.71451],[30.84605,9.7498],[31.28504,9.75287],[31.77539,10.28939],[31.99177,10.65065],[32.46967,11.04662],[32.39358,11.18207],[32.39578,11.70208],[32.10079,11.95203],[32.73921,11.95203],[32.73921,12.22757],[33.25876,12.22111],[33.13988,11.43248],[33.26977,10.83632],[33.24645,10.77913],[33.52294,10.64382],[33.66604,10.44254],[33.80913,10.32994],[33.90159,10.17179],[33.96984,10.15446],[33.99185,9.99623],[33.96323,9.80972],[33.9082,9.762],[33.87958,9.49937],[34.10229,9.50238],[34.08717,9.55243],[34.13186,9.7492],[34.20484,9.9033],[34.22718,10.02506],[34.32102,10.11599],[34.34783,10.23914],[34.2823,10.53508],[34.4372,10.781],[34.59062,10.89072],[34.77383,10.74588],[34.77532,10.69027],[34.86618,10.74588],[34.86916,10.78832],[34.97491,10.86147],[34.97789,10.91559],[34.93172,10.95946],[35.01215,11.19626],[34.95704,11.24448],[35.09556,11.56278],[35.05832,11.71158],[35.11492,11.85156],[35.24302,11.91132],[35.70476,12.67101],[36.01458,12.72478],[36.14268,12.70879],[36.16651,12.88019],[36.13374,12.92665],[36.24545,13.36759],[36.38993,13.56459],[36.48824,13.83954],[36.44653,13.95666],[36.54376,14.25597],[36.44337,15.14963],[36.54276,15.23478],[36.69761,15.75323],[36.76371,15.80831],[36.92193,16.23451],[36.99777,17.07172],[37.42694,17.04041],[37.50967,17.32199],[38.13362,17.53906],[38.37133,17.66269],[38.45916,17.87167],[38.57727,17.98125],[39.63762,18.37348],[37.8565,22.00903]]]]}},{type:"Feature",properties:{iso1A2:"SE",iso1A3:"SWE",iso1N3:"752",wikidata:"Q34",nameEn:"Sweden",groups:["EU","154","150"],callingCodes:["46"]},geometry:{type:"MultiPolygon",coordinates:[[[[24.15791,65.85385],[23.90497,66.15802],[23.71339,66.21299],[23.64982,66.30603],[23.67591,66.3862],[23.63776,66.43568],[23.85959,66.56434],[23.89488,66.772],[23.98059,66.79585],[23.98563,66.84149],[23.56214,67.17038],[23.58735,67.20752],[23.54701,67.25435],[23.75372,67.29914],[23.75372,67.43688],[23.39577,67.46974],[23.54701,67.59306],[23.45627,67.85297],[23.65793,67.9497],[23.40081,68.05545],[23.26469,68.15134],[23.15377,68.14759],[23.10336,68.26551],[22.73028,68.40881],[22.00429,68.50692],[21.03001,68.88969],[20.90649,68.89696],[20.85104,68.93142],[20.91658,68.96764],[20.78802,69.03087],[20.55258,69.06069],[20.0695,69.04469],[20.28444,68.93283],[20.33435,68.80174],[20.22027,68.67246],[19.95647,68.55546],[20.22027,68.48759],[19.93508,68.35911],[18.97255,68.52416],[18.63032,68.50849],[18.39503,68.58672],[18.1241,68.53721],[18.13836,68.20874],[17.90787,67.96537],[17.30416,68.11591],[16.7409,67.91037],[16.38441,67.52923],[16.12774,67.52106],[16.09922,67.4364],[16.39154,67.21653],[16.35589,67.06419],[15.37197,66.48217],[15.49318,66.28509],[15.05113,66.15572],[14.53778,66.12399],[14.50926,65.31786],[13.64276,64.58402],[14.11117,64.46674],[14.16051,64.18725],[13.98222,64.00953],[13.23411,64.09087],[12.74105,64.02171],[12.14928,63.59373],[12.19919,63.47935],[11.98529,63.27487],[12.19919,63.00104],[12.07085,62.6297],[12.29187,62.25699],[12.14746,61.7147],[12.40595,61.57226],[12.57707,61.56547],[12.86939,61.35427],[12.69115,61.06584],[12.2277,61.02442],[12.59133,60.50559],[12.52003,60.13846],[12.36317,59.99259],[12.15641,59.8926],[11.87121,59.86039],[11.92112,59.69531],[11.69297,59.59442],[11.8213,59.24985],[11.65732,58.90177],[11.45199,58.89604],[11.4601,58.99022],[11.34459,59.11672],[11.15367,59.07862],[11.08911,58.98745],[10.64958,58.89391],[10.40861,58.38489],[12.16597,56.60205],[12.07466,56.29488],[12.65312,56.04345],[12.6372,55.91371],[12.88472,55.63369],[12.60345,55.42675],[12.84405,55.13257],[14.28399,55.1553],[14.89259,55.5623],[15.79951,55.54655],[19.64795,57.06466],[19.84909,57.57876],[20.5104,59.15546],[19.08191,60.19152],[19.23413,60.61414],[20.15877,63.06556],[24.14112,65.39731],[24.15107,65.81427],[24.14798,65.83466],[24.15791,65.85385]]]]}},{type:"Feature",properties:{iso1A2:"SG",iso1A3:"SGP",iso1N3:"702",wikidata:"Q334",nameEn:"Singapore",groups:["035","142"],driveSide:"left",callingCodes:["65"]},geometry:{type:"MultiPolygon",coordinates:[[[[104.00131,1.42405],[103.93384,1.42926],[103.89565,1.42841],[103.86383,1.46288],[103.81181,1.47953],[103.76395,1.45183],[103.74161,1.4502],[103.7219,1.46108],[103.67468,1.43166],[103.62738,1.35255],[103.56591,1.19719],[103.66049,1.18825],[103.74084,1.12902],[104.03085,1.26954],[104.12282,1.27714],[104.08072,1.35998],[104.09162,1.39694],[104.08871,1.42015],[104.07348,1.43322],[104.04622,1.44691],[104.02277,1.4438],[104.00131,1.42405]]]]}},{type:"Feature",properties:{iso1A2:"SH",iso1A3:"SHN",iso1N3:"654",wikidata:"Q34497",nameEn:"Saint Helena, Ascension and Tristan da Cunha",country:"GB",groups:["011","202","002"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["290"]},geometry:{type:"MultiPolygon",coordinates:[[[[-14.82771,-8.70814],[-13.48367,-36.6746],[-11.55782,-36.60319],[-11.48092,-37.8367],[-13.41694,-37.88844],[-13.29655,-40.02846],[-9.34669,-41.00353],[-4.97086,-15.55882],[-13.33271,-8.07391],[-14.82771,-8.70814]]]]}},{type:"Feature",properties:{iso1A2:"SI",iso1A3:"SVN",iso1N3:"705",wikidata:"Q215",nameEn:"Slovenia",groups:["EU","039","150"],callingCodes:["386"]},geometry:{type:"MultiPolygon",coordinates:[[[[16.50139,46.56684],[16.39217,46.63673],[16.38594,46.6549],[16.41863,46.66238],[16.42641,46.69228],[16.37816,46.69975],[16.30966,46.7787],[16.31303,46.79838],[16.3408,46.80641],[16.34547,46.83836],[16.2941,46.87137],[16.2365,46.87775],[16.21892,46.86961],[16.15711,46.85434],[16.14365,46.8547],[16.10983,46.867],[16.05786,46.83927],[15.99054,46.82772],[15.99126,46.78199],[15.98432,46.74991],[15.99769,46.7266],[16.02808,46.71094],[16.04347,46.68694],[16.04036,46.6549],[15.99988,46.67947],[15.98512,46.68463],[15.94864,46.68769],[15.87691,46.7211],[15.8162,46.71897],[15.78518,46.70712],[15.76771,46.69863],[15.73823,46.70011],[15.72279,46.69548],[15.69523,46.69823],[15.67411,46.70735],[15.6543,46.70616],[15.6543,46.69228],[15.6365,46.6894],[15.63255,46.68069],[15.62317,46.67947],[15.59826,46.68908],[15.54533,46.66985],[15.55333,46.64988],[15.54431,46.6312],[15.46906,46.61321],[15.45514,46.63697],[15.41235,46.65556],[15.23711,46.63994],[15.14215,46.66131],[15.01451,46.641],[14.98024,46.6009],[14.96002,46.63459],[14.92283,46.60848],[14.87129,46.61],[14.86419,46.59411],[14.83549,46.56614],[14.81836,46.51046],[14.72185,46.49974],[14.66892,46.44936],[14.5942,46.43434],[14.56463,46.37208],[14.52176,46.42617],[14.45877,46.41717],[14.42608,46.44614],[14.314,46.43327],[14.28326,46.44315],[14.15989,46.43327],[14.12097,46.47724],[14.04002,46.49117],[14.00422,46.48474],[13.89837,46.52331],[13.7148,46.5222],[13.68684,46.43881],[13.59777,46.44137],[13.5763,46.42613],[13.5763,46.40915],[13.47019,46.3621],[13.43418,46.35992],[13.44808,46.33507],[13.37671,46.29668],[13.42218,46.20758],[13.47587,46.22725],[13.56114,46.2054],[13.56682,46.18703],[13.64451,46.18966],[13.66472,46.17392],[13.64053,46.13587],[13.57072,46.09022],[13.50104,46.05986],[13.49568,46.04839],[13.50998,46.04498],[13.49702,46.01832],[13.47474,46.00546],[13.50104,45.98078],[13.52963,45.96588],[13.56759,45.96991],[13.58903,45.99009],[13.62074,45.98388],[13.63458,45.98947],[13.64307,45.98326],[13.6329,45.94894],[13.63815,45.93607],[13.61931,45.91782],[13.60857,45.89907],[13.59565,45.89446],[13.58644,45.88173],[13.57563,45.8425],[13.58858,45.83503],[13.59784,45.8072],[13.66986,45.79955],[13.8235,45.7176],[13.83332,45.70855],[13.83422,45.68703],[13.87933,45.65207],[13.9191,45.6322],[13.8695,45.60835],[13.86771,45.59898],[13.84106,45.58185],[13.78445,45.5825],[13.74587,45.59811],[13.7198,45.59352],[13.6076,45.64761],[13.45644,45.59464],[13.56979,45.4895],[13.62902,45.45898],[13.67398,45.4436],[13.7785,45.46787],[13.81742,45.43729],[13.88124,45.42637],[13.90771,45.45149],[13.97309,45.45258],[13.99488,45.47551],[13.96063,45.50825],[14.00578,45.52352],[14.07116,45.48752],[14.20348,45.46896],[14.22371,45.50388],[14.24239,45.50607],[14.26611,45.48239],[14.27681,45.4902],[14.32487,45.47142],[14.36693,45.48642],[14.49769,45.54424],[14.5008,45.60852],[14.53816,45.6205],[14.57397,45.67165],[14.60977,45.66403],[14.59576,45.62812],[14.69694,45.57366],[14.68605,45.53006],[14.71718,45.53442],[14.80124,45.49515],[14.81992,45.45913],[14.90554,45.47769],[14.92266,45.52788],[15.02385,45.48533],[15.05187,45.49079],[15.16862,45.42309],[15.27758,45.46678],[15.33051,45.45258],[15.38188,45.48752],[15.30249,45.53224],[15.29837,45.5841],[15.27747,45.60504],[15.31027,45.6303],[15.34695,45.63382],[15.34214,45.64702],[15.38952,45.63682],[15.4057,45.64727],[15.34919,45.71623],[15.30872,45.69014],[15.25423,45.72275],[15.40836,45.79491],[15.47531,45.79802],[15.47325,45.8253],[15.52234,45.82195],[15.57952,45.84953],[15.64185,45.82915],[15.66662,45.84085],[15.70411,45.8465],[15.68232,45.86819],[15.68383,45.88867],[15.67967,45.90455],[15.70636,45.92116],[15.70327,46.00015],[15.71246,46.01196],[15.72977,46.04682],[15.62317,46.09103],[15.6083,46.11992],[15.59909,46.14761],[15.64904,46.19229],[15.6434,46.21396],[15.67395,46.22478],[15.75436,46.21969],[15.75479,46.20336],[15.78817,46.21719],[15.79284,46.25811],[15.97965,46.30652],[16.07616,46.3463],[16.07314,46.36458],[16.05065,46.3833],[16.05281,46.39141],[16.14859,46.40547],[16.18824,46.38282],[16.30233,46.37837],[16.30162,46.40437],[16.27329,46.41467],[16.27398,46.42875],[16.25124,46.48067],[16.23961,46.49653],[16.26759,46.50566],[16.26733,46.51505],[16.29793,46.5121],[16.37193,46.55008],[16.38771,46.53608],[16.44036,46.5171],[16.5007,46.49644],[16.52604,46.47831],[16.59527,46.47524],[16.52604,46.5051],[16.52885,46.53303],[16.50139,46.56684]]]]}},{type:"Feature",properties:{iso1A2:"SJ",iso1A3:"SJM",iso1N3:"744",wikidata:"Q842829",nameEn:"Svalbard and Jan Mayen",country:"NO",groups:["154","150"],callingCodes:["47 79"]},geometry:{type:"MultiPolygon",coordinates:[[[[-7.49892,77.24208],[32.07813,72.01005],[36.85549,84.09565],[-7.49892,77.24208]]],[[[-9.18243,72.23144],[-10.71459,70.09565],[-5.93364,70.76368],[-9.18243,72.23144]]]]}},{type:"Feature",properties:{iso1A2:"SK",iso1A3:"SVK",iso1N3:"703",wikidata:"Q214",nameEn:"Slovakia",groups:["EU","151","150"],callingCodes:["421"]},geometry:{type:"MultiPolygon",coordinates:[[[[19.82237,49.27806],[19.78581,49.41701],[19.72127,49.39288],[19.6375,49.40897],[19.64162,49.45184],[19.57845,49.46077],[19.53313,49.52856],[19.52626,49.57311],[19.45348,49.61583],[19.37795,49.574],[19.36009,49.53747],[19.25435,49.53391],[19.18019,49.41165],[18.9742,49.39557],[18.97283,49.49914],[18.94536,49.52143],[18.84521,49.51672],[18.74761,49.492],[18.67757,49.50895],[18.6144,49.49824],[18.57183,49.51162],[18.53063,49.49022],[18.54848,49.47059],[18.44686,49.39467],[18.4084,49.40003],[18.4139,49.36517],[18.36446,49.3267],[18.18456,49.28909],[18.15022,49.24518],[18.1104,49.08624],[18.06885,49.03157],[17.91814,49.01784],[17.87831,48.92679],[17.77944,48.92318],[17.73126,48.87885],[17.7094,48.86721],[17.5295,48.81117],[17.45671,48.85004],[17.3853,48.80936],[17.29054,48.85546],[17.19355,48.87602],[17.11202,48.82925],[17.00215,48.70887],[16.93955,48.60371],[16.94611,48.53614],[16.85204,48.44968],[16.8497,48.38321],[16.83588,48.3844],[16.83317,48.38138],[16.84243,48.35258],[16.90903,48.32519],[16.89461,48.31332],[16.97701,48.17385],[17.02919,48.13996],[17.05735,48.14179],[17.09168,48.09366],[17.07039,48.0317],[17.16001,48.00636],[17.23699,48.02094],[17.71215,47.7548],[18.02938,47.75665],[18.29305,47.73541],[18.56496,47.76588],[18.66521,47.76772],[18.74074,47.8157],[18.8506,47.82308],[18.76821,47.87469],[18.76134,47.97499],[18.82176,48.04206],[19.01952,48.07052],[19.23924,48.0595],[19.28182,48.08336],[19.47957,48.09437],[19.52489,48.19791],[19.63338,48.25006],[19.92452,48.1283],[20.24312,48.2784],[20.29943,48.26104],[20.5215,48.53336],[20.83248,48.5824],[21.11516,48.49546],[21.44063,48.58456],[21.6068,48.50365],[21.67134,48.3989],[21.72525,48.34628],[21.8279,48.33321],[21.83339,48.36242],[22.14689,48.4005],[22.16023,48.56548],[22.21379,48.6218],[22.34151,48.68893],[22.42934,48.92857],[22.48296,48.99172],[22.54338,49.01424],[22.56155,49.08865],[22.04427,49.22136],[21.96385,49.3437],[21.82927,49.39467],[21.77983,49.35443],[21.62328,49.4447],[21.43376,49.41433],[21.27858,49.45988],[21.19756,49.4054],[21.12477,49.43666],[21.041,49.41791],[21.09799,49.37176],[20.98733,49.30774],[20.9229,49.29626],[20.77971,49.35383],[20.72274,49.41813],[20.61666,49.41791],[20.5631,49.375],[20.46422,49.41612],[20.39939,49.3896],[20.31728,49.39914],[20.31453,49.34817],[20.21977,49.35265],[20.13738,49.31685],[20.08238,49.1813],[19.98494,49.22904],[19.90529,49.23532],[19.86409,49.19316],[19.75286,49.20751],[19.82237,49.27806]]]]}},{type:"Feature",properties:{iso1A2:"SL",iso1A3:"SLE",iso1N3:"694",wikidata:"Q1044",nameEn:"Sierra Leone",groups:["011","202","002"],callingCodes:["232"]},geometry:{type:"MultiPolygon",coordinates:[[[[-10.27575,8.48711],[-10.37257,8.48941],[-10.54891,8.31174],[-10.63934,8.35326],[-10.70565,8.29235],[-10.61422,8.5314],[-10.47707,8.67669],[-10.56197,8.81225],[-10.5783,9.06386],[-10.74484,9.07998],[-10.6534,9.29919],[-11.2118,10.00098],[-11.89624,9.99763],[-11.91023,9.93927],[-12.12634,9.87203],[-12.24262,9.92386],[-12.47254,9.86834],[-12.76788,9.3133],[-12.94095,9.26335],[-13.08953,9.0409],[-13.18586,9.0925],[-13.29911,9.04245],[-14.36218,8.64107],[-12.15048,6.15992],[-11.50429,6.92704],[-11.4027,6.97746],[-11.29417,7.21576],[-10.60422,7.7739],[-10.60492,8.04072],[-10.57523,8.04829],[-10.51554,8.1393],[-10.45023,8.15627],[-10.35227,8.15223],[-10.29839,8.21283],[-10.31635,8.28554],[-10.30084,8.30008],[-10.27575,8.48711]]]]}},{type:"Feature",properties:{iso1A2:"SM",iso1A3:"SMR",iso1N3:"674",wikidata:"Q238",nameEn:"San Marino",groups:["039","150"],callingCodes:["378"]},geometry:{type:"MultiPolygon",coordinates:[[[[12.45648,43.89369],[12.48771,43.89706],[12.49429,43.90973],[12.49247,43.91774],[12.49724,43.92248],[12.50269,43.92363],[12.50496,43.93017],[12.51553,43.94096],[12.51427,43.94897],[12.50655,43.95796],[12.50875,43.96198],[12.50622,43.97131],[12.51109,43.97201],[12.51064,43.98165],[12.5154,43.98508],[12.51463,43.99122],[12.50678,43.99113],[12.49406,43.98492],[12.47853,43.98052],[12.46205,43.97463],[12.44684,43.96597],[12.43662,43.95698],[12.42005,43.9578],[12.41414,43.95273],[12.40415,43.95485],[12.40506,43.94325],[12.41165,43.93769],[12.41551,43.92984],[12.40733,43.92379],[12.41233,43.90956],[12.40935,43.9024],[12.41641,43.89991],[12.44184,43.90498],[12.45648,43.89369]]]]}},{type:"Feature",properties:{iso1A2:"SN",iso1A3:"SEN",iso1N3:"686",wikidata:"Q1041",nameEn:"Senegal",groups:["011","202","002"],callingCodes:["221"]},geometry:{type:"MultiPolygon",coordinates:[[[[-14.32144,16.61495],[-15.00557,16.64997],[-15.6509,16.50315],[-16.27016,16.51565],[-16.4429,16.20605],[-16.44814,16.09753],[-16.48967,16.0496],[-16.50854,16.09032],[-17.15288,16.07139],[-18.35085,14.63444],[-17.43598,13.59273],[-15.47902,13.58758],[-15.36504,13.79313],[-14.93719,13.80173],[-14.34721,13.46578],[-13.8955,13.59126],[-13.79409,13.34472],[-14.36795,13.23033],[-15.14917,13.57989],[-15.26908,13.37768],[-15.80478,13.34832],[-15.80355,13.16729],[-16.69343,13.16791],[-16.74676,13.06025],[-17.43966,13.04579],[-17.4623,11.92379],[-16.70562,12.34803],[-16.38191,12.36449],[-16.20591,12.46157],[-15.67302,12.42974],[-15.17582,12.6847],[-13.70523,12.68013],[-13.05296,12.64003],[-13.06603,12.49342],[-12.87336,12.51892],[-12.35415,12.32758],[-11.91331,12.42008],[-11.46267,12.44559],[-11.37536,12.40788],[-11.39935,12.97808],[-11.63025,13.39174],[-11.83345,13.33333],[-12.06897,13.71049],[-11.93043,13.84505],[-12.23936,14.76324],[-13.11029,15.52116],[-13.43135,16.09022],[-13.80075,16.13961],[-14.32144,16.61495]]]]}},{type:"Feature",properties:{iso1A2:"SO",iso1A3:"SOM",iso1N3:"706",wikidata:"Q1045",nameEn:"Somalia",groups:["014","202","002"],callingCodes:["252"]},geometry:{type:"MultiPolygon",coordinates:[[[[48.95249,11.56816],[43.42425,11.70983],[42.95776,10.98533],[42.69452,10.62672],[42.87643,10.18441],[43.0937,9.90579],[43.23518,9.84605],[43.32613,9.59205],[44.19222,8.93028],[46.99339,7.9989],[47.92477,8.00111],[47.97917,8.00124],[44.98104,4.91821],[44.02436,4.9451],[43.40263,4.79289],[43.04177,4.57923],[42.97746,4.44032],[42.84526,4.28357],[42.55853,4.20518],[42.07619,4.17667],[41.89488,3.97375],[41.31368,3.14314],[40.98767,2.82959],[41.00099,-0.83068],[41.56,-1.59812],[41.56362,-1.66375],[41.75542,-1.85308],[49.16337,2.78611],[52.253,11.68582],[51.12877,12.56479],[48.95249,11.56816]]]]}},{type:"Feature",properties:{iso1A2:"SR",iso1A3:"SUR",iso1N3:"740",wikidata:"Q730",nameEn:"Suriname",groups:["005","419","019"],driveSide:"left",callingCodes:["597"]},geometry:{type:"MultiPolygon",coordinates:[[[[-54.26916,5.26909],[-54.01877,5.52789],[-54.01074,5.68785],[-53.7094,6.2264],[-56.84822,6.73257],[-57.31629,5.33714],[-57.22536,5.15605],[-57.37442,5.0208],[-57.8699,4.89394],[-58.0307,3.95513],[-57.35891,3.32121],[-56.70519,2.02964],[-56.55439,2.02003],[-56.47045,1.95135],[-55.99278,1.83137],[-55.89863,1.89861],[-55.92159,2.05236],[-56.13054,2.27723],[-55.96292,2.53188],[-55.71493,2.40342],[-55.01919,2.564],[-54.6084,2.32856],[-54.42864,2.42442],[-54.28534,2.67798],[-53.9849,3.58697],[-53.98914,3.627],[-54.05128,3.63557],[-54.19367,3.84387],[-54.38444,4.13222],[-54.4717,4.91964],[-54.26916,5.26909]]]]}},{type:"Feature",properties:{iso1A2:"SS",iso1A3:"SSD",iso1N3:"728",wikidata:"Q958",nameEn:"South Sudan",groups:["014","202","002"],callingCodes:["211"]},geometry:{type:"MultiPolygon",coordinates:[[[[34.10229,9.50238],[33.87958,9.49937],[33.9082,9.762],[33.96323,9.80972],[33.99185,9.99623],[33.96984,10.15446],[33.90159,10.17179],[33.80913,10.32994],[33.66604,10.44254],[33.52294,10.64382],[33.24645,10.77913],[33.26977,10.83632],[33.13988,11.43248],[33.25876,12.22111],[32.73921,12.22757],[32.73921,11.95203],[32.10079,11.95203],[32.39578,11.70208],[32.39358,11.18207],[32.46967,11.04662],[31.99177,10.65065],[31.77539,10.28939],[31.28504,9.75287],[30.84605,9.7498],[30.82893,9.71451],[30.53005,9.95992],[30.00389,10.28633],[29.94629,10.29245],[29.54,10.07949],[29.53844,9.75133],[29.06988,9.74826],[28.99983,9.67155],[27.90704,9.61323],[27.14427,9.62858],[26.70685,9.48735],[26.35815,9.57946],[26.21338,9.91545],[25.93241,10.17941],[25.93163,10.38159],[25.78141,10.42599],[25.0918,10.33718],[25.05688,10.06776],[24.97739,9.9081],[24.84653,9.80643],[24.49389,9.79962],[24.12744,9.73784],[24.09319,9.66572],[23.69155,9.67566],[23.62179,9.53823],[23.64981,9.44303],[23.64358,9.28637],[23.56263,9.19418],[23.4848,9.16959],[23.44744,8.99128],[23.59065,8.99743],[23.51905,8.71749],[24.25691,8.69288],[24.13238,8.36959],[24.35965,8.26177],[24.85156,8.16933],[24.98855,7.96588],[25.25319,7.8487],[25.29214,7.66675],[25.20649,7.61115],[25.20337,7.50312],[25.35281,7.42595],[25.37461,7.33024],[25.90076,7.09549],[26.38022,6.63493],[26.32729,6.36272],[26.58259,6.1987],[26.51721,6.09655],[27.22705,5.71254],[27.22705,5.62889],[27.28621,5.56382],[27.23017,5.37167],[27.26886,5.25876],[27.44012,5.07349],[27.56656,4.89375],[27.65462,4.89375],[27.76469,4.79284],[27.79551,4.59976],[28.20719,4.35614],[28.6651,4.42638],[28.8126,4.48784],[29.03054,4.48784],[29.22207,4.34297],[29.43341,4.50101],[29.49726,4.7007],[29.82087,4.56246],[29.79666,4.37809],[30.06964,4.13221],[30.1621,4.10586],[30.22374,3.93896],[30.27658,3.95653],[30.47691,3.83353],[30.55396,3.84451],[30.57378,3.74567],[30.56277,3.62703],[30.78512,3.67097],[30.80713,3.60506],[30.85997,3.5743],[30.85153,3.48867],[30.97601,3.693],[31.16666,3.79853],[31.29476,3.8015],[31.50478,3.67814],[31.50776,3.63652],[31.72075,3.74354],[31.81459,3.82083],[31.86821,3.78664],[31.96205,3.6499],[31.95907,3.57408],[32.05187,3.589],[32.08491,3.56287],[32.08866,3.53543],[32.19888,3.50867],[32.20782,3.6053],[32.41337,3.748],[32.72021,3.77327],[32.89746,3.81339],[33.02852,3.89296],[33.18356,3.77812],[33.51264,3.75068],[33.9873,4.23316],[34.47601,4.72162],[35.34151,5.02364],[35.30992,4.90402],[35.47843,4.91872],[35.42366,4.76969],[35.51424,4.61643],[35.9419,4.61933],[35.82118,4.77382],[35.81968,5.10757],[35.8576,5.33413],[35.50792,5.42431],[35.29938,5.34042],[35.31188,5.50106],[35.13058,5.62118],[35.12611,5.68937],[35.00546,5.89387],[34.96227,6.26415],[35.01738,6.46991],[34.87736,6.60161],[34.77459,6.5957],[34.65096,6.72589],[34.53776,6.74808],[34.53925,6.82794],[34.47669,6.91076],[34.35753,6.91963],[34.19369,7.04382],[34.19369,7.12807],[34.01495,7.25664],[34.03878,7.27437],[34.02984,7.36449],[33.87642,7.5491],[33.71407,7.65983],[33.44745,7.7543],[33.32531,7.71297],[33.24637,7.77939],[33.04944,7.78989],[33.0006,7.90333],[33.08401,8.05822],[33.18083,8.13047],[33.1853,8.29264],[33.19721,8.40317],[33.3119,8.45474],[33.54575,8.47094],[33.66938,8.44442],[33.71407,8.3678],[33.87195,8.41938],[33.89579,8.4842],[34.01346,8.50041],[34.14453,8.60204],[34.14304,9.04654],[34.10229,9.50238]]]]}},{type:"Feature",properties:{iso1A2:"ST",iso1A3:"STP",iso1N3:"678",wikidata:"Q1039",nameEn:"São Tomé and Principe",groups:["017","202","002"],callingCodes:["239"]},geometry:{type:"MultiPolygon",coordinates:[[[[5.9107,-0.09539],[6.69416,-0.53945],[8.0168,1.79377],[7.23334,2.23756],[5.9107,-0.09539]]]]}},{type:"Feature",properties:{iso1A2:"SV",iso1A3:"SLV",iso1N3:"222",wikidata:"Q792",nameEn:"El Salvador",groups:["013","003","419","019"],callingCodes:["503"]},geometry:{type:"MultiPolygon",coordinates:[[[[-89.34776,14.43013],[-89.39028,14.44561],[-89.57441,14.41637],[-89.58814,14.33165],[-89.50614,14.26084],[-89.52397,14.22628],[-89.61844,14.21937],[-89.70756,14.1537],[-89.75569,14.07073],[-89.73251,14.04133],[-89.76103,14.02923],[-89.81807,14.07073],[-89.88937,14.0396],[-90.10505,13.85104],[-90.11344,13.73679],[-90.55276,12.8866],[-88.11443,12.63306],[-87.7346,13.13228],[-87.55124,13.12523],[-87.69751,13.25228],[-87.73714,13.32715],[-87.80177,13.35689],[-87.84675,13.41078],[-87.83467,13.44655],[-87.77354,13.45767],[-87.73841,13.44169],[-87.72115,13.46083],[-87.71657,13.50577],[-87.78148,13.52906],[-87.73106,13.75443],[-87.68821,13.80829],[-87.7966,13.91353],[-88.00331,13.86948],[-88.07641,13.98447],[-88.23018,13.99915],[-88.25791,13.91108],[-88.48982,13.86458],[-88.49738,13.97224],[-88.70661,14.04317],[-88.73182,14.10919],[-88.815,14.11652],[-88.85785,14.17763],[-88.94608,14.20207],[-89.04187,14.33644],[-89.34776,14.43013]]]]}},{type:"Feature",properties:{iso1A2:"SX",iso1A3:"SXM",iso1N3:"534",wikidata:"Q26273",nameEn:"Sint Maarten",country:"NL",groups:["029","003","419","019"],callingCodes:["1 721"]},geometry:{type:"MultiPolygon",coordinates:[[[[-63.29212,17.90532],[-63.07669,17.79659],[-62.93924,18.02904],[-63.02323,18.05757],[-63.04039,18.05619],[-63.0579,18.06614],[-63.07759,18.04943],[-63.09686,18.04608],[-63.11096,18.05368],[-63.13584,18.0541],[-63.33064,17.9615],[-63.29212,17.90532]]]]}},{type:"Feature",properties:{iso1A2:"SY",iso1A3:"SYR",iso1N3:"760",wikidata:"Q858",nameEn:"Syria",groups:["145","142"],callingCodes:["963"]},geometry:{type:"MultiPolygon",coordinates:[[[[42.23683,37.2863],[42.21548,37.28026],[42.20454,37.28715],[42.22381,37.30238],[42.22257,37.31395],[42.2112,37.32491],[42.19301,37.31323],[42.18225,37.28569],[42.00894,37.17209],[41.515,37.08084],[41.21937,37.07665],[40.90856,37.13147],[40.69136,37.0996],[39.81589,36.75538],[39.21538,36.66834],[39.03217,36.70911],[38.74042,36.70629],[38.55908,36.84429],[38.38859,36.90064],[38.21064,36.91842],[37.81974,36.76055],[37.68048,36.75065],[37.49103,36.66904],[37.47253,36.63243],[37.21988,36.6736],[37.16177,36.66069],[37.10894,36.6704],[37.08279,36.63495],[37.02088,36.66422],[37.01647,36.69512],[37.04619,36.71101],[37.04399,36.73483],[36.99886,36.74012],[36.99557,36.75997],[36.66727,36.82901],[36.61581,36.74629],[36.62681,36.71189],[36.57398,36.65186],[36.58829,36.58295],[36.54206,36.49539],[36.6081,36.33772],[36.65653,36.33861],[36.68672,36.23677],[36.6125,36.22592],[36.50463,36.2419],[36.4617,36.20461],[36.39206,36.22088],[36.37474,36.01163],[36.33956,35.98687],[36.30099,36.00985],[36.28338,36.00273],[36.29769,35.96086],[36.27678,35.94839],[36.25366,35.96264],[36.19973,35.95195],[36.17441,35.92076],[36.1623,35.80925],[36.14029,35.81015],[36.13919,35.83692],[36.11827,35.85923],[35.99829,35.88242],[36.01844,35.92403],[36.00514,35.94113],[35.98499,35.94107],[35.931,35.92109],[35.51152,36.10954],[35.48515,34.70851],[35.97386,34.63322],[35.98718,34.64977],[36.29165,34.62991],[36.32399,34.69334],[36.35135,34.68516],[36.35384,34.65447],[36.42941,34.62505],[36.46003,34.6378],[36.45299,34.59438],[36.41429,34.61175],[36.39846,34.55672],[36.3369,34.52629],[36.34745,34.5002],[36.4442,34.50165],[36.46179,34.46541],[36.55853,34.41609],[36.53039,34.3798],[36.56556,34.31881],[36.60778,34.31009],[36.58667,34.27667],[36.59195,34.2316],[36.62537,34.20251],[36.5128,34.09916],[36.50576,34.05982],[36.41078,34.05253],[36.28589,33.91981],[36.38263,33.86579],[36.3967,33.83365],[36.14517,33.85118],[36.06778,33.82927],[35.9341,33.6596],[36.05723,33.57904],[35.94465,33.52774],[35.94816,33.47886],[35.88668,33.43183],[35.82577,33.40479],[35.81324,33.36354],[35.77477,33.33609],[35.813,33.3172],[35.77513,33.27342],[35.81295,33.24841],[35.81647,33.2028],[35.83846,33.19397],[35.84285,33.16673],[35.81911,33.1336],[35.81911,33.11077],[35.84802,33.1031],[35.87188,32.98028],[35.89298,32.9456],[35.87012,32.91976],[35.84021,32.8725],[35.83758,32.82817],[35.78745,32.77938],[35.75983,32.74803],[35.88405,32.71321],[35.93307,32.71966],[35.96633,32.66237],[36.02239,32.65911],[36.08074,32.51463],[36.20379,32.52751],[36.20875,32.49529],[36.23948,32.50108],[36.40959,32.37908],[36.83946,32.31293],[38.79171,33.37328],[40.64314,34.31604],[40.97676,34.39788],[41.12388,34.65742],[41.2345,34.80049],[41.21654,35.1508],[41.26569,35.42708],[41.38184,35.62502],[41.37027,35.84095],[41.2564,36.06012],[41.28864,36.35368],[41.40058,36.52502],[41.81736,36.58782],[42.36697,37.0627],[42.35724,37.10998],[42.32313,37.17814],[42.34735,37.22548],[42.2824,37.2798],[42.26039,37.27017],[42.23683,37.2863]]]]}},{type:"Feature",properties:{iso1A2:"SZ",iso1A3:"SWZ",iso1N3:"748",wikidata:"Q1050",nameEn:"Eswatini",aliases:["Swaziland"],groups:["018","202","002"],driveSide:"left",callingCodes:["268"]},geometry:{type:"MultiPolygon",coordinates:[[[[31.86881,-25.99973],[31.4175,-25.71886],[31.31237,-25.7431],[31.13073,-25.91558],[30.95819,-26.26303],[30.78927,-26.48271],[30.81101,-26.84722],[30.88826,-26.79622],[30.97757,-26.92706],[30.96088,-27.0245],[31.15027,-27.20151],[31.49834,-27.31549],[31.97592,-27.31675],[31.97463,-27.11057],[32.00893,-26.8096],[32.09664,-26.80721],[32.13315,-26.84345],[32.13409,-26.5317],[32.07352,-26.40185],[32.10435,-26.15656],[32.08599,-26.00978],[32.00916,-25.999],[31.974,-25.95387],[31.86881,-25.99973]]]]}},{type:"Feature",properties:{iso1A2:"TA",iso1A3:"TAA",wikidata:"Q220982",nameEn:"Tristan da Cunha",country:"GB",groups:["SH","011","202","002"],isoStatus:"excRes",driveSide:"left",roadSpeedUnit:"mph",callingCodes:["290 8","44 20"]},geometry:{type:"MultiPolygon",coordinates:[[[[-13.48367,-36.6746],[-13.41694,-37.88844],[-11.48092,-37.8367],[-11.55782,-36.60319],[-13.48367,-36.6746]]]]}},{type:"Feature",properties:{iso1A2:"TC",iso1A3:"TCA",iso1N3:"796",wikidata:"Q18221",nameEn:"Turks and Caicos Islands",country:"GB",groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 649"]},geometry:{type:"MultiPolygon",coordinates:[[[[-72.41726,22.40371],[-72.72017,21.48055],[-71.46138,20.64433],[-70.63262,21.53631],[-72.41726,22.40371]]]]}},{type:"Feature",properties:{iso1A2:"TD",iso1A3:"TCD",iso1N3:"148",wikidata:"Q657",nameEn:"Chad",groups:["017","202","002"],callingCodes:["235"]},geometry:{type:"MultiPolygon",coordinates:[[[[23.99539,19.49944],[15.99566,23.49639],[14.99751,23.00539],[15.19692,21.99339],[15.20213,21.49365],[15.28332,21.44557],[15.62515,20.95395],[15.57248,20.92138],[15.55382,20.86507],[15.56004,20.79488],[15.59841,20.74039],[15.6721,20.70069],[15.99632,20.35364],[15.75098,19.93002],[15.6032,18.77402],[15.50373,16.89649],[14.37425,15.72591],[13.86301,15.04043],[13.78991,14.87519],[13.809,14.72915],[13.67878,14.64013],[13.68573,14.55276],[13.48259,14.46704],[13.47559,14.40881],[13.6302,13.71094],[14.08251,13.0797],[14.46881,13.08259],[14.56101,12.91036],[14.55058,12.78256],[14.83314,12.62963],[14.90827,12.3269],[14.89019,12.16593],[14.96952,12.0925],[15.00146,12.1223],[15.0349,12.10698],[15.05786,12.0608],[15.04808,11.8731],[15.11579,11.79313],[15.06595,11.71126],[15.13149,11.5537],[15.0585,11.40481],[15.10021,11.04101],[15.04957,11.02347],[15.09127,10.87431],[15.06737,10.80921],[15.15532,10.62846],[15.14936,10.53915],[15.23724,10.47764],[15.30874,10.31063],[15.50535,10.1098],[15.68761,9.99344],[15.41408,9.92876],[15.24618,9.99246],[15.14043,9.99246],[15.05999,9.94845],[14.95722,9.97926],[14.80082,9.93818],[14.4673,10.00264],[14.20411,10.00055],[14.1317,9.82413],[14.01793,9.73169],[13.97544,9.6365],[14.37094,9.2954],[14.35707,9.19611],[14.83566,8.80557],[15.09484,8.65982],[15.20426,8.50892],[15.50743,7.79302],[15.59272,7.7696],[15.56964,7.58936],[15.49743,7.52179],[15.73118,7.52006],[15.79942,7.44149],[16.40703,7.68809],[16.41583,7.77971],[16.58315,7.88657],[16.59415,7.76444],[16.658,7.75353],[16.6668,7.67281],[16.8143,7.53971],[17.67288,7.98905],[17.93926,7.95853],[18.02731,8.01085],[18.6085,8.05009],[18.64153,8.08714],[18.62612,8.14163],[18.67455,8.22226],[18.79783,8.25929],[19.11044,8.68172],[18.86388,8.87971],[19.06421,9.00367],[20.36748,9.11019],[20.82979,9.44696],[21.26348,9.97642],[21.34934,9.95907],[21.52766,10.2105],[21.63553,10.217],[21.71479,10.29932],[21.72139,10.64136],[22.45889,11.00246],[22.87758,10.91915],[22.97249,11.21955],[22.93124,11.41645],[22.7997,11.40424],[22.54907,11.64372],[22.64092,12.07485],[22.48369,12.02766],[22.50548,12.16769],[22.38873,12.45514],[22.46345,12.61925],[22.22684,12.74682],[22.15679,12.66634],[21.98711,12.63292],[21.89371,12.68001],[21.81432,12.81362],[21.94819,13.05637],[22.02914,13.13976],[22.1599,13.19281],[22.29689,13.3731],[22.08674,13.77863],[22.22995,13.96754],[22.5553,14.11704],[22.55997,14.23024],[22.44944,14.24986],[22.38562,14.58907],[22.70474,14.69149],[22.66115,14.86308],[22.99584,15.22989],[22.99584,15.40105],[22.92579,15.47007],[22.93201,15.55107],[23.10792,15.71297],[23.38812,15.69649],[23.62785,15.7804],[23.99997,15.69575],[23.99539,19.49944]]]]}},{type:"Feature",properties:{iso1A2:"TF",iso1A3:"ATF",iso1N3:"260",wikidata:"Q129003",nameEn:"French Southern and Antarctic Lands",country:"FR",groups:["014","202","002"]},geometry:{type:"MultiPolygon",coordinates:[[[[53.53458,-16.36909],[54.96649,-16.28353],[54.61476,-15.02273],[53.53458,-16.36909]]],[[[39.10324,-21.48967],[40.40841,-23.17181],[43.72277,-16.09877],[41.06663,-17.08802],[39.10324,-21.48967]]],[[[46.52682,-10.83678],[47.29063,-12.45583],[48.86266,-10.8109],[46.52682,-10.83678]]],[[[80.15867,-36.04977],[46.31615,-46.28749],[70.67507,-51.14192],[80.15867,-36.04977]]]]}},{type:"Feature",properties:{iso1A2:"TG",iso1A3:"TGO",iso1N3:"768",wikidata:"Q945",nameEn:"Togo",groups:["011","202","002"],callingCodes:["228"]},geometry:{type:"MultiPolygon",coordinates:[[[[0.50388,11.01011],[-0.13493,11.14075],[-0.14462,11.10811],[-0.05733,11.08628],[-0.0275,11.11202],[-0.00514,11.10763],[0.00342,11.08317],[0.02395,11.06229],[0.03355,10.9807],[-0.0063,10.96417],[-0.00908,10.91644],[-0.02685,10.8783],[-0.0228,10.81916],[-0.07183,10.76794],[-0.07327,10.71845],[-0.09141,10.7147],[-0.05945,10.63458],[0.12886,10.53149],[0.18846,10.4096],[0.29453,10.41546],[0.33028,10.30408],[0.39584,10.31112],[0.35293,10.09412],[0.41371,10.06361],[0.41252,10.02018],[0.36366,10.03309],[0.32075,9.72781],[0.34816,9.71607],[0.34816,9.66907],[0.32313,9.6491],[0.28261,9.69022],[0.26712,9.66437],[0.29334,9.59387],[0.36008,9.6256],[0.38153,9.58682],[0.23851,9.57389],[0.2409,9.52335],[0.30406,9.521],[0.31241,9.50337],[0.2254,9.47869],[0.25758,9.42696],[0.33148,9.44812],[0.36485,9.49749],[0.49118,9.48339],[0.56388,9.40697],[0.45424,9.04581],[0.52455,8.87746],[0.37319,8.75262],[0.47211,8.59945],[0.64731,8.48866],[0.73432,8.29529],[0.63897,8.25873],[0.5913,8.19622],[0.61156,8.18324],[0.6056,8.13959],[0.58891,8.12779],[0.62943,7.85751],[0.58295,7.62368],[0.51979,7.58706],[0.52455,7.45354],[0.57223,7.39326],[0.62943,7.41099],[0.65327,7.31643],[0.59606,7.01252],[0.52217,6.9723],[0.52098,6.94391],[0.56508,6.92971],[0.52853,6.82921],[0.57406,6.80348],[0.58176,6.76049],[0.6497,6.73682],[0.63659,6.63857],[0.74862,6.56517],[0.71048,6.53083],[0.89283,6.33779],[0.99652,6.33779],[1.03108,6.24064],[1.05969,6.22998],[1.09187,6.17074],[1.19966,6.17069],[1.19771,6.11522],[1.27574,5.93551],[1.67336,6.02702],[1.62913,6.24075],[1.79826,6.28221],[1.76906,6.43189],[1.58105,6.68619],[1.61812,6.74843],[1.55877,6.99737],[1.64249,6.99562],[1.61838,9.0527],[1.5649,9.16941],[1.41746,9.3226],[1.33675,9.54765],[1.36624,9.5951],[1.35507,9.99525],[0.77666,10.37665],[0.80358,10.71459],[0.8804,10.803],[0.91245,10.99597],[0.66104,10.99964],[0.4958,10.93269],[0.50521,10.98035],[0.48852,10.98561],[0.50388,11.01011]]]]}},{type:"Feature",properties:{iso1A2:"TH",iso1A3:"THA",iso1N3:"764",wikidata:"Q869",nameEn:"Thailand",groups:["035","142"],driveSide:"left",callingCodes:["66"]},geometry:{type:"MultiPolygon",coordinates:[[[[100.08404,20.36626],[99.95721,20.46301],[99.91616,20.44986],[99.90499,20.4487],[99.89692,20.44789],[99.89301,20.44311],[99.89168,20.44548],[99.88451,20.44596],[99.88211,20.44488],[99.86383,20.44371],[99.81096,20.33687],[99.68255,20.32077],[99.46008,20.39673],[99.46077,20.36198],[99.5569,20.20676],[99.52943,20.14811],[99.416,20.08614],[99.20328,20.12877],[99.0735,20.10298],[98.98679,19.7419],[98.83661,19.80931],[98.56065,19.67807],[98.51182,19.71303],[98.24884,19.67876],[98.13829,19.78541],[98.03314,19.80941],[98.04364,19.65755],[97.84715,19.55782],[97.88423,19.5041],[97.78769,19.39429],[97.84186,19.29526],[97.78606,19.26769],[97.84024,19.22217],[97.83479,19.09972],[97.73797,19.04261],[97.73654,18.9812],[97.66487,18.9371],[97.73836,18.88478],[97.76752,18.58097],[97.5258,18.4939],[97.36444,18.57138],[97.34522,18.54596],[97.50383,18.26844],[97.56219,18.33885],[97.64116,18.29778],[97.60841,18.23846],[97.73723,17.97912],[97.66794,17.88005],[97.76407,17.71595],[97.91829,17.54504],[98.11185,17.36829],[98.10439,17.33847],[98.34566,17.04822],[98.39441,17.06266],[98.52624,16.89979],[98.49603,16.8446],[98.53833,16.81934],[98.46994,16.73613],[98.50253,16.7139],[98.49713,16.69022],[98.51043,16.70107],[98.51579,16.69433],[98.51472,16.68521],[98.51833,16.676],[98.51113,16.64503],[98.5695,16.62826],[98.57912,16.55983],[98.63817,16.47424],[98.68074,16.27068],[98.84485,16.42354],[98.92656,16.36425],[98.8376,16.11706],[98.69585,16.13353],[98.57019,16.04578],[98.59853,15.87197],[98.541,15.65406],[98.58598,15.46821],[98.56027,15.33471],[98.4866,15.39154],[98.39351,15.34177],[98.41906,15.27103],[98.40522,15.25268],[98.30446,15.30667],[98.22,15.21327],[98.18821,15.13125],[98.24874,14.83013],[98.56762,14.37701],[98.97356,14.04868],[99.16695,13.72621],[99.20617,13.20575],[99.12225,13.19847],[99.10646,13.05804],[99.18748,12.9898],[99.18905,12.84799],[99.29254,12.68921],[99.409,12.60603],[99.47519,12.1353],[99.56445,12.14805],[99.53424,12.02317],[99.64891,11.82699],[99.64108,11.78948],[99.5672,11.62732],[99.47598,11.62434],[99.39485,11.3925],[99.31573,11.32081],[99.32756,11.28545],[99.06938,10.94857],[99.02337,10.97217],[98.99701,10.92962],[99.0069,10.85485],[98.86819,10.78336],[98.78511,10.68351],[98.77275,10.62548],[98.81944,10.52761],[98.7391,10.31488],[98.55174,9.92804],[98.52291,9.92389],[98.47298,9.95782],[98.33094,9.91973],[98.12555,9.44056],[97.63455,9.60854],[97.19814,8.18901],[99.31854,5.99868],[99.50117,6.44501],[99.91873,6.50233],[100.0756,6.4045],[100.12,6.42105],[100.19511,6.72559],[100.29651,6.68439],[100.30828,6.66462],[100.31618,6.66781],[100.31884,6.66423],[100.32671,6.66526],[100.32607,6.65933],[100.31929,6.65413],[100.35413,6.54932],[100.41152,6.52299],[100.41791,6.5189],[100.42351,6.51762],[100.43027,6.52389],[100.66986,6.45086],[100.74361,6.50811],[100.74822,6.46231],[100.81045,6.45086],[100.85884,6.24929],[101.10313,6.25617],[101.12618,6.19431],[101.06165,6.14161],[101.12388,6.11411],[101.087,5.9193],[101.02708,5.91013],[100.98815,5.79464],[101.14062,5.61613],[101.25755,5.71065],[101.25524,5.78633],[101.58019,5.93534],[101.69773,5.75881],[101.75074,5.79091],[101.80144,5.74505],[101.89188,5.8386],[101.91776,5.84269],[101.92819,5.85511],[101.94712,5.98421],[101.9714,6.00575],[101.97114,6.01992],[101.99209,6.04075],[102.01835,6.05407],[102.09182,6.14161],[102.07732,6.193],[102.08127,6.22679],[102.09086,6.23546],[102.46318,7.22462],[102.47649,9.66162],[102.52395,11.25257],[102.91449,11.65512],[102.90973,11.75613],[102.83957,11.8519],[102.78427,11.98746],[102.77026,12.06815],[102.70176,12.1686],[102.73134,12.37091],[102.78116,12.40284],[102.7796,12.43781],[102.57567,12.65358],[102.51963,12.66117],[102.4994,12.71736],[102.53053,12.77506],[102.49335,12.92711],[102.48694,12.97537],[102.52275,12.99813],[102.46011,13.08057],[102.43422,13.09061],[102.36146,13.26006],[102.36001,13.31142],[102.34611,13.35618],[102.35692,13.38274],[102.35563,13.47307],[102.361,13.50551],[102.33828,13.55613],[102.36859,13.57488],[102.44601,13.5637],[102.5358,13.56933],[102.57573,13.60461],[102.62483,13.60883],[102.58635,13.6286],[102.5481,13.6589],[102.56848,13.69366],[102.72727,13.77806],[102.77864,13.93374],[102.91251,14.01531],[102.93275,14.19044],[103.16469,14.33075],[103.39353,14.35639],[103.53518,14.42575],[103.71109,14.4348],[103.70175,14.38052],[103.93836,14.3398],[104.27616,14.39861],[104.55014,14.36091],[104.69335,14.42726],[104.97667,14.38806],[105.02804,14.23722],[105.08408,14.20402],[105.14012,14.23873],[105.17748,14.34432],[105.20894,14.34967],[105.43783,14.43865],[105.53864,14.55731],[105.5121,14.80802],[105.61162,15.00037],[105.46661,15.13132],[105.58043,15.32724],[105.50662,15.32054],[105.4692,15.33709],[105.47635,15.3796],[105.58191,15.41031],[105.60446,15.53301],[105.61756,15.68792],[105.46573,15.74742],[105.42285,15.76971],[105.37959,15.84074],[105.34115,15.92737],[105.38508,15.987],[105.42001,16.00657],[105.06204,16.09792],[105.00262,16.25627],[104.88057,16.37311],[104.73349,16.565],[104.76099,16.69302],[104.7397,16.81005],[104.76442,16.84752],[104.7373,16.91125],[104.73712,17.01404],[104.80716,17.19025],[104.80061,17.39367],[104.69867,17.53038],[104.45404,17.66788],[104.35432,17.82871],[104.2757,17.86139],[104.21776,17.99335],[104.10927,18.10826],[104.06533,18.21656],[103.97725,18.33631],[103.93916,18.33914],[103.85642,18.28666],[103.82449,18.33979],[103.699,18.34125],[103.60957,18.40528],[103.47773,18.42841],[103.41044,18.4486],[103.30977,18.4341],[103.24779,18.37807],[103.23818,18.34875],[103.29757,18.30475],[103.17093,18.2618],[103.14994,18.23172],[103.1493,18.17799],[103.07343,18.12351],[103.07823,18.03833],[103.0566,18.00144],[103.01998,17.97095],[102.9912,17.9949],[102.95812,18.0054],[102.86323,17.97531],[102.81988,17.94233],[102.79044,17.93612],[102.75954,17.89561],[102.68538,17.86653],[102.67543,17.84529],[102.69946,17.81686],[102.68194,17.80151],[102.59485,17.83537],[102.5896,17.84889],[102.61432,17.92273],[102.60971,17.95411],[102.59234,17.96127],[102.45523,17.97106],[102.11359,18.21532],[101.88485,18.02474],[101.78087,18.07559],[101.72294,17.92867],[101.44667,17.7392],[101.15108,17.47586],[100.96541,17.57926],[101.02185,17.87637],[101.1793,18.0544],[101.19118,18.2125],[101.15108,18.25624],[101.18227,18.34367],[101.06047,18.43247],[101.27585,18.68875],[101.22832,18.73377],[101.25803,18.89545],[101.35606,19.04716],[101.261,19.12717],[101.24911,19.33334],[101.20604,19.35296],[101.21347,19.46223],[101.26991,19.48324],[101.26545,19.59242],[101.08928,19.59748],[100.90302,19.61901],[100.77231,19.48324],[100.64606,19.55884],[100.58219,19.49164],[100.49604,19.53504],[100.398,19.75047],[100.5094,19.87904],[100.58808,20.15791],[100.55218,20.17741],[100.51052,20.14928],[100.47567,20.19133],[100.4537,20.19971],[100.44992,20.23644],[100.41473,20.25625],[100.37439,20.35156],[100.33383,20.4028],[100.25769,20.3992],[100.22076,20.31598],[100.16668,20.2986],[100.1712,20.24324],[100.11785,20.24787],[100.09337,20.26293],[100.09999,20.31614],[100.08404,20.36626]]]]}},{type:"Feature",properties:{iso1A2:"TJ",iso1A3:"TJK",iso1N3:"762",wikidata:"Q863",nameEn:"Tajikistan",groups:["143","142"],callingCodes:["992"]},geometry:{type:"MultiPolygon",coordinates:[[[[70.45251,41.04438],[70.38028,41.02014],[70.36655,40.90296],[69.69434,40.62615],[69.59441,40.70181],[69.53021,40.77621],[69.38327,40.7918],[69.32834,40.70233],[69.3455,40.57988],[69.2643,40.57506],[69.21063,40.54469],[69.27066,40.49274],[69.28525,40.41894],[69.30774,40.36102],[69.33794,40.34819],[69.32833,40.29794],[69.30808,40.2821],[69.24817,40.30357],[69.25229,40.26362],[69.30104,40.24502],[69.30448,40.18774],[69.2074,40.21488],[69.15659,40.2162],[69.04544,40.22904],[68.85832,40.20885],[68.84357,40.18604],[68.79276,40.17555],[68.77902,40.20492],[68.5332,40.14826],[68.52771,40.11676],[68.62796,40.07789],[69.01523,40.15771],[69.01935,40.11466],[68.96579,40.06949],[68.84906,40.04952],[68.93695,39.91167],[68.88889,39.87163],[68.63071,39.85265],[68.61972,39.68905],[68.54166,39.53929],[68.12053,39.56317],[67.70992,39.66156],[67.62889,39.60234],[67.44899,39.57799],[67.46547,39.53564],[67.39681,39.52505],[67.46822,39.46146],[67.45998,39.315],[67.36522,39.31287],[67.33226,39.23739],[67.67833,39.14479],[67.68915,39.00775],[68.09704,39.02589],[68.19743,38.85985],[68.06948,38.82115],[68.12877,38.73677],[68.05598,38.71641],[68.0807,38.64136],[68.05873,38.56087],[68.11366,38.47169],[68.06274,38.39435],[68.13289,38.40822],[68.40343,38.19484],[68.27159,37.91477],[68.12635,37.93],[67.81566,37.43107],[67.8474,37.31594],[67.78329,37.1834],[67.7803,37.08978],[67.87917,37.0591],[68.02194,36.91923],[68.18542,37.02074],[68.27605,37.00977],[68.29253,37.10621],[68.41201,37.10402],[68.41888,37.13906],[68.61851,37.19815],[68.6798,37.27906],[68.81438,37.23862],[68.80889,37.32494],[68.91189,37.26704],[68.88168,37.33368],[68.96407,37.32603],[69.03274,37.25174],[69.25152,37.09426],[69.39529,37.16752],[69.45022,37.23315],[69.36645,37.40462],[69.44954,37.4869],[69.51888,37.5844],[69.80041,37.5746],[69.84435,37.60616],[69.93362,37.61378],[69.95971,37.5659],[70.15015,37.52519],[70.28243,37.66706],[70.27694,37.81258],[70.1863,37.84296],[70.17206,37.93276],[70.4898,38.12546],[70.54673,38.24541],[70.60407,38.28046],[70.61526,38.34774],[70.64966,38.34999],[70.69189,38.37031],[70.6761,38.39144],[70.67438,38.40597],[70.69807,38.41861],[70.72485,38.4131],[70.75455,38.4252],[70.77132,38.45548],[70.78581,38.45502],[70.78702,38.45031],[70.79766,38.44944],[70.80521,38.44447],[70.81697,38.44507],[70.82538,38.45394],[70.84376,38.44688],[70.88719,38.46826],[70.92728,38.43021],[70.98693,38.48862],[71.03545,38.44779],[71.0556,38.40176],[71.09542,38.42517],[71.10592,38.42077],[71.10957,38.40671],[71.1451,38.40106],[71.21291,38.32797],[71.33114,38.30339],[71.33869,38.27335],[71.37803,38.25641],[71.36444,38.15358],[71.29878,38.04429],[71.28922,38.01272],[71.27622,37.99946],[71.27278,37.96496],[71.24969,37.93031],[71.2809,37.91995],[71.296,37.93403],[71.32871,37.88564],[71.51565,37.95349],[71.58843,37.92425],[71.59255,37.79956],[71.55752,37.78677],[71.54324,37.77104],[71.53053,37.76534],[71.55234,37.73209],[71.54186,37.69691],[71.51972,37.61945],[71.5065,37.60912],[71.49693,37.53527],[71.50616,37.50733],[71.5256,37.47971],[71.49612,37.4279],[71.47685,37.40281],[71.4862,37.33405],[71.49821,37.31975],[71.50674,37.31502],[71.48536,37.26017],[71.4824,37.24921],[71.48339,37.23937],[71.47386,37.2269],[71.4555,37.21418],[71.4494,37.18137],[71.44127,37.11856],[71.43097,37.05855],[71.45578,37.03094],[71.46923,36.99925],[71.48481,36.93218],[71.51502,36.89128],[71.57195,36.74943],[71.67083,36.67346],[71.83229,36.68084],[72.31676,36.98115],[72.54095,37.00007],[72.66381,37.02014],[72.79693,37.22222],[73.06884,37.31729],[73.29633,37.46495],[73.77197,37.4417],[73.76647,37.33913],[73.61129,37.27469],[73.64974,37.23643],[73.82552,37.22659],[73.8564,37.26158],[74.20308,37.34208],[74.23339,37.41116],[74.41055,37.3948],[74.56161,37.37734],[74.68383,37.3948],[74.8294,37.3435],[74.88887,37.23275],[75.12328,37.31839],[75.09719,37.37297],[75.15899,37.41443],[75.06011,37.52779],[74.94338,37.55501],[74.8912,37.67576],[75.00935,37.77486],[74.92416,37.83428],[74.9063,38.03033],[74.82665,38.07359],[74.80331,38.19889],[74.69894,38.22155],[74.69619,38.42947],[74.51217,38.47034],[74.17022,38.65504],[73.97933,38.52945],[73.79806,38.61106],[73.80656,38.66449],[73.7033,38.84782],[73.7445,38.93867],[73.82964,38.91517],[73.81728,39.04007],[73.75823,39.023],[73.60638,39.24534],[73.54572,39.27567],[73.55396,39.3543],[73.5004,39.38402],[73.59241,39.40843],[73.59831,39.46425],[73.45096,39.46677],[73.31912,39.38615],[73.18454,39.35536],[72.85934,39.35116],[72.62027,39.39696],[72.33173,39.33093],[72.23834,39.17248],[72.17242,39.2661],[72.09689,39.26823],[72.04059,39.36704],[71.90601,39.27674],[71.79202,39.27355],[71.7522,39.32031],[71.80164,39.40631],[71.76816,39.45456],[71.62688,39.44056],[71.5517,39.45722],[71.55856,39.57588],[71.49814,39.61397],[71.08752,39.50704],[71.06418,39.41586],[70.7854,39.38933],[70.64087,39.58792],[70.44757,39.60128],[70.2869,39.53141],[70.11111,39.58223],[69.87491,39.53882],[69.68677,39.59281],[69.3594,39.52516],[69.26938,39.8127],[69.35649,40.01994],[69.43134,39.98431],[69.43557,39.92877],[69.53615,39.93991],[69.5057,40.03277],[69.53855,40.0887],[69.53794,40.11833],[69.55555,40.12296],[69.57615,40.10524],[69.64704,40.12165],[69.67001,40.10639],[70.01283,40.23288],[70.58297,40.00891],[70.57384,39.99394],[70.47557,39.93216],[70.55033,39.96619],[70.58912,39.95211],[70.65946,39.9878],[70.65827,40.0981],[70.7928,40.12797],[70.80495,40.16813],[70.9818,40.22392],[70.8607,40.217],[70.62342,40.17396],[70.56394,40.26421],[70.57149,40.3442],[70.37511,40.38605],[70.32626,40.45174],[70.49871,40.52503],[70.80009,40.72825],[70.45251,41.04438]]],[[[70.68112,40.90612],[70.6158,40.97661],[70.56077,41.00642],[70.54223,40.98787],[70.57501,40.98941],[70.6721,40.90555],[70.68112,40.90612]]],[[[70.74189,39.86319],[70.53651,39.89155],[70.52631,39.86989],[70.54998,39.85137],[70.59667,39.83542],[70.63105,39.77923],[70.74189,39.86319]]]]}},{type:"Feature",properties:{iso1A2:"TK",iso1A3:"TKL",iso1N3:"772",wikidata:"Q36823",nameEn:"Tokelau",country:"NZ",groups:["061","009"],driveSide:"left",callingCodes:["690"]},geometry:{type:"MultiPolygon",coordinates:[[[[-167.75195,-10.12005],[-167.75329,-7.52784],[-174.18707,-7.54408],[-174.17993,-10.13616],[-167.75195,-10.12005]]]]}},{type:"Feature",properties:{iso1A2:"TL",iso1A3:"TLS",iso1N3:"626",wikidata:"Q574",nameEn:"East Timor",aliases:["Timor-Leste","TP"],groups:["035","142"],driveSide:"left",callingCodes:["670"]},geometry:{type:"MultiPolygon",coordinates:[[[[124.46701,-9.13002],[124.94011,-8.85617],[124.97742,-9.08128],[125.11764,-8.96359],[125.18632,-9.03142],[125.18907,-9.16434],[125.09434,-9.19669],[125.04044,-9.17093],[124.97892,-9.19281],[125.09025,-9.46406],[125.68138,-9.85176],[127.55165,-9.05052],[127.42116,-8.22471],[125.87691,-8.31789],[125.65946,-8.06136],[125.31127,-8.22976],[124.92337,-8.75859],[124.33472,-9.11416],[124.04628,-9.22671],[124.04286,-9.34243],[124.10539,-9.41206],[124.14517,-9.42324],[124.21247,-9.36904],[124.28115,-9.42189],[124.28115,-9.50453],[124.3535,-9.48493],[124.35258,-9.43002],[124.38554,-9.3582],[124.45971,-9.30263],[124.46701,-9.13002]]]]}},{type:"Feature",properties:{iso1A2:"TM",iso1A3:"TKM",iso1N3:"795",wikidata:"Q874",nameEn:"Turkmenistan",groups:["143","142"],callingCodes:["993"]},geometry:{type:"MultiPolygon",coordinates:[[[[60.5078,41.21694],[60.06581,41.4363],[60.18117,41.60082],[60.06032,41.76287],[60.08504,41.80997],[60.33223,41.75058],[59.95046,41.97966],[60.0356,42.01028],[60.04659,42.08982],[59.96419,42.1428],[60.00539,42.212],[59.94633,42.27655],[59.4341,42.29738],[59.2955,42.37064],[59.17317,42.52248],[58.93422,42.5407],[58.6266,42.79314],[58.57991,42.64988],[58.27504,42.69632],[58.14321,42.62159],[58.29427,42.56497],[58.51674,42.30348],[58.40688,42.29535],[58.3492,42.43335],[57.99214,42.50021],[57.90975,42.4374],[57.92897,42.24047],[57.84932,42.18555],[57.6296,42.16519],[57.30275,42.14076],[57.03633,41.92043],[56.96218,41.80383],[57.03359,41.41777],[57.13796,41.36625],[57.03423,41.25435],[56.00314,41.32584],[55.45471,41.25609],[54.95182,41.92424],[54.20635,42.38477],[52.97575,42.1308],[52.47884,41.78034],[52.26048,41.69249],[51.7708,40.29239],[53.89734,37.3464],[54.24565,37.32047],[54.36211,37.34912],[54.58664,37.45809],[54.67247,37.43532],[54.77822,37.51597],[54.81804,37.61285],[54.77684,37.62264],[54.851,37.75739],[55.13412,37.94705],[55.44152,38.08564],[55.76561,38.12238],[55.97847,38.08024],[56.33278,38.08132],[56.32454,38.18502],[56.43303,38.26054],[56.62255,38.24005],[56.73928,38.27887],[57.03453,38.18717],[57.21169,38.28965],[57.37236,38.09321],[57.35042,37.98546],[57.79534,37.89299],[58.21399,37.77281],[58.22999,37.6856],[58.39959,37.63134],[58.47786,37.6433],[58.5479,37.70526],[58.6921,37.64548],[58.9338,37.67374],[59.22905,37.51161],[59.33507,37.53146],[59.39797,37.47892],[59.39385,37.34257],[59.55178,37.13594],[59.74678,37.12499],[60.00768,37.04102],[60.34767,36.63214],[61.14516,36.64644],[61.18187,36.55348],[61.1393,36.38782],[61.22719,36.12759],[61.12007,35.95992],[61.22444,35.92879],[61.26152,35.80749],[61.22719,35.67038],[61.27371,35.61482],[61.58742,35.43803],[61.77693,35.41341],[61.97743,35.4604],[62.05709,35.43803],[62.15871,35.33278],[62.29191,35.25964],[62.29878,35.13312],[62.48006,35.28796],[62.62288,35.22067],[62.74098,35.25432],[62.90853,35.37086],[63.0898,35.43131],[63.12276,35.53196],[63.10079,35.63024],[63.23262,35.67487],[63.10318,35.81782],[63.12276,35.86208],[63.29579,35.85985],[63.53475,35.90881],[63.56496,35.95106],[63.98519,36.03773],[64.05385,36.10433],[64.43288,36.24401],[64.57295,36.34362],[64.62514,36.44311],[64.61141,36.6351],[64.97945,37.21913],[65.51778,37.23881],[65.64263,37.34388],[65.64137,37.45061],[65.72274,37.55438],[66.30993,37.32409],[66.55743,37.35409],[66.52303,37.39827],[66.65761,37.45497],[66.52852,37.58568],[66.53676,37.80084],[66.67684,37.96776],[66.56697,38.0435],[66.41042,38.02403],[66.24013,38.16238],[65.83913,38.25733],[65.55873,38.29052],[64.32576,38.98691],[64.19086,38.95561],[63.70778,39.22349],[63.6913,39.27666],[62.43337,39.98528],[62.34273,40.43206],[62.11751,40.58242],[61.87856,41.12257],[61.4446,41.29407],[61.39732,41.19873],[61.33199,41.14946],[61.22212,41.14946],[61.03261,41.25691],[60.5078,41.21694]]]]}},{type:"Feature",properties:{iso1A2:"TN",iso1A3:"TUN",iso1N3:"788",wikidata:"Q948",nameEn:"Tunisia",groups:["015","002"],callingCodes:["216"]},geometry:{type:"MultiPolygon",coordinates:[[[[11.2718,37.6713],[7.89009,38.19924],[8.59123,37.14286],[8.64044,36.9401],[8.62972,36.86499],[8.67706,36.8364],[8.57613,36.78062],[8.46537,36.7706],[8.47609,36.66607],[8.16167,36.48817],[8.18936,36.44939],[8.40731,36.42208],[8.2626,35.91733],[8.26472,35.73669],[8.35371,35.66373],[8.36086,35.47774],[8.30329,35.29884],[8.47318,35.23376],[8.3555,35.10007],[8.30727,34.95378],[8.25189,34.92009],[8.29655,34.72798],[8.20482,34.57575],[7.86264,34.3987],[7.81242,34.21841],[7.74207,34.16492],[7.66174,34.20167],[7.52851,34.06493],[7.54088,33.7726],[7.73687,33.42114],[7.83028,33.18851],[8.11433,33.10175],[8.1179,33.05086],[8.31895,32.83483],[8.35999,32.50101],[9.07483,32.07865],[9.55544,30.23971],[9.76848,30.34366],[9.88152,30.34074],[10.29516,30.90337],[10.12239,31.42098],[10.31364,31.72648],[10.48497,31.72956],[10.62788,31.96629],[10.7315,31.97235],[11.04234,32.2145],[11.53898,32.4138],[11.57828,32.48013],[11.46037,32.6307],[11.51549,33.09826],[11.55852,33.1409],[11.56255,33.16754],[11.66543,33.34642],[11.2718,37.6713]]]]}},{type:"Feature",properties:{iso1A2:"TO",iso1A3:"TON",iso1N3:"776",wikidata:"Q678",nameEn:"Tonga",groups:["061","009"],driveSide:"left",callingCodes:["676"]},geometry:{type:"MultiPolygon",coordinates:[[[[-176.74538,-22.89767],[-180,-22.90585],[-180,-24.21376],[-173.10761,-24.19665],[-173.11048,-23.23027],[-173.13438,-14.94228],[-174.17905,-14.94502],[-176.76826,-14.95183],[-176.74538,-22.89767]]]]}},{type:"Feature",properties:{iso1A2:"TR",iso1A3:"TUR",iso1N3:"792",wikidata:"Q43",nameEn:"Turkey",groups:["145","142"],callingCodes:["90"]},geometry:{type:"MultiPolygon",coordinates:[[[[41.54366,41.52185],[40.89217,41.72528],[34.8305,42.4581],[28.32297,41.98371],[28.02971,41.98066],[27.91479,41.97902],[27.83492,41.99709],[27.81235,41.94803],[27.69949,41.97515],[27.55191,41.90928],[27.52379,41.93756],[27.45478,41.96591],[27.27411,42.10409],[27.22376,42.10152],[27.19251,42.06028],[27.08486,42.08735],[27.03277,42.0809],[26.95638,42.00741],[26.79143,41.97386],[26.62996,41.97644],[26.56051,41.92995],[26.57961,41.90024],[26.53968,41.82653],[26.36952,41.82265],[26.33589,41.76802],[26.32952,41.73637],[26.35957,41.71149],[26.47958,41.67037],[26.5209,41.62592],[26.59196,41.60491],[26.59742,41.48058],[26.61767,41.42281],[26.62997,41.34613],[26.5837,41.32131],[26.5209,41.33993],[26.39861,41.25053],[26.32259,41.24929],[26.31928,41.07386],[26.3606,41.02027],[26.33297,40.98388],[26.35894,40.94292],[26.32259,40.94042],[26.28623,40.93005],[26.29441,40.89119],[26.26169,40.9168],[26.20856,40.86048],[26.21351,40.83298],[26.15685,40.80709],[26.12854,40.77339],[26.12495,40.74283],[26.08638,40.73214],[26.0754,40.72772],[26.03489,40.73051],[25.94795,40.72797],[26.04292,40.3958],[25.61285,40.17161],[25.94257,39.39358],[26.43357,39.43096],[26.70773,39.0312],[26.61814,38.81372],[26.21136,38.65436],[26.32173,38.48731],[26.24183,38.44695],[26.21136,38.17558],[27.05537,37.9131],[27.16428,37.72343],[26.99377,37.69034],[26.95583,37.64989],[27.14757,37.32],[27.20312,36.94571],[27.45627,36.9008],[27.24613,36.71622],[27.46117,36.53789],[27.89482,36.69898],[27.95037,36.46155],[28.23708,36.56812],[29.30783,36.01033],[29.48192,36.18377],[29.61002,36.1731],[29.61805,36.14179],[29.69611,36.10365],[29.73302,35.92555],[32.82353,35.70297],[35.51152,36.10954],[35.931,35.92109],[35.98499,35.94107],[36.00514,35.94113],[36.01844,35.92403],[35.99829,35.88242],[36.11827,35.85923],[36.13919,35.83692],[36.14029,35.81015],[36.1623,35.80925],[36.17441,35.92076],[36.19973,35.95195],[36.25366,35.96264],[36.27678,35.94839],[36.29769,35.96086],[36.28338,36.00273],[36.30099,36.00985],[36.33956,35.98687],[36.37474,36.01163],[36.39206,36.22088],[36.4617,36.20461],[36.50463,36.2419],[36.6125,36.22592],[36.68672,36.23677],[36.65653,36.33861],[36.6081,36.33772],[36.54206,36.49539],[36.58829,36.58295],[36.57398,36.65186],[36.62681,36.71189],[36.61581,36.74629],[36.66727,36.82901],[36.99557,36.75997],[36.99886,36.74012],[37.04399,36.73483],[37.04619,36.71101],[37.01647,36.69512],[37.02088,36.66422],[37.08279,36.63495],[37.10894,36.6704],[37.16177,36.66069],[37.21988,36.6736],[37.47253,36.63243],[37.49103,36.66904],[37.68048,36.75065],[37.81974,36.76055],[38.21064,36.91842],[38.38859,36.90064],[38.55908,36.84429],[38.74042,36.70629],[39.03217,36.70911],[39.21538,36.66834],[39.81589,36.75538],[40.69136,37.0996],[40.90856,37.13147],[41.21937,37.07665],[41.515,37.08084],[42.00894,37.17209],[42.18225,37.28569],[42.19301,37.31323],[42.2112,37.32491],[42.22257,37.31395],[42.22381,37.30238],[42.20454,37.28715],[42.21548,37.28026],[42.23683,37.2863],[42.26039,37.27017],[42.2824,37.2798],[42.34735,37.22548],[42.32313,37.17814],[42.35724,37.10998],[42.56725,37.14878],[42.78887,37.38615],[42.93705,37.32015],[43.11403,37.37436],[43.30083,37.30629],[43.33508,37.33105],[43.50787,37.24436],[43.56702,37.25675],[43.63085,37.21957],[43.7009,37.23692],[43.8052,37.22825],[43.82699,37.19477],[43.84878,37.22205],[43.90949,37.22453],[44.02002,37.33229],[44.13521,37.32486],[44.2613,37.25055],[44.27998,37.16501],[44.22239,37.15756],[44.18503,37.09551],[44.25975,36.98119],[44.30645,36.97373],[44.35937,37.02843],[44.35315,37.04955],[44.38117,37.05825],[44.42631,37.05825],[44.63179,37.19229],[44.76698,37.16162],[44.78319,37.1431],[44.7868,37.16644],[44.75986,37.21549],[44.81021,37.2915],[44.58449,37.45018],[44.61401,37.60165],[44.56887,37.6429],[44.62096,37.71985],[44.55498,37.783],[44.45948,37.77065],[44.3883,37.85433],[44.22509,37.88859],[44.42476,38.25763],[44.50115,38.33939],[44.44386,38.38295],[44.38309,38.36117],[44.3119,38.37887],[44.3207,38.49799],[44.32058,38.62752],[44.28065,38.6465],[44.26155,38.71427],[44.30322,38.81581],[44.18863,38.93881],[44.20946,39.13975],[44.1043,39.19842],[44.03667,39.39223],[44.22452,39.4169],[44.29818,39.378],[44.37921,39.4131],[44.42832,39.4131],[44.41849,39.56659],[44.48111,39.61579],[44.47298,39.68788],[44.6137,39.78393],[44.65422,39.72163],[44.71806,39.71124],[44.81043,39.62677],[44.80977,39.65768],[44.75779,39.7148],[44.61845,39.8281],[44.46635,39.97733],[44.26973,40.04866],[44.1778,40.02845],[44.1057,40.03555],[43.92307,40.01787],[43.65688,40.11199],[43.65221,40.14889],[43.71136,40.16673],[43.59928,40.34019],[43.60862,40.43267],[43.54791,40.47413],[43.63664,40.54159],[43.7425,40.66805],[43.74872,40.7365],[43.67712,40.84846],[43.67712,40.93084],[43.58683,40.98961],[43.47319,41.02251],[43.44984,41.0988],[43.4717,41.12611],[43.44973,41.17666],[43.36118,41.2028],[43.23096,41.17536],[43.1945,41.25242],[43.13373,41.25503],[43.21707,41.30331],[43.02956,41.37891],[42.8785,41.50516],[42.84899,41.47265],[42.78995,41.50126],[42.84471,41.58912],[42.72794,41.59714],[42.59202,41.58183],[42.51772,41.43606],[42.26387,41.49346],[41.95134,41.52466],[41.81939,41.43621],[41.7124,41.47417],[41.7148,41.4932],[41.54366,41.52185]]]]}},{type:"Feature",properties:{iso1A2:"TT",iso1A3:"TTO",iso1N3:"780",wikidata:"Q754",nameEn:"Trinidad and Tobago",groups:["029","003","419","019"],driveSide:"left",callingCodes:["1 868"]},geometry:{type:"MultiPolygon",coordinates:[[[[-61.62505,11.18974],[-62.08693,10.04435],[-60.89962,9.81445],[-60.07172,11.77667],[-61.62505,11.18974]]]]}},{type:"Feature",properties:{iso1A2:"TV",iso1A3:"TUV",iso1N3:"798",wikidata:"Q672",nameEn:"Tuvalu",groups:["061","009"],driveSide:"left",callingCodes:["688"]},geometry:{type:"MultiPolygon",coordinates:[[[[174,-5],[174,-11.5],[179.99999,-11.5],[179.99999,-5],[174,-5]]]]}},{type:"Feature",properties:{iso1A2:"TW",iso1A3:"TWN",iso1N3:"158",wikidata:"Q865",nameEn:"Taiwan",groups:["030","142"],callingCodes:["886"]},geometry:{type:"MultiPolygon",coordinates:[[[[123.0791,22.07818],[122.26612,25.98197],[120.49232,25.22863],[118.56434,24.49266],[118.42453,24.54644],[118.35291,24.51645],[118.28244,24.51231],[118.11703,24.39734],[120.69238,21.52331],[123.0791,22.07818]]]]}},{type:"Feature",properties:{iso1A2:"TZ",iso1A3:"TZA",iso1N3:"834",wikidata:"Q924",nameEn:"Tanzania",groups:["014","202","002"],driveSide:"left",callingCodes:["255"]},geometry:{type:"MultiPolygon",coordinates:[[[[30.80408,-0.99911],[30.76635,-0.9852],[30.70631,-1.01175],[30.64166,-1.06601],[30.47194,-1.0555],[30.45116,-1.10641],[30.50889,-1.16412],[30.57123,-1.33264],[30.71974,-1.43244],[30.84079,-1.64652],[30.80802,-1.91477],[30.89303,-2.08223],[30.83915,-2.35795],[30.54501,-2.41404],[30.41789,-2.66266],[30.52747,-2.65841],[30.40662,-2.86151],[30.4987,-2.9573],[30.57926,-2.89791],[30.6675,-2.98987],[30.83823,-2.97837],[30.84165,-3.25152],[30.45915,-3.56532],[30.22042,-4.01738],[30.03323,-4.26631],[29.88172,-4.35743],[29.82885,-4.36153],[29.77289,-4.41733],[29.75109,-4.45836],[29.63827,-4.44681],[29.43673,-4.44845],[29.52552,-6.2731],[30.2567,-7.14121],[30.79243,-8.27382],[31.00796,-8.58615],[31.37533,-8.60769],[31.57147,-8.70619],[31.57147,-8.81388],[31.71158,-8.91386],[31.81587,-8.88618],[31.94663,-8.93846],[31.94196,-9.02303],[31.98866,-9.07069],[32.08206,-9.04609],[32.16146,-9.05993],[32.25486,-9.13371],[32.43543,-9.11988],[32.49147,-9.14754],[32.53661,-9.24281],[32.75611,-9.28583],[32.76233,-9.31963],[32.95389,-9.40138],[32.99397,-9.36712],[33.14925,-9.49322],[33.31581,-9.48554],[33.48052,-9.62442],[33.76677,-9.58516],[33.93298,-9.71647],[33.9638,-9.62206],[33.95829,-9.54066],[34.03865,-9.49398],[34.54499,-10.0678],[34.51911,-10.12279],[34.57581,-10.56271],[34.65946,-10.6828],[34.67047,-10.93796],[34.61161,-11.01611],[34.63305,-11.11731],[34.79375,-11.32245],[34.91153,-11.39799],[34.96296,-11.57354],[35.63599,-11.55927],[35.82767,-11.41081],[36.19094,-11.57593],[36.19094,-11.70008],[36.62068,-11.72884],[36.80309,-11.56836],[37.3936,-11.68949],[37.76614,-11.53352],[37.8388,-11.3123],[37.93618,-11.26228],[38.21598,-11.27289],[38.47258,-11.4199],[38.88996,-11.16978],[39.24395,-11.17433],[39.58249,-10.96043],[40.00295,-10.80255],[40.44265,-10.4618],[40.74206,-10.25691],[40.14328,-4.64201],[39.62121,-4.68136],[39.44306,-4.93877],[39.21631,-4.67835],[37.81321,-3.69179],[37.75036,-3.54243],[37.63099,-3.50723],[37.5903,-3.42735],[37.71745,-3.304],[37.67199,-3.06222],[34.0824,-1.02264],[34.03084,-1.05101],[34.02286,-1.00779],[33.93107,-0.99298],[30.80408,-0.99911]]]]}},{type:"Feature",properties:{iso1A2:"UA",iso1A3:"UKR",iso1N3:"804",wikidata:"Q212",nameEn:"Ukraine",groups:["151","150"],callingCodes:["380"]},geometry:{type:"MultiPolygon",coordinates:[[[[33.57318,46.10317],[33.61467,46.13561],[33.63854,46.14147],[33.61517,46.22615],[33.646,46.23028],[33.74047,46.18555],[33.79715,46.20482],[33.85234,46.19863],[33.91549,46.15938],[34.05272,46.10838],[34.07311,46.11769],[34.12929,46.10494],[34.181,46.06804],[34.25111,46.0532],[34.33912,46.06114],[34.41221,46.00245],[34.44155,45.95995],[34.48729,45.94267],[34.52011,45.95097],[34.55889,45.99347],[34.60861,45.99347],[34.66679,45.97136],[34.75479,45.90705],[34.80153,45.90047],[34.79905,45.81009],[34.96015,45.75634],[35.23066,45.79231],[37.62608,46.82615],[38.12112,46.86078],[38.3384,46.98085],[38.22955,47.12069],[38.23049,47.2324],[38.32112,47.2585],[38.33074,47.30508],[38.22225,47.30788],[38.28954,47.39255],[38.28679,47.53552],[38.35062,47.61631],[38.76379,47.69346],[38.79628,47.81109],[38.87979,47.87719],[39.73935,47.82876],[39.82213,47.96396],[39.77544,48.04206],[39.88256,48.04482],[39.83724,48.06501],[39.94847,48.22811],[40.00752,48.22445],[39.99241,48.31768],[39.97325,48.31399],[39.9693,48.29904],[39.95248,48.29972],[39.91465,48.26743],[39.90041,48.3049],[39.84273,48.30947],[39.84136,48.33321],[39.94847,48.35055],[39.88794,48.44226],[39.86196,48.46633],[39.84548,48.57821],[39.79764,48.58668],[39.67226,48.59368],[39.71765,48.68673],[39.73104,48.7325],[39.79466,48.83739],[39.97182,48.79398],[40.08168,48.87443],[40.03636,48.91957],[39.98967,48.86901],[39.78368,48.91596],[39.74874,48.98675],[39.72649,48.9754],[39.71353,48.98959],[39.6683,48.99454],[39.6836,49.05121],[39.93437,49.05709],[40.01988,49.1761],[40.22176,49.25683],[40.18331,49.34996],[40.14912,49.37681],[40.1141,49.38798],[40.03087,49.45452],[40.03636,49.52321],[40.16683,49.56865],[40.13249,49.61672],[39.84548,49.56064],[39.65047,49.61761],[39.59142,49.73758],[39.44496,49.76067],[39.27968,49.75976],[39.1808,49.88911],[38.9391,49.79524],[38.90477,49.86787],[38.73311,49.90238],[38.68677,50.00904],[38.65688,49.97176],[38.35408,50.00664],[38.32524,50.08866],[38.18517,50.08161],[38.21675,49.98104],[38.02999,49.90592],[38.02999,49.94482],[37.90776,50.04194],[37.79515,50.08425],[37.75807,50.07896],[37.61113,50.21976],[37.62879,50.24481],[37.62486,50.29966],[37.47243,50.36277],[37.48204,50.46079],[37.08468,50.34935],[36.91762,50.34963],[36.69377,50.26982],[36.64571,50.218],[36.56655,50.2413],[36.58371,50.28563],[36.47817,50.31457],[36.30101,50.29088],[36.20763,50.3943],[36.06893,50.45205],[35.8926,50.43829],[35.80388,50.41356],[35.73659,50.35489],[35.61711,50.35707],[35.58003,50.45117],[35.47463,50.49247],[35.39464,50.64751],[35.48116,50.66405],[35.47704,50.77274],[35.41367,50.80227],[35.39307,50.92145],[35.32598,50.94524],[35.40837,51.04119],[35.31774,51.08434],[35.20375,51.04723],[35.12685,51.16191],[35.14058,51.23162],[34.97304,51.2342],[34.82472,51.17483],[34.6874,51.18],[34.6613,51.25053],[34.38802,51.2746],[34.31661,51.23936],[34.23009,51.26429],[34.33446,51.363],[34.22048,51.4187],[34.30562,51.5205],[34.17599,51.63253],[34.07765,51.67065],[34.42922,51.72852],[34.41136,51.82793],[34.09413,52.00835],[34.11199,52.14087],[34.05239,52.20132],[33.78789,52.37204],[33.55718,52.30324],[33.48027,52.31499],[33.51323,52.35779],[33.18913,52.3754],[32.89937,52.2461],[32.85405,52.27888],[32.69475,52.25535],[32.54781,52.32423],[32.3528,52.32842],[32.38988,52.24946],[32.33083,52.23685],[32.34044,52.1434],[32.2777,52.10266],[32.23331,52.08085],[32.08813,52.03319],[31.92159,52.05144],[31.96141,52.08015],[31.85018,52.11305],[31.81722,52.09955],[31.7822,52.11406],[31.38326,52.12991],[31.25142,52.04131],[31.13332,52.1004],[30.95589,52.07775],[30.90897,52.00699],[30.76443,51.89739],[30.68804,51.82806],[30.51946,51.59649],[30.64992,51.35014],[30.56203,51.25655],[30.36153,51.33984],[30.34642,51.42555],[30.17888,51.51025],[29.77376,51.4461],[29.7408,51.53417],[29.54372,51.48372],[29.49773,51.39814],[29.42357,51.4187],[29.32881,51.37843],[29.25191,51.49828],[29.25603,51.57089],[29.20659,51.56918],[29.16402,51.64679],[29.1187,51.65872],[28.99098,51.56833],[28.95528,51.59222],[28.81795,51.55552],[28.76027,51.48802],[28.78224,51.45294],[28.75615,51.41442],[28.73143,51.46236],[28.69161,51.44695],[28.64429,51.5664],[28.47051,51.59734],[28.37592,51.54505],[28.23452,51.66988],[28.10658,51.57857],[27.95827,51.56065],[27.91844,51.61952],[27.85253,51.62293],[27.76052,51.47604],[27.67125,51.50854],[27.71932,51.60672],[27.55727,51.63486],[27.51058,51.5854],[27.47212,51.61184],[27.24828,51.60161],[27.26613,51.65957],[27.20948,51.66713],[27.20602,51.77291],[26.99422,51.76933],[26.9489,51.73788],[26.80043,51.75777],[26.69759,51.82284],[26.46962,51.80501],[26.39367,51.87315],[26.19084,51.86781],[26.00408,51.92967],[25.83217,51.92587],[25.80574,51.94556],[25.73673,51.91973],[25.46163,51.92205],[25.20228,51.97143],[24.98784,51.91273],[24.37123,51.88222],[24.29021,51.80841],[24.3163,51.75063],[24.13075,51.66979],[23.99907,51.58369],[23.8741,51.59734],[23.91118,51.63316],[23.7766,51.66809],[23.60906,51.62122],[23.6736,51.50255],[23.62751,51.50512],[23.69905,51.40871],[23.63858,51.32182],[23.80678,51.18405],[23.90376,51.07697],[23.92217,51.00836],[24.04576,50.90196],[24.14524,50.86128],[24.0952,50.83262],[23.99254,50.83847],[23.95925,50.79271],[24.0595,50.71625],[24.0996,50.60752],[24.07048,50.5071],[24.03668,50.44507],[23.99563,50.41289],[23.79445,50.40481],[23.71382,50.38248],[23.67635,50.33385],[23.28221,50.0957],[22.99329,49.84249],[22.83179,49.69875],[22.80261,49.69098],[22.78304,49.65543],[22.64534,49.53094],[22.69444,49.49378],[22.748,49.32759],[22.72009,49.20288],[22.86336,49.10513],[22.89122,49.00725],[22.56155,49.08865],[22.54338,49.01424],[22.48296,48.99172],[22.42934,48.92857],[22.34151,48.68893],[22.21379,48.6218],[22.16023,48.56548],[22.14689,48.4005],[22.2083,48.42534],[22.38133,48.23726],[22.49806,48.25189],[22.59007,48.15121],[22.58733,48.10813],[22.66835,48.09162],[22.73427,48.12005],[22.81804,48.11363],[22.87847,48.04665],[22.84276,47.98602],[22.89849,47.95851],[22.94301,47.96672],[22.92241,48.02002],[23.0158,47.99338],[23.08858,48.00716],[23.1133,48.08061],[23.15999,48.12188],[23.27397,48.08245],[23.33577,48.0237],[23.4979,47.96858],[23.52803,48.01818],[23.5653,48.00499],[23.63894,48.00293],[23.66262,47.98786],[23.75188,47.99705],[23.80904,47.98142],[23.8602,47.9329],[23.89352,47.94512],[23.94192,47.94868],[23.96337,47.96672],[23.98553,47.96076],[24.00801,47.968],[24.02999,47.95087],[24.06466,47.95317],[24.11281,47.91487],[24.22566,47.90231],[24.34926,47.9244],[24.43578,47.97131],[24.61994,47.95062],[24.70632,47.84428],[24.81893,47.82031],[24.88896,47.7234],[25.11144,47.75203],[25.23778,47.89403],[25.63878,47.94924],[25.77723,47.93919],[26.05901,47.9897],[26.17711,47.99246],[26.33504,48.18418],[26.55202,48.22445],[26.62823,48.25804],[26.6839,48.35828],[26.79239,48.29071],[26.82809,48.31629],[26.71274,48.40388],[26.85556,48.41095],[26.93384,48.36558],[27.03821,48.37653],[27.0231,48.42485],[27.08078,48.43214],[27.13434,48.37288],[27.27855,48.37534],[27.32159,48.4434],[27.37604,48.44398],[27.37741,48.41026],[27.44333,48.41209],[27.46942,48.454],[27.5889,48.49224],[27.59027,48.46311],[27.6658,48.44034],[27.74422,48.45926],[27.79225,48.44244],[27.81902,48.41874],[27.87533,48.4037],[27.88391,48.36699],[27.95883,48.32368],[28.04527,48.32661],[28.09873,48.3124],[28.07504,48.23494],[28.17666,48.25963],[28.19314,48.20749],[28.2856,48.23202],[28.32508,48.23384],[28.35519,48.24957],[28.36996,48.20543],[28.34912,48.1787],[28.30586,48.1597],[28.30609,48.14018],[28.34009,48.13147],[28.38712,48.17567],[28.43701,48.15832],[28.42454,48.12047],[28.48428,48.0737],[28.53921,48.17453],[28.69896,48.13106],[28.85232,48.12506],[28.8414,48.03392],[28.9306,47.96255],[29.1723,47.99013],[29.19839,47.89261],[29.27804,47.88893],[29.20663,47.80367],[29.27255,47.79953],[29.22242,47.73607],[29.22414,47.60012],[29.11743,47.55001],[29.18603,47.43387],[29.3261,47.44664],[29.39889,47.30179],[29.47854,47.30366],[29.48678,47.36043],[29.5733,47.36508],[29.59665,47.25521],[29.54996,47.24962],[29.57696,47.13581],[29.49732,47.12878],[29.53044,47.07851],[29.61038,47.09932],[29.62137,47.05069],[29.57056,46.94766],[29.72986,46.92234],[29.75458,46.8604],[29.87405,46.88199],[29.98814,46.82358],[29.94522,46.80055],[29.9743,46.75325],[29.94409,46.56002],[29.88916,46.54302],[30.02511,46.45132],[30.16794,46.40967],[30.09103,46.38694],[29.94114,46.40114],[29.88329,46.35851],[29.74496,46.45605],[29.66359,46.4215],[29.6763,46.36041],[29.5939,46.35472],[29.49914,46.45889],[29.35357,46.49505],[29.24886,46.37912],[29.23547,46.55435],[29.02409,46.49582],[29.01241,46.46177],[28.9306,46.45699],[29.004,46.31495],[28.98478,46.31803],[28.94953,46.25852],[29.06656,46.19716],[28.94643,46.09176],[29.00613,46.04962],[28.98004,46.00385],[28.74383,45.96664],[28.78503,45.83475],[28.69852,45.81753],[28.70401,45.78019],[28.52823,45.73803],[28.47879,45.66994],[28.51587,45.6613],[28.54196,45.58062],[28.49252,45.56716],[28.51449,45.49982],[28.43072,45.48538],[28.41836,45.51715],[28.30201,45.54744],[28.21139,45.46895],[28.28504,45.43907],[28.34554,45.32102],[28.5735,45.24759],[28.71358,45.22631],[28.78911,45.24179],[28.81383,45.3384],[28.94292,45.28045],[28.96077,45.33164],[29.24779,45.43388],[29.42632,45.44545],[29.59798,45.38857],[29.68175,45.26885],[29.65428,45.25629],[29.69272,45.19227],[30.04414,45.08461],[31.62627,45.50633],[33.54017,46.0123],[33.59087,46.06013],[33.57318,46.10317]]]]}},{type:"Feature",properties:{iso1A2:"UG",iso1A3:"UGA",iso1N3:"800",wikidata:"Q1036",nameEn:"Uganda",groups:["014","202","002"],driveSide:"left",callingCodes:["256"]},geometry:{type:"MultiPolygon",coordinates:[[[[33.93107,-0.99298],[33.9264,-0.54188],[33.98449,-0.13079],[33.90936,0.10581],[34.10067,0.36372],[34.08727,0.44713],[34.11408,0.48884],[34.13493,0.58118],[34.20196,0.62289],[34.27345,0.63182],[34.31516,0.75693],[34.40041,0.80266],[34.43349,0.85254],[34.52369,1.10692],[34.57427,1.09868],[34.58029,1.14712],[34.67562,1.21265],[34.80223,1.22754],[34.82606,1.26626],[34.82606,1.30944],[34.7918,1.36752],[34.87819,1.5596],[34.92734,1.56109],[34.9899,1.6668],[34.98692,1.97348],[34.90947,2.42447],[34.95267,2.47209],[34.77244,2.70272],[34.78137,2.76223],[34.73967,2.85447],[34.65774,2.8753],[34.60114,2.93034],[34.56242,3.11478],[34.45815,3.18319],[34.40006,3.37949],[34.41794,3.44342],[34.39112,3.48802],[34.44922,3.51627],[34.45815,3.67385],[34.15429,3.80464],[34.06046,4.15235],[33.9873,4.23316],[33.51264,3.75068],[33.18356,3.77812],[33.02852,3.89296],[32.89746,3.81339],[32.72021,3.77327],[32.41337,3.748],[32.20782,3.6053],[32.19888,3.50867],[32.08866,3.53543],[32.08491,3.56287],[32.05187,3.589],[31.95907,3.57408],[31.96205,3.6499],[31.86821,3.78664],[31.81459,3.82083],[31.72075,3.74354],[31.50776,3.63652],[31.50478,3.67814],[31.29476,3.8015],[31.16666,3.79853],[30.97601,3.693],[30.85153,3.48867],[30.94081,3.50847],[30.93486,3.40737],[30.84251,3.26908],[30.77101,3.04897],[30.8574,2.9508],[30.8857,2.83923],[30.75612,2.5863],[30.74271,2.43601],[30.83059,2.42559],[30.91102,2.33332],[30.96911,2.41071],[31.06593,2.35862],[31.07934,2.30207],[31.12104,2.27676],[31.1985,2.29462],[31.20148,2.2217],[31.28042,2.17853],[31.30127,2.11006],[30.48503,1.21675],[30.24671,1.14974],[30.22139,0.99635],[30.1484,0.89805],[29.98307,0.84295],[29.95477,0.64486],[29.97413,0.52124],[29.87284,0.39166],[29.81922,0.16824],[29.77454,0.16675],[29.7224,0.07291],[29.72687,-0.08051],[29.65091,-0.46777],[29.67474,-0.47969],[29.67176,-0.55714],[29.62708,-0.71055],[29.63006,-0.8997],[29.58388,-0.89821],[29.59061,-1.39016],[29.82657,-1.31187],[29.912,-1.48269],[30.16369,-1.34303],[30.35212,-1.06896],[30.47194,-1.0555],[30.64166,-1.06601],[30.70631,-1.01175],[30.76635,-0.9852],[30.80408,-0.99911],[33.93107,-0.99298]]]]}},{type:"Feature",properties:{iso1A2:"UM",iso1A3:"UMI",iso1N3:"581",wikidata:"Q16645",nameEn:"United States Minor Outlying Islands",country:"US",groups:["057","009"]},geometry:{type:"MultiPolygon",coordinates:[[[[-175.33482,-1.40631],[-175.33167,1.67574],[-177.43928,1.65656],[-177.43039,-1.43294],[-175.33482,-1.40631]]],[[[-161.04969,-1.36251],[-158.62058,-1.35506],[-158.62734,1.1296],[-161.05669,1.11722],[-161.04969,-1.36251]]],[[[-161.06795,5.2462],[-161.0731,7.1291],[-163.24994,7.12322],[-163.24478,5.24198],[-161.06795,5.2462]]],[[[-170.65691,16.57199],[-168.87689,16.01159],[-169.2329,17.4933],[-170.65691,16.57199]]],[[[-176.29741,29.09786],[-177.77531,29.29793],[-177.5224,27.7635],[-176.29741,29.09786]]],[[[-74.7289,18.71009],[-75.71816,18.46438],[-74.76465,18.06252],[-74.7289,18.71009]]],[[[167.34779,18.97692],[166.67967,20.14834],[165.82549,18.97692],[167.34779,18.97692]]]]}},{type:"Feature",properties:{iso1A2:"US",iso1A3:"USA",iso1N3:"840",wikidata:"Q30",nameEn:"United States of America",groups:["021","003","019"],roadSpeedUnit:"mph",callingCodes:["1"]},geometry:{type:"MultiPolygon",coordinates:[[[[-177.8563,29.18961],[-179.49839,27.86265],[-151.6784,9.55515],[-154.05867,45.51124],[-177.5224,27.7635],[-177.8563,29.18961]]],[[[169.34848,52.47228],[180,51.0171],[179.84401,55.10087],[169.34848,52.47228]]],[[[-168.95635,65.98512],[-169.03888,65.48473],[-172.76104,63.77445],[-179.55295,57.62081],[-179.55295,50.81807],[-133.92876,54.62289],[-130.61931,54.70835],[-130.64499,54.76912],[-130.44184,54.85377],[-130.27203,54.97174],[-130.18765,55.07744],[-130.08035,55.21556],[-129.97513,55.28029],[-130.15373,55.74895],[-130.00857,55.91344],[-130.00093,56.00325],[-130.10173,56.12178],[-130.33965,56.10849],[-130.77769,56.36185],[-131.8271,56.62247],[-133.38523,58.42773],[-133.84645,58.73543],[-134.27175,58.8634],[-134.48059,59.13231],[-134.55699,59.1297],[-134.7047,59.2458],[-135.00267,59.28745],[-135.03069,59.56208],[-135.48007,59.79937],[-136.31566,59.59083],[-136.22381,59.55526],[-136.33727,59.44466],[-136.47323,59.46617],[-136.52365,59.16752],[-136.82619,59.16198],[-137.4925,58.89415],[-137.60623,59.24465],[-138.62145,59.76431],[-138.71149,59.90728],[-139.05365,59.99655],[-139.20603,60.08896],[-139.05831,60.35205],[-139.68991,60.33693],[-139.98024,60.18027],[-140.45648,60.30919],[-140.5227,60.22077],[-141.00116,60.30648],[-140.97446,84.39275],[-168.25765,71.99091],[-168.95635,65.98512]]],[[[-97.13927,25.96583],[-96.92418,25.97377],[-82.02215,24.23074],[-79.89631,24.6597],[-79.14818,27.83105],[-61.98255,37.34815],[-67.16117,44.20069],[-66.93432,44.82597],[-66.96824,44.83078],[-66.98249,44.87071],[-66.96824,44.90965],[-67.0216,44.95333],[-67.11316,45.11176],[-67.15965,45.16179],[-67.19603,45.16771],[-67.20349,45.1722],[-67.22751,45.16344],[-67.27039,45.1934],[-67.29748,45.18173],[-67.29754,45.14865],[-67.34927,45.122],[-67.48201,45.27351],[-67.42394,45.37969],[-67.50578,45.48971],[-67.42144,45.50584],[-67.43815,45.59162],[-67.6049,45.60725],[-67.80705,45.69528],[-67.80653,45.80022],[-67.75654,45.82324],[-67.80961,45.87531],[-67.75196,45.91814],[-67.78111,45.9392],[-67.78578,47.06473],[-67.87993,47.10377],[-67.94843,47.1925],[-68.23244,47.35712],[-68.37458,47.35851],[-68.38332,47.28723],[-68.57914,47.28431],[-68.60575,47.24659],[-68.70125,47.24399],[-68.89222,47.1807],[-69.05039,47.2456],[-69.05073,47.30076],[-69.05148,47.42012],[-69.22119,47.46461],[-69.99966,46.69543],[-70.05812,46.41768],[-70.18547,46.35357],[-70.29078,46.18832],[-70.23855,46.1453],[-70.31025,45.96424],[-70.24694,45.95138],[-70.25976,45.89675],[-70.41523,45.79497],[-70.38934,45.73215],[-70.54019,45.67291],[-70.68516,45.56964],[-70.72651,45.49771],[-70.62518,45.42286],[-70.65383,45.37592],[-70.78372,45.43269],[-70.82638,45.39828],[-70.80236,45.37444],[-70.84816,45.22698],[-70.89864,45.2398],[-70.91169,45.29849],[-70.95193,45.33895],[-71.0107,45.34819],[-71.01866,45.31573],[-71.08364,45.30623],[-71.14568,45.24128],[-71.19723,45.25438],[-71.22338,45.25184],[-71.29371,45.29996],[-71.37133,45.24624],[-71.44252,45.2361],[-71.40364,45.21382],[-71.42778,45.12624],[-71.48735,45.07784],[-71.50067,45.01357],[-73.35025,45.00942],[-74.32699,44.99029],[-74.66689,45.00646],[-74.8447,45.00606],[-74.99101,44.98051],[-75.01363,44.95608],[-75.2193,44.87821],[-75.41441,44.76614],[-75.76813,44.51537],[-75.8217,44.43176],[-75.95947,44.34463],[-76.00018,44.34896],[-76.16285,44.28262],[-76.1664,44.23051],[-76.244,44.19643],[-76.31222,44.19894],[-76.35324,44.13493],[-76.43859,44.09393],[-76.79706,43.63099],[-79.25796,43.54052],[-79.06921,43.26183],[-79.05512,43.25375],[-79.05544,43.21224],[-79.05002,43.20133],[-79.05384,43.17418],[-79.04652,43.16396],[-79.0427,43.13934],[-79.06881,43.12029],[-79.05671,43.10937],[-79.07486,43.07845],[-79.01055,43.06659],[-78.99941,43.05612],[-79.02424,43.01983],[-79.02074,42.98444],[-78.98126,42.97],[-78.96312,42.95509],[-78.93224,42.95229],[-78.90905,42.93022],[-78.90712,42.89733],[-78.93684,42.82887],[-82.67862,41.67615],[-83.11184,41.95671],[-83.14962,42.04089],[-83.12724,42.2376],[-83.09837,42.28877],[-83.07837,42.30978],[-83.02253,42.33045],[-82.82964,42.37355],[-82.64242,42.55594],[-82.58873,42.54984],[-82.57583,42.5718],[-82.51858,42.611],[-82.51063,42.66025],[-82.46613,42.76615],[-82.4826,42.8068],[-82.45331,42.93139],[-82.4253,42.95423],[-82.4146,42.97626],[-82.42469,42.992],[-82.48419,45.30225],[-83.59589,45.82131],[-83.43746,45.99749],[-83.57017,46.105],[-83.83329,46.12169],[-83.90453,46.05922],[-83.95399,46.05634],[-84.1096,46.23987],[-84.09756,46.25512],[-84.11615,46.2681],[-84.11254,46.32329],[-84.13451,46.39218],[-84.11196,46.50248],[-84.12885,46.53068],[-84.17723,46.52753],[-84.1945,46.54061],[-84.2264,46.53337],[-84.26351,46.49508],[-84.29893,46.49127],[-84.34174,46.50683],[-84.42101,46.49853],[-84.4481,46.48972],[-84.47607,46.45225],[-84.55635,46.45974],[-84.85871,46.88881],[-88.37033,48.30586],[-89.48837,48.01412],[-89.57972,48.00023],[-89.77248,48.02607],[-89.89974,47.98109],[-90.07418,48.11043],[-90.56312,48.09488],[-90.56444,48.12184],[-90.75045,48.09143],[-90.87588,48.2484],[-91.08016,48.18096],[-91.25025,48.08522],[-91.43248,48.04912],[-91.45829,48.07454],[-91.58025,48.04339],[-91.55649,48.10611],[-91.70451,48.11805],[-91.71231,48.19875],[-91.86125,48.21278],[-91.98929,48.25409],[-92.05339,48.35958],[-92.14732,48.36578],[-92.202,48.35252],[-92.26662,48.35651],[-92.30939,48.31251],[-92.27167,48.25046],[-92.37185,48.22259],[-92.48147,48.36609],[-92.45588,48.40624],[-92.50712,48.44921],[-92.65606,48.43471],[-92.71323,48.46081],[-92.69927,48.49573],[-92.62747,48.50278],[-92.6342,48.54133],[-92.7287,48.54005],[-92.94973,48.60866],[-93.25391,48.64266],[-93.33946,48.62787],[-93.3712,48.60599],[-93.39758,48.60364],[-93.40693,48.60948],[-93.44472,48.59147],[-93.47022,48.54357],[-93.66382,48.51845],[-93.79267,48.51631],[-93.80939,48.52439],[-93.80676,48.58232],[-93.83288,48.62745],[-93.85769,48.63284],[-94.23215,48.65202],[-94.25104,48.65729],[-94.25172,48.68404],[-94.27153,48.70232],[-94.4174,48.71049],[-94.44258,48.69223],[-94.53826,48.70216],[-94.54885,48.71543],[-94.58903,48.71803],[-94.69335,48.77883],[-94.69669,48.80918],[-94.70486,48.82365],[-94.70087,48.8339],[-94.687,48.84077],[-94.75017,49.09931],[-94.77355,49.11998],[-94.82487,49.29483],[-94.8159,49.32299],[-94.85381,49.32492],[-94.95681,49.37035],[-94.99532,49.36579],[-95.01419,49.35647],[-95.05825,49.35311],[-95.12903,49.37056],[-95.15357,49.384],[-95.15355,48.9996],[-97.24024,48.99952],[-101.36198,48.99935],[-104.05004,48.99925],[-110.0051,48.99901],[-114.0683,48.99885],[-116.04938,48.99999],[-117.03266,49.00056],[-123.32163,49.00419],[-123.0093,48.83186],[-123.0093,48.76586],[-123.26565,48.6959],[-123.15614,48.35395],[-123.50039,48.21223],[-125.03842,48.53282],[-133.98258,38.06389],[-118.48109,32.5991],[-117.1243,32.53427],[-115.88053,32.63624],[-114.71871,32.71894],[-114.76736,32.64094],[-114.80584,32.62028],[-114.81141,32.55543],[-114.79524,32.55731],[-114.82011,32.49609],[-112.34553,31.7357],[-111.07523,31.33232],[-109.05235,31.3333],[-108.20979,31.33316],[-108.20899,31.78534],[-106.529,31.784],[-106.52266,31.77509],[-106.51251,31.76922],[-106.50962,31.76155],[-106.50111,31.75714],[-106.48815,31.74769],[-106.47298,31.75054],[-106.46726,31.75998],[-106.45244,31.76523],[-106.43419,31.75478],[-106.41773,31.75196],[-106.38003,31.73151],[-106.3718,31.71165],[-106.34864,31.69663],[-106.33419,31.66303],[-106.30305,31.62154],[-106.28084,31.56173],[-106.24612,31.54193],[-106.23711,31.51262],[-106.20346,31.46305],[-106.09025,31.40569],[-106.00363,31.39181],[-104.77674,30.4236],[-104.5171,29.64671],[-104.3969,29.57105],[-104.39363,29.55396],[-104.37752,29.54255],[-103.15787,28.93865],[-102.60596,29.8192],[-101.47277,29.7744],[-101.05686,29.44738],[-101.01128,29.36947],[-100.96725,29.3477],[-100.94579,29.34523],[-100.94056,29.33371],[-100.87982,29.296],[-100.79696,29.24688],[-100.67294,29.09744],[-100.63689,28.90812],[-100.59809,28.88197],[-100.52313,28.75598],[-100.5075,28.74066],[-100.51222,28.70679],[-100.50029,28.66117],[-99.55409,27.61314],[-99.51478,27.55836],[-99.52955,27.49747],[-99.50208,27.50021],[-99.48045,27.49016],[-99.482,27.47128],[-99.49744,27.43746],[-99.53573,27.30926],[-99.08477,26.39849],[-99.03053,26.41249],[-99.00546,26.3925],[-98.35126,26.15129],[-98.30491,26.10475],[-98.27075,26.09457],[-98.24603,26.07191],[-97.97017,26.05232],[-97.95155,26.0625],[-97.66511,26.01708],[-97.52025,25.88518],[-97.49828,25.89877],[-97.45669,25.86874],[-97.42511,25.83969],[-97.37332,25.83854],[-97.35946,25.92189],[-97.13927,25.96583]]]]}},{type:"Feature",properties:{iso1A2:"UY",iso1A3:"URY",iso1N3:"858",wikidata:"Q77",nameEn:"Uruguay",groups:["005","419","019"],callingCodes:["598"]},geometry:{type:"MultiPolygon",coordinates:[[[[-57.65132,-30.19229],[-57.61478,-30.25165],[-57.64859,-30.35095],[-57.89115,-30.49572],[-57.8024,-30.77193],[-57.89476,-30.95994],[-57.86729,-31.06352],[-57.9908,-31.34924],[-57.98127,-31.3872],[-58.07569,-31.44916],[-58.0023,-31.53084],[-58.00076,-31.65016],[-58.20252,-31.86966],[-58.10036,-32.25338],[-58.22362,-32.52416],[-58.1224,-32.98842],[-58.40475,-33.11777],[-58.44442,-33.84033],[-58.34425,-34.15035],[-57.83001,-34.69099],[-54.78916,-36.21945],[-52.83257,-34.01481],[-53.37138,-33.74313],[-53.39593,-33.75169],[-53.44031,-33.69344],[-53.52794,-33.68908],[-53.53459,-33.16843],[-53.1111,-32.71147],[-53.37671,-32.57005],[-53.39572,-32.58596],[-53.76024,-32.0751],[-54.17384,-31.86168],[-55.50821,-30.91349],[-55.50841,-30.9027],[-55.51862,-30.89828],[-55.52712,-30.89997],[-55.53276,-30.90218],[-55.53431,-30.89714],[-55.54572,-30.89051],[-55.55218,-30.88193],[-55.55373,-30.8732],[-55.5634,-30.8686],[-55.58866,-30.84117],[-55.87388,-31.05053],[-56.4619,-30.38457],[-56.4795,-30.3899],[-56.49267,-30.39471],[-56.90236,-30.02578],[-57.22502,-30.26121],[-57.65132,-30.19229]]]]}},{type:"Feature",properties:{iso1A2:"UZ",iso1A3:"UZB",iso1N3:"860",wikidata:"Q265",nameEn:"Uzbekistan",groups:["143","142"],callingCodes:["998"]},geometry:{type:"MultiPolygon",coordinates:[[[[65.85194,42.85481],[65.53277,43.31856],[65.18666,43.48835],[64.96464,43.74748],[64.53885,43.56941],[63.34656,43.64003],[62.01711,43.51008],[61.01475,44.41383],[58.59711,45.58671],[55.97842,44.99622],[55.97832,44.99622],[55.97822,44.99617],[55.97811,44.99617],[55.97801,44.99612],[55.97801,44.99607],[55.97791,44.99607],[55.9778,44.99607],[55.9777,44.99601],[55.9777,44.99596],[55.9776,44.99591],[55.97749,44.99591],[55.97739,44.99591],[55.97739,44.99586],[55.97729,44.99586],[55.97718,44.99581],[55.97708,44.99576],[55.97698,44.9957],[55.97698,44.99565],[55.97687,44.9956],[55.97677,44.9956],[55.97677,44.99555],[55.97677,44.9955],[55.97667,44.99545],[55.97656,44.99539],[55.97646,44.99534],[55.97646,44.99529],[55.97636,44.99524],[55.97636,44.99519],[55.97625,44.99514],[55.97615,44.99508],[55.97615,44.99503],[55.97615,44.99498],[55.97615,44.99493],[55.97615,44.99483],[55.97615,44.99477],[55.97605,44.99477],[55.97605,44.99467],[55.97605,44.99462],[55.97605,44.99457],[55.97605,44.99452],[55.97594,44.99446],[55.97584,44.99441],[55.97584,44.99436],[55.97584,44.99431],[55.97584,44.99426],[55.97584,44.99421],[55.97584,44.99415],[55.97584,44.99405],[55.97584,44.994],[55.97584,44.9939],[55.97584,44.99384],[55.97584,44.99374],[55.97584,44.99369],[55.97584,44.99359],[55.97584,44.99353],[55.97584,44.99348],[55.97584,44.99343],[55.97584,44.99338],[55.97584,44.99328],[55.97584,44.99322],[56.00314,41.32584],[57.03423,41.25435],[57.13796,41.36625],[57.03359,41.41777],[56.96218,41.80383],[57.03633,41.92043],[57.30275,42.14076],[57.6296,42.16519],[57.84932,42.18555],[57.92897,42.24047],[57.90975,42.4374],[57.99214,42.50021],[58.3492,42.43335],[58.40688,42.29535],[58.51674,42.30348],[58.29427,42.56497],[58.14321,42.62159],[58.27504,42.69632],[58.57991,42.64988],[58.6266,42.79314],[58.93422,42.5407],[59.17317,42.52248],[59.2955,42.37064],[59.4341,42.29738],[59.94633,42.27655],[60.00539,42.212],[59.96419,42.1428],[60.04659,42.08982],[60.0356,42.01028],[59.95046,41.97966],[60.33223,41.75058],[60.08504,41.80997],[60.06032,41.76287],[60.18117,41.60082],[60.06581,41.4363],[60.5078,41.21694],[61.03261,41.25691],[61.22212,41.14946],[61.33199,41.14946],[61.39732,41.19873],[61.4446,41.29407],[61.87856,41.12257],[62.11751,40.58242],[62.34273,40.43206],[62.43337,39.98528],[63.6913,39.27666],[63.70778,39.22349],[64.19086,38.95561],[64.32576,38.98691],[65.55873,38.29052],[65.83913,38.25733],[66.24013,38.16238],[66.41042,38.02403],[66.56697,38.0435],[66.67684,37.96776],[66.53676,37.80084],[66.52852,37.58568],[66.65761,37.45497],[66.52303,37.39827],[66.55743,37.35409],[66.64699,37.32958],[66.95598,37.40162],[67.08232,37.35469],[67.13039,37.27168],[67.2224,37.24545],[67.2581,37.17216],[67.51868,37.26102],[67.78329,37.1834],[67.8474,37.31594],[67.81566,37.43107],[68.12635,37.93],[68.27159,37.91477],[68.40343,38.19484],[68.13289,38.40822],[68.06274,38.39435],[68.11366,38.47169],[68.05873,38.56087],[68.0807,38.64136],[68.05598,38.71641],[68.12877,38.73677],[68.06948,38.82115],[68.19743,38.85985],[68.09704,39.02589],[67.68915,39.00775],[67.67833,39.14479],[67.33226,39.23739],[67.36522,39.31287],[67.45998,39.315],[67.46822,39.46146],[67.39681,39.52505],[67.46547,39.53564],[67.44899,39.57799],[67.62889,39.60234],[67.70992,39.66156],[68.12053,39.56317],[68.54166,39.53929],[68.61972,39.68905],[68.63071,39.85265],[68.88889,39.87163],[68.93695,39.91167],[68.84906,40.04952],[68.96579,40.06949],[69.01935,40.11466],[69.01523,40.15771],[68.62796,40.07789],[68.52771,40.11676],[68.5332,40.14826],[68.77902,40.20492],[68.79276,40.17555],[68.84357,40.18604],[68.85832,40.20885],[69.04544,40.22904],[69.15659,40.2162],[69.2074,40.21488],[69.30448,40.18774],[69.30104,40.24502],[69.25229,40.26362],[69.24817,40.30357],[69.30808,40.2821],[69.32833,40.29794],[69.33794,40.34819],[69.30774,40.36102],[69.28525,40.41894],[69.27066,40.49274],[69.21063,40.54469],[69.2643,40.57506],[69.3455,40.57988],[69.32834,40.70233],[69.38327,40.7918],[69.53021,40.77621],[69.59441,40.70181],[69.69434,40.62615],[70.36655,40.90296],[70.38028,41.02014],[70.45251,41.04438],[70.80009,40.72825],[70.49871,40.52503],[70.32626,40.45174],[70.37511,40.38605],[70.57149,40.3442],[70.56394,40.26421],[70.62342,40.17396],[70.8607,40.217],[70.9818,40.22392],[70.95789,40.28761],[71.05901,40.28765],[71.13042,40.34106],[71.36663,40.31593],[71.4246,40.28619],[71.51215,40.26943],[71.51549,40.22986],[71.61725,40.20615],[71.61931,40.26775],[71.68386,40.26984],[71.70569,40.20391],[71.69621,40.18492],[71.71719,40.17886],[71.73054,40.14818],[71.82646,40.21872],[71.85002,40.25647],[72.05464,40.27586],[71.96401,40.31907],[72.18648,40.49893],[72.24368,40.46091],[72.40346,40.4007],[72.44191,40.48222],[72.41513,40.50856],[72.38384,40.51535],[72.41714,40.55736],[72.34406,40.60144],[72.40517,40.61917],[72.47795,40.5532],[72.66713,40.5219],[72.66713,40.59076],[72.69579,40.59778],[72.73995,40.58409],[72.74768,40.58051],[72.74862,40.57131],[72.75982,40.57273],[72.74894,40.59592],[72.74866,40.60873],[72.80137,40.67856],[72.84754,40.67229],[72.85372,40.7116],[72.8722,40.71111],[72.93296,40.73089],[72.99133,40.76457],[73.0612,40.76678],[73.13412,40.79122],[73.13267,40.83512],[73.01869,40.84681],[72.94454,40.8094],[72.84291,40.85512],[72.68157,40.84942],[72.59136,40.86947],[72.55109,40.96046],[72.48742,40.97136],[72.45206,41.03018],[72.38511,41.02785],[72.36138,41.04384],[72.34757,41.06104],[72.34026,41.04539],[72.324,41.03381],[72.18339,40.99571],[72.17594,41.02377],[72.21061,41.05607],[72.1792,41.10621],[72.14864,41.13363],[72.17594,41.15522],[72.16433,41.16483],[72.10745,41.15483],[72.07249,41.11739],[71.85964,41.19081],[71.91457,41.2982],[71.83914,41.3546],[71.76625,41.4466],[71.71132,41.43012],[71.73054,41.54713],[71.65914,41.49599],[71.6787,41.42111],[71.57227,41.29175],[71.46688,41.31883],[71.43814,41.19644],[71.46148,41.13958],[71.40198,41.09436],[71.34877,41.16807],[71.27187,41.11015],[71.25813,41.18796],[71.11806,41.15359],[71.02193,41.19494],[70.9615,41.16393],[70.86263,41.23833],[70.77885,41.24813],[70.78572,41.36419],[70.67586,41.47953],[70.48909,41.40335],[70.17682,41.5455],[70.69777,41.92554],[71.28719,42.18033],[71.13263,42.28356],[70.94483,42.26238],[69.49545,41.545],[69.45751,41.56863],[69.39485,41.51518],[69.45081,41.46246],[69.37468,41.46555],[69.35554,41.47211],[69.29778,41.43673],[69.25059,41.46693],[69.23332,41.45847],[69.22671,41.46298],[69.20439,41.45391],[69.18528,41.45175],[69.17701,41.43769],[69.15137,41.43078],[69.05006,41.36183],[69.01308,41.22804],[68.7217,41.05025],[68.73945,40.96989],[68.65662,40.93861],[68.62221,41.03019],[68.49983,40.99669],[68.58444,40.91447],[68.63,40.59358],[68.49983,40.56437],[67.96736,40.83798],[68.1271,41.0324],[68.08273,41.08148],[67.98511,41.02794],[67.9644,41.14611],[66.69129,41.1311],[66.53302,41.87388],[66.00546,41.94455],[66.09482,42.93426],[65.85194,42.85481]],[[70.68112,40.90612],[70.6721,40.90555],[70.57501,40.98941],[70.54223,40.98787],[70.56077,41.00642],[70.6158,40.97661],[70.68112,40.90612]]],[[[71.21139,40.03369],[71.12218,40.03052],[71.06305,40.1771],[71.00236,40.18154],[71.01035,40.05481],[71.11037,40.01984],[71.11668,39.99291],[71.09063,39.99],[71.10501,39.95568],[71.04979,39.89808],[71.10531,39.91354],[71.16101,39.88423],[71.23067,39.93581],[71.1427,39.95026],[71.21139,40.03369]]],[[[71.86463,39.98598],[71.78838,40.01404],[71.71511,39.96348],[71.7504,39.93701],[71.84316,39.95582],[71.86463,39.98598]]]]}},{type:"Feature",properties:{iso1A2:"VA",iso1A3:"VAT",iso1N3:"336",wikidata:"Q237",nameEn:"Vatican City",aliases:["Holy See"],groups:["039","150"],callingCodes:["379","39 06"]},geometry:{type:"MultiPolygon",coordinates:[[[[12.45181,41.90056],[12.45446,41.90028],[12.45435,41.90143],[12.45626,41.90172],[12.45691,41.90125],[12.4577,41.90115],[12.45834,41.90174],[12.45826,41.90281],[12.45755,41.9033],[12.45762,41.9058],[12.45561,41.90629],[12.45543,41.90738],[12.45091,41.90625],[12.44984,41.90545],[12.44815,41.90326],[12.44582,41.90194],[12.44834,41.90095],[12.45181,41.90056]]]]}},{type:"Feature",properties:{iso1A2:"VC",iso1A3:"VCT",iso1N3:"670",wikidata:"Q757",nameEn:"St. Vincent and the Grenadines",aliases:["WV"],groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 784"]},geometry:{type:"MultiPolygon",coordinates:[[[[-61.73897,12.61191],[-61.38256,12.52991],[-61.13395,12.51526],[-60.70539,13.41452],[-61.43129,13.68336],[-61.73897,12.61191]]]]}},{type:"Feature",properties:{iso1A2:"VE",iso1A3:"VEN",iso1N3:"862",wikidata:"Q717",nameEn:"Venezuela",aliases:["YV"],groups:["005","419","019"],callingCodes:["58"]},geometry:{type:"MultiPolygon",coordinates:[[[[-71.22331,13.01387],[-70.92579,11.96275],[-71.3275,11.85],[-71.9675,11.65536],[-72.24983,11.14138],[-72.4767,11.1117],[-72.88002,10.44309],[-72.98085,9.85253],[-73.36905,9.16636],[-73.02119,9.27584],[-72.94052,9.10663],[-72.77415,9.10165],[-72.65474,8.61428],[-72.4042,8.36513],[-72.36987,8.19976],[-72.35163,8.01163],[-72.39137,8.03534],[-72.47213,7.96106],[-72.48801,7.94329],[-72.48183,7.92909],[-72.47042,7.92306],[-72.45806,7.91141],[-72.46183,7.90682],[-72.44454,7.86031],[-72.46763,7.79518],[-72.47827,7.65604],[-72.45321,7.57232],[-72.47415,7.48928],[-72.43132,7.40034],[-72.19437,7.37034],[-72.04895,7.03837],[-71.82441,7.04314],[-71.44118,7.02116],[-71.42212,7.03854],[-71.37234,7.01588],[-71.03941,6.98163],[-70.7596,7.09799],[-70.10716,6.96516],[-69.41843,6.1072],[-67.60654,6.2891],[-67.4625,6.20625],[-67.43513,5.98835],[-67.58558,5.84537],[-67.63914,5.64963],[-67.59141,5.5369],[-67.83341,5.31104],[-67.85358,4.53249],[-67.62671,3.74303],[-67.50067,3.75812],[-67.30945,3.38393],[-67.85862,2.86727],[-67.85862,2.79173],[-67.65696,2.81691],[-67.21967,2.35778],[-66.85795,1.22998],[-66.28507,0.74585],[-65.6727,1.01353],[-65.50158,0.92086],[-65.57288,0.62856],[-65.11657,1.12046],[-64.38932,1.5125],[-64.34654,1.35569],[-64.08274,1.64792],[-64.06135,1.94722],[-63.39827,2.16098],[-63.39114,2.4317],[-64.0257,2.48156],[-64.02908,2.79797],[-64.48379,3.7879],[-64.84028,4.24665],[-64.72977,4.28931],[-64.57648,4.12576],[-64.14512,4.12932],[-63.99183,3.90172],[-63.86082,3.94796],[-63.70218,3.91417],[-63.67099,4.01731],[-63.50611,3.83592],[-63.42233,3.89995],[-63.4464,3.9693],[-63.21111,3.96219],[-62.98296,3.59935],[-62.7655,3.73099],[-62.74411,4.03331],[-62.57656,4.04754],[-62.44822,4.18621],[-62.13094,4.08309],[-61.54629,4.2822],[-61.48569,4.43149],[-61.29675,4.44216],[-61.31457,4.54167],[-61.15703,4.49839],[-60.98303,4.54167],[-60.86539,4.70512],[-60.5802,4.94312],[-60.73204,5.20931],[-61.4041,5.95304],[-61.15058,6.19558],[-61.20762,6.58174],[-61.13632,6.70922],[-60.54873,6.8631],[-60.39419,6.94847],[-60.28074,7.1162],[-60.44116,7.20817],[-60.54098,7.14804],[-60.63367,7.25061],[-60.59802,7.33194],[-60.71923,7.55817],[-60.64793,7.56877],[-60.51959,7.83373],[-60.38056,7.8302],[-60.02407,8.04557],[-59.97059,8.20791],[-59.83156,8.23261],[-59.80661,8.28906],[-59.85562,8.35213],[-59.98508,8.53046],[-59.54058,8.6862],[-60.89962,9.81445],[-62.08693,10.04435],[-61.62505,11.18974],[-63.73917,11.92623],[-63.19938,16.44103],[-67.89186,12.4116],[-68.01417,11.77722],[-68.33524,11.78151],[-68.99639,11.79035],[-71.22331,13.01387]]]]}},{type:"Feature",properties:{iso1A2:"VG",iso1A3:"VGB",iso1N3:"092",wikidata:"Q25305",nameEn:"British Virgin Islands",country:"GB",groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 284"]},geometry:{type:"MultiPolygon",coordinates:[[[[-64.03057,18.08241],[-63.75633,19.39745],[-65.02435,18.73231],[-64.86027,18.39056],[-64.64067,18.36478],[-64.646,18.10286],[-64.03057,18.08241]]]]}},{type:"Feature",properties:{iso1A2:"VI",iso1A3:"VIR",iso1N3:"850",wikidata:"Q11703",nameEn:"United States Virgin Islands",country:"US",groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 340"]},geometry:{type:"MultiPolygon",coordinates:[[[[-65.02435,18.73231],[-65.27974,17.56928],[-64.35558,17.48384],[-64.646,18.10286],[-64.64067,18.36478],[-64.86027,18.39056],[-65.02435,18.73231]]]]}},{type:"Feature",properties:{iso1A2:"VN",iso1A3:"VNM",iso1N3:"704",wikidata:"Q881",nameEn:"Vietnam",groups:["035","142"],callingCodes:["84"]},geometry:{type:"MultiPolygon",coordinates:[[[[108.10003,21.47338],[108.0569,21.53604],[108.02926,21.54997],[107.97932,21.54503],[107.97383,21.53961],[107.97074,21.54072],[107.96774,21.53601],[107.95232,21.5388],[107.92652,21.58906],[107.90006,21.5905],[107.86114,21.65128],[107.80355,21.66141],[107.66967,21.60787],[107.56537,21.61945],[107.54047,21.5934],[107.49065,21.59774],[107.49532,21.62958],[107.47197,21.6672],[107.41593,21.64839],[107.38636,21.59774],[107.35989,21.60063],[107.35834,21.6672],[107.29296,21.74674],[107.24625,21.7077],[107.20734,21.71493],[107.10771,21.79879],[107.02615,21.81981],[107.00964,21.85948],[107.06101,21.88982],[107.05634,21.92303],[106.99252,21.95191],[106.97228,21.92592],[106.92714,21.93459],[106.9178,21.97357],[106.81038,21.97934],[106.74345,22.00965],[106.72551,21.97923],[106.69276,21.96013],[106.68274,21.99811],[106.70142,22.02409],[106.6983,22.15102],[106.67495,22.1885],[106.69986,22.22309],[106.6516,22.33977],[106.55976,22.34841],[106.57221,22.37],[106.55665,22.46498],[106.58395,22.474],[106.61269,22.60301],[106.65316,22.5757],[106.71698,22.58432],[106.72321,22.63606],[106.76293,22.73491],[106.82404,22.7881],[106.83685,22.8098],[106.81271,22.8226],[106.78422,22.81532],[106.71128,22.85982],[106.71387,22.88296],[106.6734,22.89587],[106.6516,22.86862],[106.60179,22.92884],[106.55976,22.92311],[106.51306,22.94891],[106.49749,22.91164],[106.34961,22.86718],[106.27022,22.87722],[106.19705,22.98475],[106.00179,22.99049],[105.99568,22.94178],[105.90119,22.94168],[105.8726,22.92756],[105.72382,23.06641],[105.57594,23.075],[105.56037,23.16806],[105.49966,23.20669],[105.42805,23.30824],[105.40782,23.28107],[105.32376,23.39684],[105.22569,23.27249],[105.17276,23.28679],[105.11672,23.25247],[105.07002,23.26248],[104.98712,23.19176],[104.96532,23.20463],[104.9486,23.17235],[104.91435,23.18666],[104.87992,23.17141],[104.87382,23.12854],[104.79478,23.12934],[104.8334,23.01484],[104.86765,22.95178],[104.84942,22.93631],[104.77114,22.90017],[104.72755,22.81984],[104.65283,22.83419],[104.60457,22.81841],[104.58122,22.85571],[104.47225,22.75813],[104.35593,22.69353],[104.25683,22.76534],[104.27084,22.8457],[104.11384,22.80363],[104.03734,22.72945],[104.01088,22.51823],[103.99247,22.51958],[103.97384,22.50634],[103.96783,22.51173],[103.96352,22.50584],[103.95191,22.5134],[103.94513,22.52553],[103.93286,22.52703],[103.87904,22.56683],[103.64506,22.79979],[103.56255,22.69499],[103.57812,22.65764],[103.52675,22.59155],[103.43646,22.70648],[103.43179,22.75816],[103.32282,22.8127],[103.28079,22.68063],[103.18895,22.64471],[103.15782,22.59873],[103.17961,22.55705],[103.07843,22.50097],[103.0722,22.44775],[102.9321,22.48659],[102.8636,22.60735],[102.60675,22.73376],[102.57095,22.7036],[102.51802,22.77969],[102.46665,22.77108],[102.42618,22.69212],[102.38415,22.67919],[102.41061,22.64184],[102.25339,22.4607],[102.26428,22.41321],[102.16621,22.43336],[102.14099,22.40092],[102.18712,22.30403],[102.51734,22.02676],[102.49092,21.99002],[102.62301,21.91447],[102.67145,21.65894],[102.74189,21.66713],[102.82115,21.73667],[102.81894,21.83888],[102.85637,21.84501],[102.86077,21.71213],[102.97965,21.74076],[102.98846,21.58936],[102.86297,21.4255],[102.94223,21.46034],[102.88939,21.3107],[102.80794,21.25736],[102.89825,21.24707],[102.97745,21.05821],[103.03469,21.05821],[103.12055,20.89994],[103.21497,20.89832],[103.38032,20.79501],[103.45737,20.82382],[103.68633,20.66324],[103.73478,20.6669],[103.82282,20.8732],[103.98024,20.91531],[104.11121,20.96779],[104.27412,20.91433],[104.63957,20.6653],[104.38199,20.47155],[104.40621,20.3849],[104.47886,20.37459],[104.66158,20.47774],[104.72102,20.40554],[104.62195,20.36633],[104.61315,20.24452],[104.86852,20.14121],[104.91695,20.15567],[104.9874,20.09573],[104.8465,19.91783],[104.8355,19.80395],[104.68359,19.72729],[104.64837,19.62365],[104.53169,19.61743],[104.41281,19.70035],[104.23229,19.70242],[104.06498,19.66926],[104.05617,19.61743],[104.10832,19.51575],[104.06058,19.43484],[103.87125,19.31854],[104.5361,18.97747],[104.64617,18.85668],[105.12829,18.70453],[105.19654,18.64196],[105.1327,18.58355],[105.10408,18.43533],[105.15942,18.38691],[105.38366,18.15315],[105.46292,18.22008],[105.64784,17.96687],[105.60381,17.89356],[105.76612,17.67147],[105.85744,17.63221],[106.09019,17.36399],[106.18991,17.28227],[106.24444,17.24714],[106.29287,17.3018],[106.31929,17.20509],[106.43597,17.01362],[106.50862,16.9673],[106.55045,17.0031],[106.54824,16.92729],[106.51963,16.92097],[106.52183,16.87884],[106.55265,16.86831],[106.55485,16.68704],[106.59013,16.62259],[106.58267,16.6012],[106.61477,16.60713],[106.66052,16.56892],[106.65832,16.47816],[106.74418,16.41904],[106.84104,16.55415],[106.88727,16.52671],[106.88067,16.43594],[106.96638,16.34938],[106.97385,16.30204],[107.02597,16.31132],[107.09091,16.3092],[107.15035,16.26271],[107.14595,16.17816],[107.25822,16.13587],[107.33968,16.05549],[107.44975,16.08511],[107.46296,16.01106],[107.39471,15.88829],[107.34188,15.89464],[107.21419,15.83747],[107.21859,15.74638],[107.27143,15.71459],[107.27583,15.62769],[107.34408,15.62345],[107.3815,15.49832],[107.50699,15.48771],[107.53341,15.40496],[107.62367,15.42193],[107.60605,15.37524],[107.62587,15.2266],[107.58844,15.20111],[107.61926,15.13949],[107.61486,15.0566],[107.46516,15.00982],[107.48277,14.93751],[107.59285,14.87795],[107.51579,14.79282],[107.54361,14.69092],[107.55371,14.628],[107.52102,14.59034],[107.52569,14.54665],[107.48521,14.40346],[107.44941,14.41552],[107.39493,14.32655],[107.40427,14.24509],[107.33577,14.11832],[107.37158,14.07906],[107.35757,14.02319],[107.38247,13.99147],[107.44318,13.99751],[107.46498,13.91593],[107.45252,13.78897],[107.53503,13.73908],[107.61909,13.52577],[107.62843,13.3668],[107.49144,13.01215],[107.49611,12.88926],[107.55993,12.7982],[107.5755,12.52177],[107.55059,12.36824],[107.4463,12.29373],[107.42917,12.24657],[107.34511,12.33327],[107.15831,12.27547],[106.99953,12.08983],[106.92325,12.06548],[106.79405,12.0807],[106.70687,11.96956],[106.4111,11.97413],[106.4687,11.86751],[106.44068,11.86294],[106.44535,11.8279],[106.41577,11.76999],[106.45158,11.68616],[106.44691,11.66787],[106.37219,11.69836],[106.30525,11.67549],[106.26478,11.72122],[106.18539,11.75171],[106.13158,11.73283],[106.06708,11.77761],[106.02038,11.77457],[106.00792,11.7197],[105.95188,11.63738],[105.88962,11.67854],[105.8507,11.66635],[105.80867,11.60536],[105.81645,11.56876],[105.87328,11.55953],[105.88962,11.43605],[105.86782,11.28343],[106.10444,11.07879],[106.1527,11.10476],[106.1757,11.07301],[106.20095,10.97795],[106.14301,10.98176],[106.18539,10.79451],[106.06708,10.8098],[105.94535,10.9168],[105.93403,10.83853],[105.84603,10.85873],[105.86376,10.89839],[105.77751,11.03671],[105.50045,10.94586],[105.42884,10.96878],[105.34011,10.86179],[105.11449,10.96332],[105.08326,10.95656],[105.02722,10.89236],[105.09571,10.72722],[104.95094,10.64003],[104.87933,10.52833],[104.59018,10.53073],[104.49869,10.4057],[104.47963,10.43046],[104.43778,10.42386],[103.99198,10.48391],[102.47649,9.66162],[104.81582,8.03101],[109.55486,8.10026],[111.60491,13.57105],[108.00365,17.98159],[108.10003,21.47338]]]]}},{type:"Feature",properties:{iso1A2:"VU",iso1A3:"VUT",iso1N3:"548",wikidata:"Q686",nameEn:"Vanuatu",groups:["054","009"],callingCodes:["678"]},geometry:{type:"MultiPolygon",coordinates:[[[[162.93363,-17.28904],[173.26254,-22.69968],[168.21179,-12.88558],[166.02864,-12.9396],[162.93363,-17.28904]]]]}},{type:"Feature",properties:{iso1A2:"WF",iso1A3:"WLF",iso1N3:"876",wikidata:"Q35555",nameEn:"Wallis and Futuna",country:"FR",groups:["061","009"],callingCodes:["681"]},geometry:{type:"MultiPolygon",coordinates:[[[[-178.60161,-14.95666],[-176.76826,-14.95183],[-174.17905,-14.94502],[-174.18596,-12.48057],[-178.60852,-12.49232],[-178.60161,-14.95666]]]]}},{type:"Feature",properties:{iso1A2:"WS",iso1A3:"WSM",iso1N3:"882",wikidata:"Q683",nameEn:"Samoa",groups:["061","009"],driveSide:"left",callingCodes:["685"]},geometry:{type:"MultiPolygon",coordinates:[[[[-174.17905,-14.94502],[-173.13438,-14.94228],[-171.14262,-14.93704],[-171.14953,-12.4725],[-174.18596,-12.48057],[-174.17905,-14.94502]]]]}},{type:"Feature",properties:{iso1A2:"XK",iso1A3:"XKX",wikidata:"Q1246",nameEn:"Kosovo",aliases:["KV"],groups:["039","150"],isoStatus:"usrAssn",callingCodes:["383"]},geometry:{type:"MultiPolygon",coordinates:[[[[21.39045,42.74888],[21.44047,42.87276],[21.36941,42.87397],[21.32974,42.90424],[21.2719,42.8994],[21.23534,42.95523],[21.23877,43.00848],[21.2041,43.02277],[21.16734,42.99694],[21.14465,43.11089],[21.08952,43.13471],[21.05378,43.10707],[21.00749,43.13984],[20.96287,43.12416],[20.83727,43.17842],[20.88685,43.21697],[20.82145,43.26769],[20.73811,43.25068],[20.68688,43.21335],[20.59929,43.20492],[20.69515,43.09641],[20.64557,43.00826],[20.59929,43.01067],[20.48692,42.93208],[20.53484,42.8885],[20.43734,42.83157],[20.40594,42.84853],[20.35692,42.8335],[20.27869,42.81945],[20.2539,42.76245],[20.04898,42.77701],[20.02088,42.74789],[20.02915,42.71147],[20.0969,42.65559],[20.07761,42.55582],[20.17127,42.50469],[20.21797,42.41237],[20.24399,42.32168],[20.34479,42.32656],[20.3819,42.3029],[20.48857,42.25444],[20.56955,42.12097],[20.55633,42.08173],[20.59434,42.03879],[20.63069,41.94913],[20.57946,41.91593],[20.59524,41.8818],[20.68523,41.85318],[20.76786,41.91839],[20.75464,42.05229],[21.11491,42.20794],[21.16614,42.19815],[21.22728,42.08909],[21.31983,42.10993],[21.29913,42.13954],[21.30496,42.1418],[21.38428,42.24465],[21.43882,42.23609],[21.43882,42.2789],[21.50823,42.27156],[21.52145,42.24465],[21.58992,42.25915],[21.56772,42.30946],[21.5264,42.33634],[21.53467,42.36809],[21.57021,42.3647],[21.59029,42.38042],[21.62887,42.37664],[21.64209,42.41081],[21.62556,42.45106],[21.7035,42.51899],[21.70522,42.54176],[21.7327,42.55041],[21.75672,42.62695],[21.79413,42.65923],[21.75025,42.70125],[21.6626,42.67813],[21.58755,42.70418],[21.59154,42.72643],[21.47498,42.74695],[21.39045,42.74888]]]]}},{type:"Feature",properties:{iso1A2:"YE",iso1A3:"YEM",iso1N3:"887",wikidata:"Q805",nameEn:"Yemen",groups:["145","142"],callingCodes:["967"]},geometry:{type:"MultiPolygon",coordinates:[[[[53.32998,16.16312],[53.09917,16.67084],[52.81185,17.28568],[52.74267,17.29519],[52.78009,17.35124],[52.00311,19.00083],[49.04884,18.59899],[48.19996,18.20584],[47.58351,17.50366],[47.48245,17.10808],[47.00571,16.94765],[46.76494,17.29151],[46.31018,17.20464],[44.50126,17.47475],[43.70631,17.35762],[43.43005,17.56148],[43.29185,17.53224],[43.22533,17.38343],[43.32653,17.31179],[43.20156,17.25901],[43.17787,17.14717],[43.23967,17.03428],[43.18233,17.02673],[43.1813,16.98438],[43.19328,16.94703],[43.1398,16.90696],[43.18338,16.84852],[43.22012,16.83932],[43.22956,16.80613],[43.24801,16.80613],[43.26303,16.79479],[43.25857,16.75304],[43.21325,16.74416],[43.22066,16.65179],[43.15274,16.67248],[43.11601,16.53166],[42.97215,16.51093],[42.94351,16.49467],[42.94625,16.39721],[42.76801,16.40371],[42.15205,16.40211],[41.37609,16.19728],[41.29956,15.565],[42.63806,13.58268],[43.29075,12.79154],[43.32909,12.59711],[43.90659,12.3823],[50.51849,13.0483],[51.12877,12.56479],[52.253,11.68582],[55.69862,12.12478],[53.32998,16.16312]]]]}},{type:"Feature",properties:{iso1A2:"YT",iso1A3:"MYT",iso1N3:"175",wikidata:"Q17063",nameEn:"Mayotte",country:"FR",groups:["EU","014","202","002"],callingCodes:["262"]},geometry:{type:"MultiPolygon",coordinates:[[[[43.83794,-13.66915],[45.54824,-13.22353],[45.50237,-11.90315],[43.83794,-13.66915]]]]}},{type:"Feature",properties:{iso1A2:"ZA",iso1A3:"ZAF",iso1N3:"710",wikidata:"Q258",nameEn:"South Africa",groups:["018","202","002"],driveSide:"left",callingCodes:["27"]},geometry:{type:"MultiPolygon",coordinates:[[[[31.30611,-22.422],[31.16344,-22.32599],[31.08932,-22.34884],[30.86696,-22.28907],[30.6294,-22.32599],[30.48686,-22.31368],[30.38614,-22.34533],[30.28351,-22.35587],[30.2265,-22.2961],[30.13147,-22.30841],[29.92242,-22.19408],[29.76848,-22.14128],[29.64609,-22.12917],[29.37703,-22.19581],[29.21955,-22.17771],[29.18974,-22.18599],[29.15268,-22.21399],[29.10881,-22.21202],[29.0151,-22.22907],[28.91889,-22.44299],[28.63287,-22.55887],[28.34874,-22.5694],[28.04562,-22.8394],[28.04752,-22.90243],[27.93729,-22.96194],[27.93539,-23.04941],[27.74154,-23.2137],[27.6066,-23.21894],[27.52393,-23.37952],[27.33768,-23.40917],[26.99749,-23.65486],[26.84165,-24.24885],[26.51667,-24.47219],[26.46346,-24.60358],[26.39409,-24.63468],[25.8515,-24.75727],[25.84295,-24.78661],[25.88571,-24.87802],[25.72702,-25.25503],[25.69661,-25.29284],[25.6643,-25.4491],[25.58543,-25.6343],[25.33076,-25.76616],[25.12266,-25.75931],[25.01718,-25.72507],[24.8946,-25.80723],[24.67319,-25.81749],[24.44703,-25.73021],[24.36531,-25.773],[24.18287,-25.62916],[23.9244,-25.64286],[23.47588,-25.29971],[23.03497,-25.29971],[22.86012,-25.50572],[22.70808,-25.99186],[22.56365,-26.19668],[22.41921,-26.23078],[22.21206,-26.3773],[22.06192,-26.61882],[21.90703,-26.66808],[21.83291,-26.65959],[21.77114,-26.69015],[21.7854,-26.79199],[21.69322,-26.86152],[21.37869,-26.82083],[21.13353,-26.86661],[20.87031,-26.80047],[20.68596,-26.9039],[20.63275,-26.78181],[20.61754,-26.4692],[20.86081,-26.14892],[20.64795,-25.47827],[20.29826,-24.94869],[20.03678,-24.81004],[20.02809,-24.78725],[19.99817,-24.76768],[19.99882,-28.42622],[18.99885,-28.89165],[17.4579,-28.68718],[17.15405,-28.08573],[16.90446,-28.057],[16.59922,-28.53246],[16.46592,-28.57126],[16.45332,-28.63117],[12.51595,-32.27486],[38.88176,-48.03306],[34.51034,-26.91792],[32.35222,-26.86027],[32.29584,-26.852],[32.22302,-26.84136],[32.19409,-26.84032],[32.13315,-26.84345],[32.09664,-26.80721],[32.00893,-26.8096],[31.97463,-27.11057],[31.97592,-27.31675],[31.49834,-27.31549],[31.15027,-27.20151],[30.96088,-27.0245],[30.97757,-26.92706],[30.88826,-26.79622],[30.81101,-26.84722],[30.78927,-26.48271],[30.95819,-26.26303],[31.13073,-25.91558],[31.31237,-25.7431],[31.4175,-25.71886],[31.86881,-25.99973],[31.974,-25.95387],[31.92649,-25.84216],[32.00631,-25.65044],[31.97875,-25.46356],[32.01676,-25.38117],[32.03196,-25.10785],[31.9835,-24.29983],[31.90368,-24.18892],[31.87707,-23.95293],[31.77445,-23.90082],[31.70223,-23.72695],[31.67942,-23.60858],[31.56539,-23.47268],[31.55779,-23.176],[31.30611,-22.422]],[[29.33204,-29.45598],[29.28545,-29.58456],[29.12553,-29.76266],[29.16548,-29.91706],[28.9338,-30.05072],[28.80222,-30.10579],[28.68627,-30.12885],[28.399,-30.1592],[28.2319,-30.28476],[28.12073,-30.68072],[27.74814,-30.60635],[27.69467,-30.55862],[27.67819,-30.53437],[27.6521,-30.51707],[27.62137,-30.50509],[27.56781,-30.44562],[27.56901,-30.42504],[27.45452,-30.32239],[27.38108,-30.33456],[27.36649,-30.27246],[27.37293,-30.19401],[27.40778,-30.14577],[27.32555,-30.14785],[27.29603,-30.05473],[27.22719,-30.00718],[27.09489,-29.72796],[27.01016,-29.65439],[27.33464,-29.48161],[27.4358,-29.33465],[27.47254,-29.31968],[27.45125,-29.29708],[27.48679,-29.29349],[27.54258,-29.25575],[27.5158,-29.2261],[27.55974,-29.18954],[27.75458,-28.89839],[27.8907,-28.91612],[27.88933,-28.88156],[27.9392,-28.84864],[27.98675,-28.8787],[28.02503,-28.85991],[28.1317,-28.7293],[28.2348,-28.69471],[28.30518,-28.69531],[28.40612,-28.6215],[28.65091,-28.57025],[28.68043,-28.58744],[29.40524,-29.21246],[29.44883,-29.3772],[29.33204,-29.45598]]]]}},{type:"Feature",properties:{iso1A2:"ZM",iso1A3:"ZMB",iso1N3:"894",wikidata:"Q953",nameEn:"Zambia",groups:["014","202","002"],driveSide:"left",callingCodes:["260"]},geometry:{type:"MultiPolygon",coordinates:[[[[32.95389,-9.40138],[32.76233,-9.31963],[32.75611,-9.28583],[32.53661,-9.24281],[32.49147,-9.14754],[32.43543,-9.11988],[32.25486,-9.13371],[32.16146,-9.05993],[32.08206,-9.04609],[31.98866,-9.07069],[31.94196,-9.02303],[31.94663,-8.93846],[31.81587,-8.88618],[31.71158,-8.91386],[31.57147,-8.81388],[31.57147,-8.70619],[31.37533,-8.60769],[31.00796,-8.58615],[30.79243,-8.27382],[28.88917,-8.4831],[28.9711,-8.66935],[28.38526,-9.23393],[28.36562,-9.30091],[28.52636,-9.35379],[28.51627,-9.44726],[28.56208,-9.49122],[28.68532,-9.78],[28.62795,-9.92942],[28.65032,-10.65133],[28.37241,-11.57848],[28.48357,-11.87532],[29.18592,-12.37921],[29.4992,-12.43843],[29.48404,-12.23604],[29.8139,-12.14898],[29.81551,-13.44683],[29.65078,-13.41844],[29.60531,-13.21685],[29.01918,-13.41353],[28.33199,-12.41375],[27.59932,-12.22123],[27.21025,-11.76157],[27.22541,-11.60323],[27.04351,-11.61312],[26.88687,-12.01868],[26.01777,-11.91488],[25.33058,-11.65767],[25.34069,-11.19707],[24.42612,-11.44975],[24.34528,-11.06816],[24.00027,-10.89356],[24.02603,-11.15368],[23.98804,-12.13149],[24.06672,-12.29058],[23.90937,-12.844],[24.03339,-12.99091],[21.97988,-13.00148],[22.00323,-16.18028],[22.17217,-16.50269],[23.20038,-17.47563],[23.47474,-17.62877],[24.23619,-17.47489],[24.32811,-17.49082],[24.38712,-17.46818],[24.5621,-17.52963],[24.70864,-17.49501],[25.00198,-17.58221],[25.26433,-17.79571],[25.51646,-17.86232],[25.6827,-17.81987],[25.85738,-17.91403],[25.85892,-17.97726],[26.08925,-17.98168],[26.0908,-17.93021],[26.21601,-17.88608],[26.55918,-17.99638],[26.68403,-18.07411],[26.74314,-18.0199],[26.89926,-17.98756],[27.14196,-17.81398],[27.30736,-17.60487],[27.61377,-17.34378],[27.62795,-17.24365],[27.83141,-16.96274],[28.73725,-16.5528],[28.76199,-16.51575],[28.81454,-16.48611],[28.8501,-16.04537],[28.9243,-15.93987],[29.01298,-15.93805],[29.21955,-15.76589],[29.4437,-15.68702],[29.8317,-15.6126],[30.35574,-15.6513],[30.41902,-15.62269],[30.22098,-14.99447],[33.24249,-14.00019],[33.16749,-13.93992],[33.07568,-13.98447],[33.02977,-14.05022],[32.99042,-13.95689],[32.88985,-13.82956],[32.79015,-13.80755],[32.76962,-13.77224],[32.84528,-13.71576],[32.7828,-13.64805],[32.68654,-13.64268],[32.66468,-13.60019],[32.68436,-13.55769],[32.73683,-13.57682],[32.84176,-13.52794],[32.86113,-13.47292],[33.0078,-13.19492],[32.98289,-13.12671],[33.02181,-12.88707],[32.96733,-12.88251],[32.94397,-12.76868],[33.05917,-12.59554],[33.18837,-12.61377],[33.28177,-12.54692],[33.37517,-12.54085],[33.54485,-12.35996],[33.47636,-12.32498],[33.3705,-12.34931],[33.25998,-12.14242],[33.33937,-11.91252],[33.32692,-11.59248],[33.24252,-11.59302],[33.23663,-11.40637],[33.29267,-11.43536],[33.29267,-11.3789],[33.39697,-11.15296],[33.25998,-10.88862],[33.28022,-10.84428],[33.47636,-10.78465],[33.70675,-10.56896],[33.54797,-10.36077],[33.53863,-10.20148],[33.31297,-10.05133],[33.37902,-9.9104],[33.36581,-9.81063],[33.31517,-9.82364],[33.2095,-9.61099],[33.12144,-9.58929],[33.10163,-9.66525],[33.05485,-9.61316],[33.00256,-9.63053],[33.00476,-9.5133],[32.95389,-9.40138]]]]}},{type:"Feature",properties:{iso1A2:"ZW",iso1A3:"ZWE",iso1N3:"716",wikidata:"Q954",nameEn:"Zimbabwe",groups:["014","202","002"],driveSide:"left",callingCodes:["263"]},geometry:{type:"MultiPolygon",coordinates:[[[[30.41902,-15.62269],[30.35574,-15.6513],[29.8317,-15.6126],[29.4437,-15.68702],[29.21955,-15.76589],[29.01298,-15.93805],[28.9243,-15.93987],[28.8501,-16.04537],[28.81454,-16.48611],[28.76199,-16.51575],[28.73725,-16.5528],[27.83141,-16.96274],[27.62795,-17.24365],[27.61377,-17.34378],[27.30736,-17.60487],[27.14196,-17.81398],[26.89926,-17.98756],[26.74314,-18.0199],[26.68403,-18.07411],[26.55918,-17.99638],[26.21601,-17.88608],[26.0908,-17.93021],[26.08925,-17.98168],[25.85892,-17.97726],[25.85738,-17.91403],[25.6827,-17.81987],[25.51646,-17.86232],[25.26433,-17.79571],[25.23909,-17.90832],[25.31799,-18.07091],[25.39972,-18.12691],[25.53465,-18.39041],[25.68859,-18.56165],[25.79217,-18.6355],[25.82353,-18.82808],[25.94326,-18.90362],[25.99837,-19.02943],[25.96226,-19.08152],[26.17227,-19.53709],[26.72246,-19.92707],[27.21278,-20.08244],[27.29831,-20.28935],[27.28865,-20.49873],[27.69361,-20.48531],[27.72972,-20.51735],[27.69171,-21.08409],[27.91407,-21.31621],[28.01669,-21.57624],[28.29416,-21.59037],[28.49942,-21.66634],[28.58114,-21.63455],[29.07763,-21.81877],[29.04023,-21.85864],[29.02191,-21.90647],[29.02191,-21.95665],[29.04108,-22.00563],[29.08495,-22.04867],[29.14501,-22.07275],[29.1974,-22.07472],[29.24648,-22.05967],[29.3533,-22.18363],[29.37703,-22.19581],[29.64609,-22.12917],[29.76848,-22.14128],[29.92242,-22.19408],[30.13147,-22.30841],[30.2265,-22.2961],[30.28351,-22.35587],[30.38614,-22.34533],[30.48686,-22.31368],[30.6294,-22.32599],[30.86696,-22.28907],[31.08932,-22.34884],[31.16344,-22.32599],[31.30611,-22.422],[31.38336,-22.36919],[32.41234,-21.31246],[32.48236,-21.32873],[32.37115,-21.133],[32.51644,-20.91929],[32.48122,-20.63319],[32.55167,-20.56312],[32.66174,-20.56106],[32.85987,-20.27841],[32.85987,-20.16686],[32.93032,-20.03868],[33.01178,-20.02007],[33.06461,-19.77787],[32.95013,-19.67219],[32.84666,-19.68462],[32.84446,-19.48343],[32.78282,-19.47513],[32.77966,-19.36098],[32.85107,-19.29238],[32.87088,-19.09279],[32.84006,-19.0262],[32.72118,-19.02204],[32.69917,-18.94293],[32.73439,-18.92628],[32.70137,-18.84712],[32.82465,-18.77419],[32.9017,-18.7992],[32.95013,-18.69079],[32.88629,-18.58023],[32.88629,-18.51344],[33.02278,-18.4696],[33.03159,-18.35054],[32.94133,-17.99705],[33.0492,-17.60298],[32.98536,-17.55891],[32.96554,-17.48964],[33.0426,-17.3468],[33.00517,-17.30477],[32.96554,-17.11971],[32.84113,-16.92259],[32.91051,-16.89446],[32.97655,-16.70689],[32.78943,-16.70267],[32.69917,-16.66893],[32.71017,-16.59932],[32.42838,-16.4727],[32.28529,-16.43892],[32.02772,-16.43892],[31.91324,-16.41569],[31.90223,-16.34388],[31.67988,-16.19595],[31.42451,-16.15154],[31.30563,-16.01193],[31.13171,-15.98019],[30.97761,-16.05848],[30.91597,-15.99924],[30.42568,-15.9962],[30.41902,-15.62269]]]]}}];
60342         var rawBorders = {
60343         type: type,
60344         features: features
60345         };
60346
60347         let borders = rawBorders;
60348         let whichPolygonGetter = {};
60349         let featuresByCode = {};
60350         let idFilterRegex = /\bThe\b|\bthe\b|\band\b|\bof\b|[-_ .,()&[\]/]/g;
60351         let levels = [
60352           'subterritory',
60353           'territory',
60354           'country',
60355           'intermediateRegion',
60356           'subregion',
60357           'region',
60358           'union',
60359           'world'
60360         ];
60361         loadDerivedDataAndCaches(borders);
60362         function loadDerivedDataAndCaches(borders) {
60363           let identifierProps = ['iso1A2', 'iso1A3', 'm49', 'wikidata', 'emojiFlag', 'nameEn'];
60364           let geometryFeatures = [];
60365           for (let i in borders.features) {
60366             let feature = borders.features[i];
60367             feature.properties.id = feature.properties.iso1A2 || feature.properties.m49;
60368             loadM49(feature);
60369             loadIsoStatus(feature);
60370             loadLevel(feature);
60371             loadGroups(feature);
60372             loadRoadSpeedUnit(feature);
60373             loadDriveSide(feature);
60374             loadFlag(feature);
60375             cacheFeatureByIDs(feature);
60376             if (feature.geometry) geometryFeatures.push(feature);
60377           }
60378           for (let i in borders.features) {
60379             let feature = borders.features[i];
60380             feature.properties.groups.sort(function(groupID1, groupID2) {
60381               return (
60382                 levels.indexOf(featuresByCode[groupID1].properties.level) -
60383                 levels.indexOf(featuresByCode[groupID2].properties.level)
60384               );
60385             });
60386             loadMembersForGroupsOf(feature);
60387           }
60388           let geometryOnlyCollection = {
60389             type: 'RegionFeatureCollection',
60390             features: geometryFeatures
60391           };
60392           whichPolygonGetter = whichPolygon_1(geometryOnlyCollection);
60393           function loadGroups(feature) {
60394             let props = feature.properties;
60395             if (!props.groups) {
60396               props.groups = [];
60397             }
60398             if (props.country) {
60399               props.groups.push(props.country);
60400             }
60401             if (props.m49 !== '001') {
60402               props.groups.push('001');
60403             }
60404           }
60405           function loadM49(feature) {
60406             let props = feature.properties;
60407             if (!props.m49 && props.iso1N3) {
60408               props.m49 = props.iso1N3;
60409             }
60410           }
60411           function loadIsoStatus(feature) {
60412             let props = feature.properties;
60413             if (!props.isoStatus && props.iso1A2) {
60414               props.isoStatus = 'official';
60415             }
60416           }
60417           function loadLevel(feature) {
60418             let props = feature.properties;
60419             if (props.level) return;
60420             if (!props.country) {
60421               props.level = 'country';
60422             } else if (props.isoStatus === 'official') {
60423               props.level = 'territory';
60424             } else {
60425               props.level = 'subterritory';
60426             }
60427           }
60428           function loadRoadSpeedUnit(feature) {
60429             let props = feature.properties;
60430             if (props.roadSpeedUnit === undefined && props.iso1A2 && props.iso1A2 !== 'EU') {
60431               props.roadSpeedUnit = 'km/h';
60432             }
60433           }
60434           function loadDriveSide(feature) {
60435             let props = feature.properties;
60436             if (props.driveSide === undefined && props.iso1A2 && props.iso1A2 !== 'EU') {
60437               props.driveSide = 'right';
60438             }
60439           }
60440           function loadFlag(feature) {
60441             if (!feature.properties.iso1A2) return;
60442             let flag = feature.properties.iso1A2.replace(/./g, function(char) {
60443               return String.fromCodePoint(char.charCodeAt(0) + 127397);
60444             });
60445             feature.properties.emojiFlag = flag;
60446           }
60447           function loadMembersForGroupsOf(feature) {
60448             let featureID = feature.properties.id;
60449             let standardizedGroupIDs = [];
60450             for (let j in feature.properties.groups) {
60451               let groupID = feature.properties.groups[j];
60452               let groupFeature = featuresByCode[groupID];
60453               standardizedGroupIDs.push(groupFeature.properties.id);
60454               if (groupFeature.properties.members) {
60455                 groupFeature.properties.members.push(featureID);
60456               } else {
60457                 groupFeature.properties.members = [featureID];
60458               }
60459             }
60460             feature.properties.groups = standardizedGroupIDs;
60461           }
60462           function cacheFeatureByIDs(feature) {
60463             for (let k in identifierProps) {
60464               let prop = identifierProps[k];
60465               let id = prop && feature.properties[prop];
60466               if (id) {
60467                 id = id.replace(idFilterRegex, '').toUpperCase();
60468                 featuresByCode[id] = feature;
60469               }
60470             }
60471             if (feature.properties.aliases) {
60472               for (let j in feature.properties.aliases) {
60473                 let alias = feature.properties.aliases[j].replace(idFilterRegex, '').toUpperCase();
60474                 featuresByCode[alias] = feature;
60475               }
60476             }
60477           }
60478         }
60479         function locArray(loc) {
60480           if (Array.isArray(loc)) {
60481             return loc;
60482           } else if (loc.coordinates) {
60483             return loc.coordinates;
60484           }
60485           return loc.geometry.coordinates;
60486         }
60487         function smallestFeature(loc) {
60488           let query = locArray(loc);
60489           let featureProperties = whichPolygonGetter(query);
60490           if (!featureProperties) return null;
60491           return featuresByCode[featureProperties.id];
60492         }
60493         function countryFeature(loc) {
60494           let feature = smallestFeature(loc);
60495           if (!feature) return null;
60496           let countryCode = feature.properties.country || feature.properties.iso1A2;
60497           return featuresByCode[countryCode];
60498         }
60499         function featureForLoc(loc, opts) {
60500           if (opts && opts.level && opts.level !== 'country') {
60501             let features = featuresContaining(loc);
60502             let targetLevel = opts.level;
60503             let targetLevelIndex = levels.indexOf(targetLevel);
60504             if (targetLevelIndex === -1) return null;
60505             for (let i in features) {
60506               let feature = features[i];
60507               if (
60508                 feature.properties.level === targetLevel ||
60509                 levels.indexOf(feature.properties.level) > targetLevelIndex
60510               ) {
60511                 return feature;
60512               }
60513             }
60514             return null;
60515           }
60516           return countryFeature(loc);
60517         }
60518         function featureForID(id) {
60519           let stringID;
60520           if (typeof id === 'number') {
60521             stringID = id.toString();
60522             if (stringID.length === 1) {
60523               stringID = '00' + stringID;
60524             } else if (stringID.length === 2) {
60525               stringID = '0' + stringID;
60526             }
60527           } else {
60528             stringID = id.replace(idFilterRegex, '').toUpperCase();
60529           }
60530           return featuresByCode[stringID] || null;
60531         }
60532         function smallestOrMatchingFeature(query) {
60533           if (typeof query === 'object') {
60534             return smallestFeature(query);
60535           }
60536           return featureForID(query);
60537         }
60538         function feature(query, opts) {
60539           if (typeof query === 'object') {
60540             return featureForLoc(query, opts);
60541           }
60542           return featureForID(query);
60543         }
60544         function iso1A2Code(query, opts) {
60545           let match = feature(query, opts);
60546           if (!match) return null;
60547           return match.properties.iso1A2 || null;
60548         }
60549         function featuresContaining(query, strict) {
60550           let feature = smallestOrMatchingFeature(query);
60551           if (!feature) return [];
60552           let features = [];
60553           if (!strict || typeof query === 'object') {
60554             features.push(feature);
60555           }
60556           let properties = feature.properties;
60557           for (let i in properties.groups) {
60558             let groupID = properties.groups[i];
60559             features.push(featuresByCode[groupID]);
60560           }
60561           return features;
60562         }
60563         function roadSpeedUnit(query) {
60564           let feature = smallestOrMatchingFeature(query);
60565           return (feature && feature.properties.roadSpeedUnit) || null;
60566         }
60567
60568         let _dataDeprecated;
60569         let _nsi;
60570
60571         function validationOutdatedTags() {
60572           const type = 'outdated_tags';
60573           const nsiKeys = ['amenity', 'shop', 'tourism', 'leisure', 'office'];
60574
60575           // A concern here in switching to async data means that `_dataDeprecated`
60576           // and `_nsi` will not be available at first, so the data on early tiles
60577           // may not have tags validated fully.
60578
60579           // initialize deprecated tags array
60580           _mainFileFetcher.get('deprecated')
60581             .then(d => _dataDeprecated = d)
60582             .catch(() => { /* ignore */ });
60583
60584           _mainFileFetcher.get('nsi_brands')
60585             .then(d => {
60586               _nsi = {
60587                 brands: d.brands,
60588                 matcher: matcher$1(),
60589                 wikidata: {},
60590                 wikipedia: {}
60591               };
60592
60593               // initialize name-suggestion-index matcher
60594               _nsi.matcher.buildMatchIndex(d.brands);
60595
60596               // index all known wikipedia and wikidata tags
60597               Object.keys(d.brands).forEach(kvnd => {
60598                 const brand = d.brands[kvnd];
60599                 const wd = brand.tags['brand:wikidata'];
60600                 const wp = brand.tags['brand:wikipedia'];
60601                 if (wd) { _nsi.wikidata[wd] = kvnd; }
60602                 if (wp) { _nsi.wikipedia[wp] = kvnd; }
60603               });
60604
60605               return _nsi;
60606             })
60607             .catch(() => { /* ignore */ });
60608
60609
60610           function oldTagIssues(entity, graph) {
60611             const oldTags = Object.assign({}, entity.tags);  // shallow copy
60612             let preset = _mainPresetIndex.match(entity, graph);
60613             let subtype = 'deprecated_tags';
60614             if (!preset) return [];
60615
60616             // upgrade preset..
60617             if (preset.replacement) {
60618               const newPreset = _mainPresetIndex.item(preset.replacement);
60619               graph = actionChangePreset(entity.id, preset, newPreset, true /* skip field defaults */)(graph);
60620               entity = graph.entity(entity.id);
60621               preset = newPreset;
60622             }
60623
60624             // upgrade tags..
60625             if (_dataDeprecated) {
60626               const deprecatedTags = entity.deprecatedTags(_dataDeprecated);
60627               if (deprecatedTags.length) {
60628                 deprecatedTags.forEach(tag => {
60629                   graph = actionUpgradeTags(entity.id, tag.old, tag.replace)(graph);
60630                 });
60631                 entity = graph.entity(entity.id);
60632               }
60633             }
60634
60635             // add missing addTags..
60636             let newTags = Object.assign({}, entity.tags);  // shallow copy
60637             if (preset.tags !== preset.addTags) {
60638               Object.keys(preset.addTags).forEach(k => {
60639                 if (!newTags[k]) {
60640                   if (preset.addTags[k] === '*') {
60641                     newTags[k] = 'yes';
60642                   } else {
60643                     newTags[k] = preset.addTags[k];
60644                   }
60645                 }
60646               });
60647             }
60648
60649             if (_nsi) {
60650               // Do `wikidata` or `wikipedia` identify this entity as a brand?  #6416
60651               // If so, these tags can be swapped to `brand:wikidata`/`brand:wikipedia`
60652               let isBrand;
60653               if (newTags.wikidata) {                 // try matching `wikidata`
60654                 isBrand = _nsi.wikidata[newTags.wikidata];
60655               }
60656               if (!isBrand && newTags.wikipedia) {    // fallback to `wikipedia`
60657                 isBrand = _nsi.wikipedia[newTags.wikipedia];
60658               }
60659               if (isBrand && !newTags.office) {       // but avoid doing this for corporate offices
60660                 if (newTags.wikidata) {
60661                   newTags['brand:wikidata'] = newTags.wikidata;
60662                   delete newTags.wikidata;
60663                 }
60664                 if (newTags.wikipedia) {
60665                   newTags['brand:wikipedia'] = newTags.wikipedia;
60666                   delete newTags.wikipedia;
60667                 }
60668                 // I considered setting `name` and other tags here, but they aren't unique per wikidata
60669                 // (Q2759586 -> in USA "Papa John's", in Russia "Папа Джонс")
60670                 // So users will really need to use a preset or assign `name` themselves.
60671               }
60672
60673               // try key/value|name match against name-suggestion-index
60674               if (newTags.name) {
60675                 for (let i = 0; i < nsiKeys.length; i++) {
60676                   const k = nsiKeys[i];
60677                   if (!newTags[k]) continue;
60678
60679                   const center = entity.extent(graph).center();
60680                   const countryCode = iso1A2Code(center);
60681                   const match = _nsi.matcher.matchKVN(k, newTags[k], newTags.name, countryCode && countryCode.toLowerCase());
60682                   if (!match) continue;
60683
60684                   // for now skip ambiguous matches (like Target~(USA) vs Target~(Australia))
60685                   if (match.d) continue;
60686
60687                   const brand = _nsi.brands[match.kvnd];
60688                   if (brand && brand.tags['brand:wikidata'] &&
60689                     brand.tags['brand:wikidata'] !== entity.tags['not:brand:wikidata']) {
60690                     subtype = 'noncanonical_brand';
60691
60692                     const keepTags = ['takeaway'].reduce((acc, k) => {
60693                       if (newTags[k]) {
60694                         acc[k] = newTags[k];
60695                       }
60696                       return acc;
60697                     }, {});
60698
60699                     nsiKeys.forEach(k => delete newTags[k]);
60700                     Object.assign(newTags, brand.tags, keepTags);
60701                     break;
60702                   }
60703                 }
60704               }
60705             }
60706
60707             // determine diff
60708             const tagDiff = utilTagDiff(oldTags, newTags);
60709             if (!tagDiff.length) return [];
60710
60711             const isOnlyAddingTags = tagDiff.every(d => d.type === '+');
60712
60713             let prefix = '';
60714             if (subtype === 'noncanonical_brand') {
60715               prefix = 'noncanonical_brand.';
60716             } else if (subtype === 'deprecated_tags' && isOnlyAddingTags) {
60717               subtype = 'incomplete_tags';
60718               prefix = 'incomplete.';
60719             }
60720
60721             // don't allow autofixing brand tags
60722             let autoArgs = subtype !== 'noncanonical_brand' ? [doUpgrade, _t('issues.fix.upgrade_tags.annotation')] : null;
60723
60724             return [new validationIssue({
60725               type: type,
60726               subtype: subtype,
60727               severity: 'warning',
60728               message: showMessage,
60729               reference: showReference,
60730               entityIds: [entity.id],
60731               hash: JSON.stringify(tagDiff),
60732               dynamicFixes: () => {
60733                 return [
60734                   new validationIssueFix({
60735                     autoArgs: autoArgs,
60736                     title: _t('issues.fix.upgrade_tags.title'),
60737                     onClick: (context) => {
60738                       context.perform(doUpgrade, _t('issues.fix.upgrade_tags.annotation'));
60739                     }
60740                   })
60741                 ];
60742               }
60743             })];
60744
60745
60746             function doUpgrade(graph) {
60747               const currEntity = graph.hasEntity(entity.id);
60748               if (!currEntity) return graph;
60749
60750               let newTags = Object.assign({}, currEntity.tags);  // shallow copy
60751               tagDiff.forEach(diff => {
60752                 if (diff.type === '-') {
60753                   delete newTags[diff.key];
60754                 } else if (diff.type === '+') {
60755                   newTags[diff.key] = diff.newVal;
60756                 }
60757               });
60758
60759               return actionChangeTags(currEntity.id, newTags)(graph);
60760             }
60761
60762
60763             function showMessage(context) {
60764               const currEntity = context.hasEntity(entity.id);
60765               if (!currEntity) return '';
60766
60767               let messageID = `issues.outdated_tags.${prefix}message`;
60768               if (subtype === 'noncanonical_brand' && isOnlyAddingTags) {
60769                 messageID += '_incomplete';
60770               }
60771               return _t(messageID, { feature: utilDisplayLabel(currEntity, context.graph()) });
60772             }
60773
60774
60775             function showReference(selection) {
60776               let enter = selection.selectAll('.issue-reference')
60777                 .data([0])
60778                 .enter();
60779
60780               enter
60781                 .append('div')
60782                 .attr('class', 'issue-reference')
60783                 .text(_t(`issues.outdated_tags.${prefix}reference`));
60784
60785               enter
60786                 .append('strong')
60787                 .text(_t('issues.suggested'));
60788
60789               enter
60790                 .append('table')
60791                 .attr('class', 'tagDiff-table')
60792                 .selectAll('.tagDiff-row')
60793                 .data(tagDiff)
60794                 .enter()
60795                 .append('tr')
60796                 .attr('class', 'tagDiff-row')
60797                 .append('td')
60798                 .attr('class', d => {
60799                   let klass = d.type === '+' ? 'add' : 'remove';
60800                   return `tagDiff-cell tagDiff-cell-${klass}`;
60801                 })
60802                 .text(d => d.display);
60803             }
60804           }
60805
60806
60807           function oldMultipolygonIssues(entity, graph) {
60808             let multipolygon, outerWay;
60809             if (entity.type === 'relation') {
60810               outerWay = osmOldMultipolygonOuterMemberOfRelation(entity, graph);
60811               multipolygon = entity;
60812             } else if (entity.type === 'way') {
60813               multipolygon = osmIsOldMultipolygonOuterMember(entity, graph);
60814               outerWay = entity;
60815             } else {
60816               return [];
60817             }
60818
60819             if (!multipolygon || !outerWay) return [];
60820
60821             return [new validationIssue({
60822               type: type,
60823               subtype: 'old_multipolygon',
60824               severity: 'warning',
60825               message: showMessage,
60826               reference: showReference,
60827               entityIds: [outerWay.id, multipolygon.id],
60828               dynamicFixes: () => {
60829                 return [
60830                   new validationIssueFix({
60831                     autoArgs: [doUpgrade, _t('issues.fix.move_tags.annotation')],
60832                     title: _t('issues.fix.move_tags.title'),
60833                     onClick: (context) => {
60834                       context.perform(doUpgrade, _t('issues.fix.move_tags.annotation'));
60835                     }
60836                   })
60837                 ];
60838               }
60839             })];
60840
60841
60842             function doUpgrade(graph) {
60843               let currMultipolygon = graph.hasEntity(multipolygon.id);
60844               let currOuterWay = graph.hasEntity(outerWay.id);
60845               if (!currMultipolygon || !currOuterWay) return graph;
60846
60847               currMultipolygon = currMultipolygon.mergeTags(currOuterWay.tags);
60848               graph = graph.replace(currMultipolygon);
60849               return actionChangeTags(currOuterWay.id, {})(graph);
60850             }
60851
60852
60853             function showMessage(context) {
60854               let currMultipolygon = context.hasEntity(multipolygon.id);
60855               if (!currMultipolygon) return '';
60856
60857               return _t('issues.old_multipolygon.message',
60858                   { multipolygon: utilDisplayLabel(currMultipolygon, context.graph()) }
60859               );
60860             }
60861
60862
60863             function showReference(selection) {
60864               selection.selectAll('.issue-reference')
60865                 .data([0])
60866                 .enter()
60867                 .append('div')
60868                 .attr('class', 'issue-reference')
60869                 .text(_t('issues.old_multipolygon.reference'));
60870             }
60871           }
60872
60873
60874           let validation = function checkOutdatedTags(entity, graph) {
60875             let issues = oldMultipolygonIssues(entity, graph);
60876             if (!issues.length) issues = oldTagIssues(entity, graph);
60877             return issues;
60878           };
60879
60880
60881           validation.type = type;
60882
60883           return validation;
60884         }
60885
60886         function validationPrivateData() {
60887             var type = 'private_data';
60888
60889             // assume that some buildings are private
60890             var privateBuildingValues = {
60891                 detached: true,
60892                 farm: true,
60893                 house: true,
60894                 houseboat: true,
60895                 residential: true,
60896                 semidetached_house: true,
60897                 static_caravan: true
60898             };
60899
60900             // but they might be public if they have one of these other tags
60901             var publicKeys = {
60902                 amenity: true,
60903                 craft: true,
60904                 historic: true,
60905                 leisure: true,
60906                 office: true,
60907                 shop: true,
60908                 tourism: true
60909             };
60910
60911             // these tags may contain personally identifying info
60912             var personalTags = {
60913                 'contact:email': true,
60914                 'contact:fax': true,
60915                 'contact:phone': true,
60916                 email: true,
60917                 fax: true,
60918                 phone: true
60919             };
60920
60921
60922             var validation = function checkPrivateData(entity) {
60923                 var tags = entity.tags;
60924                 if (!tags.building || !privateBuildingValues[tags.building]) return [];
60925
60926                 var keepTags = {};
60927                 for (var k in tags) {
60928                     if (publicKeys[k]) return [];  // probably a public feature
60929                     if (!personalTags[k]) {
60930                         keepTags[k] = tags[k];
60931                     }
60932                 }
60933
60934                 var tagDiff = utilTagDiff(tags, keepTags);
60935                 if (!tagDiff.length) return [];
60936
60937                 var fixID = tagDiff.length === 1 ? 'remove_tag' : 'remove_tags';
60938
60939                 return [new validationIssue({
60940                     type: type,
60941                     severity: 'warning',
60942                     message: showMessage,
60943                     reference: showReference,
60944                     entityIds: [entity.id],
60945                     dynamicFixes: function() {
60946                         return [
60947                             new validationIssueFix({
60948                                 icon: 'iD-operation-delete',
60949                                 title: _t('issues.fix.' + fixID + '.title'),
60950                                 onClick: function(context) {
60951                                     context.perform(doUpgrade, _t('issues.fix.upgrade_tags.annotation'));
60952                                 }
60953                             })
60954                         ];
60955                     }
60956                 })];
60957
60958
60959                 function doUpgrade(graph) {
60960                     var currEntity = graph.hasEntity(entity.id);
60961                     if (!currEntity) return graph;
60962
60963                     var newTags = Object.assign({}, currEntity.tags);  // shallow copy
60964                     tagDiff.forEach(function(diff) {
60965                         if (diff.type === '-') {
60966                             delete newTags[diff.key];
60967                         } else if (diff.type === '+') {
60968                             newTags[diff.key] = diff.newVal;
60969                         }
60970                     });
60971
60972                     return actionChangeTags(currEntity.id, newTags)(graph);
60973                 }
60974
60975
60976                 function showMessage(context) {
60977                     var currEntity = context.hasEntity(this.entityIds[0]);
60978                     if (!currEntity) return '';
60979
60980                     return _t('issues.private_data.contact.message',
60981                         { feature: utilDisplayLabel(currEntity, context.graph()) }
60982                     );
60983                 }
60984
60985
60986                 function showReference(selection) {
60987                     var enter = selection.selectAll('.issue-reference')
60988                         .data([0])
60989                         .enter();
60990
60991                     enter
60992                         .append('div')
60993                         .attr('class', 'issue-reference')
60994                         .text(_t('issues.private_data.reference'));
60995
60996                     enter
60997                         .append('strong')
60998                         .text(_t('issues.suggested'));
60999
61000                     enter
61001                         .append('table')
61002                         .attr('class', 'tagDiff-table')
61003                         .selectAll('.tagDiff-row')
61004                         .data(tagDiff)
61005                         .enter()
61006                         .append('tr')
61007                         .attr('class', 'tagDiff-row')
61008                         .append('td')
61009                         .attr('class', function(d) {
61010                             var klass = d.type === '+' ? 'add' : 'remove';
61011                             return 'tagDiff-cell tagDiff-cell-' + klass;
61012                         })
61013                         .text(function(d) { return d.display; });
61014                 }
61015             };
61016
61017
61018             validation.type = type;
61019
61020             return validation;
61021         }
61022
61023         let _discardNameRegexes = [];
61024
61025         function validationSuspiciousName() {
61026           const type = 'suspicious_name';
61027           const keysToTestForGenericValues = [
61028             'aerialway', 'aeroway', 'amenity', 'building', 'craft', 'highway',
61029             'leisure', 'railway', 'man_made', 'office', 'shop', 'tourism', 'waterway'
61030           ];
61031
61032           // A concern here in switching to async data means that `_nsiFilters` will not
61033           // be available at first, so the data on early tiles may not have tags validated fully.
61034
61035           _mainFileFetcher.get('nsi_filters')
61036             .then(filters => {
61037               // known list of generic names (e.g. "bar")
61038               _discardNameRegexes = filters.discardNames
61039                 .map(discardName => new RegExp(discardName, 'i'));
61040             })
61041             .catch(() => { /* ignore */ });
61042
61043
61044           function isDiscardedSuggestionName(lowercaseName) {
61045             return _discardNameRegexes.some(regex => regex.test(lowercaseName));
61046           }
61047
61048           // test if the name is just the key or tag value (e.g. "park")
61049           function nameMatchesRawTag(lowercaseName, tags) {
61050             for (let i = 0; i < keysToTestForGenericValues.length; i++) {
61051               let key = keysToTestForGenericValues[i];
61052               let val = tags[key];
61053               if (val) {
61054                 val = val.toLowerCase();
61055                 if (key === lowercaseName ||
61056                   val === lowercaseName ||
61057                   key.replace(/\_/g, ' ') === lowercaseName ||
61058                   val.replace(/\_/g, ' ') === lowercaseName) {
61059                   return true;
61060                 }
61061               }
61062             }
61063             return false;
61064           }
61065
61066           function isGenericName(name, tags) {
61067             name = name.toLowerCase();
61068             return nameMatchesRawTag(name, tags) || isDiscardedSuggestionName(name);
61069           }
61070
61071           function makeGenericNameIssue(entityId, nameKey, genericName, langCode) {
61072             return new validationIssue({
61073               type: type,
61074               subtype: 'generic_name',
61075               severity: 'warning',
61076               message: function(context) {
61077                 let entity = context.hasEntity(this.entityIds[0]);
61078                 if (!entity) return '';
61079                 let preset = _mainPresetIndex.match(entity, context.graph());
61080                 let langName = langCode && _mainLocalizer.languageName(langCode);
61081                 return _t('issues.generic_name.message' + (langName ? '_language' : ''),
61082                   { feature: preset.name(), name: genericName, language: langName }
61083                 );
61084               },
61085               reference: showReference,
61086               entityIds: [entityId],
61087               hash: nameKey + '=' + genericName,
61088               dynamicFixes: function() {
61089                 return [
61090                   new validationIssueFix({
61091                     icon: 'iD-operation-delete',
61092                     title: _t('issues.fix.remove_the_name.title'),
61093                     onClick: function(context) {
61094                       let entityId = this.issue.entityIds[0];
61095                       let entity = context.entity(entityId);
61096                       let tags = Object.assign({}, entity.tags);   // shallow copy
61097                       delete tags[nameKey];
61098                       context.perform(
61099                         actionChangeTags(entityId, tags), _t('issues.fix.remove_generic_name.annotation')
61100                       );
61101                     }
61102                   })
61103                 ];
61104               }
61105             });
61106
61107             function showReference(selection) {
61108               selection.selectAll('.issue-reference')
61109                 .data([0])
61110                 .enter()
61111                 .append('div')
61112                 .attr('class', 'issue-reference')
61113                 .text(_t('issues.generic_name.reference'));
61114             }
61115           }
61116
61117           function makeIncorrectNameIssue(entityId, nameKey, incorrectName, langCode) {
61118             return new validationIssue({
61119               type: type,
61120               subtype: 'not_name',
61121               severity: 'warning',
61122               message: function(context) {
61123                 const entity = context.hasEntity(this.entityIds[0]);
61124                 if (!entity) return '';
61125                 const preset = _mainPresetIndex.match(entity, context.graph());
61126                 const langName = langCode && _mainLocalizer.languageName(langCode);
61127                 return _t('issues.incorrect_name.message' + (langName ? '_language' : ''),
61128                   { feature: preset.name(), name: incorrectName, language: langName }
61129                 );
61130               },
61131               reference: showReference,
61132               entityIds: [entityId],
61133               hash: nameKey + '=' + incorrectName,
61134               dynamicFixes: function() {
61135                 return [
61136                   new validationIssueFix({
61137                     icon: 'iD-operation-delete',
61138                     title: _t('issues.fix.remove_the_name.title'),
61139                     onClick: function(context) {
61140                       const entityId = this.issue.entityIds[0];
61141                       const entity = context.entity(entityId);
61142                       let tags = Object.assign({}, entity.tags);   // shallow copy
61143                       delete tags[nameKey];
61144                       context.perform(
61145                         actionChangeTags(entityId, tags), _t('issues.fix.remove_mistaken_name.annotation')
61146                       );
61147                     }
61148                   })
61149                 ];
61150               }
61151             });
61152
61153             function showReference(selection) {
61154               selection.selectAll('.issue-reference')
61155                 .data([0])
61156                 .enter()
61157                 .append('div')
61158                 .attr('class', 'issue-reference')
61159                 .text(_t('issues.generic_name.reference'));
61160             }
61161           }
61162
61163
61164           let validation = function checkGenericName(entity) {
61165             // a generic name is okay if it's a known brand or entity
61166             if (entity.hasWikidata()) return [];
61167
61168             let issues = [];
61169             const notNames = (entity.tags['not:name'] || '').split(';');
61170
61171             for (let key in entity.tags) {
61172               const m = key.match(/^name(?:(?::)([a-zA-Z_-]+))?$/);
61173               if (!m) continue;
61174
61175               const langCode = m.length >= 2 ? m[1] : null;
61176               const value = entity.tags[key];
61177               if (notNames.length) {
61178                 for (let i in notNames) {
61179                   const notName = notNames[i];
61180                   if (notName && value === notName) {
61181                     issues.push(makeIncorrectNameIssue(entity.id, key, value, langCode));
61182                     continue;
61183                   }
61184                 }
61185               }
61186               if (isGenericName(value, entity.tags)) {
61187                 issues.push(makeGenericNameIssue(entity.id, key, value, langCode));
61188               }
61189             }
61190
61191             return issues;
61192           };
61193
61194
61195           validation.type = type;
61196
61197           return validation;
61198         }
61199
61200         function validationUnsquareWay(context) {
61201             var type = 'unsquare_way';
61202             var DEFAULT_DEG_THRESHOLD = 5;   // see also issues.js
61203
61204             // use looser epsilon for detection to reduce warnings of buildings that are essentially square already
61205             var epsilon = 0.05;
61206             var nodeThreshold = 10;
61207
61208             function isBuilding(entity, graph) {
61209                 if (entity.type !== 'way' || entity.geometry(graph) !== 'area') return false;
61210                 return entity.tags.building && entity.tags.building !== 'no';
61211             }
61212
61213
61214             var validation = function checkUnsquareWay(entity, graph) {
61215
61216                 if (!isBuilding(entity, graph)) return [];
61217
61218                 // don't flag ways marked as physically unsquare
61219                 if (entity.tags.nonsquare === 'yes') return [];
61220
61221                 var isClosed = entity.isClosed();
61222                 if (!isClosed) return [];        // this building has bigger problems
61223
61224                 // don't flag ways with lots of nodes since they are likely detail-mapped
61225                 var nodes = graph.childNodes(entity).slice();    // shallow copy
61226                 if (nodes.length > nodeThreshold + 1) return [];   // +1 because closing node appears twice
61227
61228                 // ignore if not all nodes are fully downloaded
61229                 var osm = services.osm;
61230                 if (!osm || nodes.some(function(node) { return !osm.isDataLoaded(node.loc); })) return [];
61231
61232                 // don't flag connected ways to avoid unresolvable unsquare loops
61233                 var hasConnectedSquarableWays = nodes.some(function(node) {
61234                     return graph.parentWays(node).some(function(way) {
61235                         if (way.id === entity.id) return false;
61236                         if (isBuilding(way, graph)) return true;
61237                         return graph.parentRelations(way).some(function(parentRelation) {
61238                             return parentRelation.isMultipolygon() &&
61239                                 parentRelation.tags.building &&
61240                                 parentRelation.tags.building !== 'no';
61241                         });
61242                     });
61243                 });
61244                 if (hasConnectedSquarableWays) return [];
61245
61246
61247                 // user-configurable square threshold
61248                 var storedDegreeThreshold = corePreferences('validate-square-degrees');
61249                 var degreeThreshold = isNaN(storedDegreeThreshold) ? DEFAULT_DEG_THRESHOLD : parseFloat(storedDegreeThreshold);
61250
61251                 var points = nodes.map(function(node) { return context.projection(node.loc); });
61252                 if (!geoOrthoCanOrthogonalize(points, isClosed, epsilon, degreeThreshold, true)) return [];
61253
61254                 var autoArgs;
61255                 // don't allow autosquaring features linked to wikidata
61256                 if (!entity.tags.wikidata) {
61257                     // use same degree threshold as for detection
61258                     var autoAction = actionOrthogonalize(entity.id, context.projection, undefined, degreeThreshold);
61259                     autoAction.transitionable = false;  // when autofixing, do it instantly
61260                     autoArgs = [autoAction, _t('operations.orthogonalize.annotation.feature.single')];
61261                 }
61262
61263                 return [new validationIssue({
61264                     type: type,
61265                     subtype: 'building',
61266                     severity: 'warning',
61267                     message: function(context) {
61268                         var entity = context.hasEntity(this.entityIds[0]);
61269                         return entity ? _t('issues.unsquare_way.message', { feature: utilDisplayLabel(entity, context.graph()) }) : '';
61270                     },
61271                     reference: showReference,
61272                     entityIds: [entity.id],
61273                     hash: JSON.stringify(autoArgs !== undefined) + degreeThreshold,
61274                     dynamicFixes: function() {
61275                         return [
61276                             new validationIssueFix({
61277                                 icon: 'iD-operation-orthogonalize',
61278                                 title: _t('issues.fix.square_feature.title'),
61279                                 autoArgs: autoArgs,
61280                                 onClick: function(context, completionHandler) {
61281                                     var entityId = this.issue.entityIds[0];
61282                                     // use same degree threshold as for detection
61283                                     context.perform(
61284                                         actionOrthogonalize(entityId, context.projection, undefined, degreeThreshold),
61285                                         _t('operations.orthogonalize.annotation.feature.single')
61286                                     );
61287                                     // run after the squaring transition (currently 150ms)
61288                                     window.setTimeout(function() { completionHandler(); }, 175);
61289                                 }
61290                             }),
61291                             /*
61292                             new validationIssueFix({
61293                                 title: t('issues.fix.tag_as_unsquare.title'),
61294                                 onClick: function(context) {
61295                                     var entityId = this.issue.entityIds[0];
61296                                     var entity = context.entity(entityId);
61297                                     var tags = Object.assign({}, entity.tags);  // shallow copy
61298                                     tags.nonsquare = 'yes';
61299                                     context.perform(
61300                                         actionChangeTags(entityId, tags),
61301                                         t('issues.fix.tag_as_unsquare.annotation')
61302                                     );
61303                                 }
61304                             })
61305                             */
61306                         ];
61307                     }
61308                 })];
61309
61310                 function showReference(selection) {
61311                     selection.selectAll('.issue-reference')
61312                         .data([0])
61313                         .enter()
61314                         .append('div')
61315                         .attr('class', 'issue-reference')
61316                         .text(_t('issues.unsquare_way.buildings.reference'));
61317                 }
61318             };
61319
61320             validation.type = type;
61321
61322             return validation;
61323         }
61324
61325         var Validations = /*#__PURE__*/Object.freeze({
61326                 __proto__: null,
61327                 validationAlmostJunction: validationAlmostJunction,
61328                 validationCloseNodes: validationCloseNodes,
61329                 validationCrossingWays: validationCrossingWays,
61330                 validationDisconnectedWay: validationDisconnectedWay,
61331                 validationFormatting: validationFormatting,
61332                 validationHelpRequest: validationHelpRequest,
61333                 validationImpossibleOneway: validationImpossibleOneway,
61334                 validationIncompatibleSource: validationIncompatibleSource,
61335                 validationMaprules: validationMaprules,
61336                 validationMismatchedGeometry: validationMismatchedGeometry,
61337                 validationMissingRole: validationMissingRole,
61338                 validationMissingTag: validationMissingTag,
61339                 validationOutdatedTags: validationOutdatedTags,
61340                 validationPrivateData: validationPrivateData,
61341                 validationSuspiciousName: validationSuspiciousName,
61342                 validationUnsquareWay: validationUnsquareWay
61343         });
61344
61345         function coreValidator(context) {
61346             var dispatch$1 = dispatch('validated', 'focusedIssue');
61347             var validator = utilRebind({}, dispatch$1, 'on');
61348
61349             var _rules = {};
61350             var _disabledRules = {};
61351
61352             var _ignoredIssueIDs = {};          // issue.id -> true
61353             var _baseCache = validationCache(); // issues before any user edits
61354             var _headCache = validationCache(); // issues after all user edits
61355             var _validatedGraph = null;
61356             var _deferred = new Set();
61357
61358             //
61359             // initialize the validator rulesets
61360             //
61361             validator.init = function() {
61362                 Object.values(Validations).forEach(function(validation) {
61363                     if (typeof validation !== 'function') return;
61364
61365                     var fn = validation(context);
61366                     var key = fn.type;
61367                     _rules[key] = fn;
61368                 });
61369
61370                 var disabledRules = corePreferences('validate-disabledRules');
61371                 if (disabledRules) {
61372                     disabledRules.split(',')
61373                         .forEach(function(key) { _disabledRules[key] = true; });
61374                 }
61375             };
61376
61377
61378             //
61379             // clear caches, called whenever iD resets after a save
61380             //
61381             validator.reset = function() {
61382                 Array.from(_deferred).forEach(function(handle) {
61383                     window.cancelIdleCallback(handle);
61384                     _deferred.delete(handle);
61385                 });
61386
61387                 // clear caches
61388                 _ignoredIssueIDs = {};
61389                 _baseCache = validationCache();
61390                 _headCache = validationCache();
61391                 _validatedGraph = null;
61392             };
61393
61394             validator.resetIgnoredIssues = function() {
61395                 _ignoredIssueIDs = {};
61396                 // reload UI
61397                 dispatch$1.call('validated');
61398             };
61399
61400
61401             // must update issues when the user changes the unsquare thereshold
61402             validator.reloadUnsquareIssues = function() {
61403
61404                 reloadUnsquareIssues(_headCache, context.graph());
61405                 reloadUnsquareIssues(_baseCache, context.history().base());
61406
61407                 dispatch$1.call('validated');
61408             };
61409
61410             function reloadUnsquareIssues(cache, graph) {
61411
61412                 var checkUnsquareWay = _rules.unsquare_way;
61413                 if (typeof checkUnsquareWay !== 'function') return;
61414
61415                 // uncache existing
61416                 cache.uncacheIssuesOfType('unsquare_way');
61417
61418                 var buildings = context.history().tree().intersects(geoExtent([-180,-90],[180, 90]), graph)  // everywhere
61419                     .filter(function(entity) {
61420                         return entity.type === 'way' && entity.tags.building && entity.tags.building !== 'no';
61421                     });
61422
61423                 // rerun for all buildings
61424                 buildings.forEach(function(entity) {
61425                     var detected = checkUnsquareWay(entity, graph);
61426                     if (detected.length !== 1) return;
61427                     var issue = detected[0];
61428                     if (!cache.issuesByEntityID[entity.id]) {
61429                         cache.issuesByEntityID[entity.id] = new Set();
61430                     }
61431                     cache.issuesByEntityID[entity.id].add(issue.id);
61432                     cache.issuesByIssueID[issue.id] = issue;
61433                 });
61434             }
61435
61436             // options = {
61437             //     what: 'all',     // 'all' or 'edited'
61438             //     where: 'all',   // 'all' or 'visible'
61439             //     includeIgnored: false   // true, false, or 'only'
61440             //     includeDisabledRules: false   // true, false, or 'only'
61441             // };
61442             validator.getIssues = function(options) {
61443                 var opts = Object.assign({ what: 'all', where: 'all', includeIgnored: false, includeDisabledRules: false }, options);
61444                 var issues = Object.values(_headCache.issuesByIssueID);
61445                 var view = context.map().extent();
61446
61447                 return issues.filter(function(issue) {
61448                     if (!issue) return false;
61449                     if (opts.includeDisabledRules === 'only' && !_disabledRules[issue.type]) return false;
61450                     if (!opts.includeDisabledRules && _disabledRules[issue.type]) return false;
61451
61452                     if (opts.includeIgnored === 'only' && !_ignoredIssueIDs[issue.id]) return false;
61453                     if (!opts.includeIgnored && _ignoredIssueIDs[issue.id]) return false;
61454
61455                     // Sanity check:  This issue may be for an entity that not longer exists.
61456                     // If we detect this, uncache and return false so it is not included..
61457                     var entityIds = issue.entityIds || [];
61458                     for (var i = 0; i < entityIds.length; i++) {
61459                         var entityId = entityIds[i];
61460                         if (!context.hasEntity(entityId)) {
61461                             delete _headCache.issuesByEntityID[entityId];
61462                             delete _headCache.issuesByIssueID[issue.id];
61463                             return false;
61464                         }
61465                     }
61466
61467                     if (opts.what === 'edited' && _baseCache.issuesByIssueID[issue.id]) return false;
61468
61469                     if (opts.where === 'visible') {
61470                         var extent = issue.extent(context.graph());
61471                         if (!view.intersects(extent)) return false;
61472                     }
61473
61474                     return true;
61475                 });
61476             };
61477
61478             validator.getResolvedIssues = function() {
61479                 var baseIssues = Object.values(_baseCache.issuesByIssueID);
61480                 return baseIssues.filter(function(issue) {
61481                     return !_headCache.issuesByIssueID[issue.id];
61482                 });
61483             };
61484
61485             validator.focusIssue = function(issue) {
61486                 var extent = issue.extent(context.graph());
61487
61488                 if (extent) {
61489                     var setZoom = Math.max(context.map().zoom(), 19);
61490                     context.map().unobscuredCenterZoomEase(extent.center(), setZoom);
61491
61492                     // select the first entity
61493                     if (issue.entityIds && issue.entityIds.length) {
61494                         window.setTimeout(function() {
61495                             var ids = issue.entityIds;
61496                             context.enter(modeSelect(context, [ids[0]]));
61497                             dispatch$1.call('focusedIssue', this, issue);
61498                         }, 250);  // after ease
61499                     }
61500                 }
61501             };
61502
61503
61504             validator.getIssuesBySeverity = function(options) {
61505                 var groups = utilArrayGroupBy(validator.getIssues(options), 'severity');
61506                 groups.error = groups.error || [];
61507                 groups.warning = groups.warning || [];
61508                 return groups;
61509             };
61510
61511             // show some issue types in a particular order
61512             var orderedIssueTypes = [
61513                 // flag missing data first
61514                 'missing_tag', 'missing_role',
61515                 // then flag identity issues
61516                 'outdated_tags', 'mismatched_geometry',
61517                 // flag geometry issues where fixing them might solve connectivity issues
61518                 'crossing_ways', 'almost_junction',
61519                 // then flag connectivity issues
61520                 'disconnected_way', 'impossible_oneway'
61521             ];
61522
61523             // returns the issues that the given entity IDs have in common, matching the given options
61524             validator.getSharedEntityIssues = function(entityIDs, options) {
61525                 var cache = _headCache;
61526
61527                 // gather the issues that are common to all the entities
61528                 var issueIDs = entityIDs.reduce(function(acc, entityID) {
61529                     var entityIssueIDs = cache.issuesByEntityID[entityID] || new Set();
61530                     if (!acc) {
61531                         return new Set(entityIssueIDs);
61532                     }
61533                     return new Set([...acc].filter(function(elem) {
61534                         return entityIssueIDs.has(elem);
61535                     }));
61536                 }, null) || [];
61537
61538                 var opts = options || {};
61539
61540                 return Array.from(issueIDs)
61541                     .map(function(id) { return cache.issuesByIssueID[id]; })
61542                     .filter(function(issue) {
61543                         if (!issue) return false;
61544                         if (opts.includeDisabledRules === 'only' && !_disabledRules[issue.type]) return false;
61545                         if (!opts.includeDisabledRules && _disabledRules[issue.type]) return false;
61546
61547                         if (opts.includeIgnored === 'only' && !_ignoredIssueIDs[issue.id]) return false;
61548                         if (!opts.includeIgnored && _ignoredIssueIDs[issue.id]) return false;
61549
61550                         return true;
61551                     }).sort(function(issue1, issue2) {
61552                         if (issue1.type === issue2.type) {
61553                             // issues of the same type, sort deterministically
61554                             return issue1.id < issue2.id ? -1 : 1;
61555                         }
61556                         var index1 = orderedIssueTypes.indexOf(issue1.type);
61557                         var index2 = orderedIssueTypes.indexOf(issue2.type);
61558                         if (index1 !== -1 && index2 !== -1) {
61559                             // both issue types have explicit sort orders
61560                             return index1 - index2;
61561                         } else if (index1 === -1 && index2 === -1) {
61562                             // neither issue type has an explicit sort order, sort by type
61563                             return issue1.type < issue2.type ? -1 : 1;
61564                         } else {
61565                             // order explicit types before everything else
61566                             return index1 !== -1 ? -1 : 1;
61567                         }
61568                     });
61569             };
61570
61571
61572             validator.getEntityIssues = function(entityID, options) {
61573                 return validator.getSharedEntityIssues([entityID], options);
61574             };
61575
61576
61577             validator.getRuleKeys = function() {
61578                 return Object.keys(_rules);
61579             };
61580
61581
61582             validator.isRuleEnabled = function(key) {
61583                 return !_disabledRules[key];
61584             };
61585
61586
61587             validator.toggleRule = function(key) {
61588                 if (_disabledRules[key]) {
61589                     delete _disabledRules[key];
61590                 } else {
61591                     _disabledRules[key] = true;
61592                 }
61593
61594                 corePreferences('validate-disabledRules', Object.keys(_disabledRules).join(','));
61595                 validator.validate();
61596             };
61597
61598
61599             validator.disableRules = function(keys) {
61600                 _disabledRules = {};
61601                 keys.forEach(function(k) {
61602                     _disabledRules[k] = true;
61603                 });
61604
61605                 corePreferences('validate-disabledRules', Object.keys(_disabledRules).join(','));
61606                 validator.validate();
61607             };
61608
61609
61610             validator.ignoreIssue = function(id) {
61611                 _ignoredIssueIDs[id] = true;
61612             };
61613
61614
61615             //
61616             // Run validation on a single entity for the given graph
61617             //
61618             function validateEntity(entity, graph) {
61619                 var entityIssues = [];
61620
61621                 // runs validation and appends resulting issues
61622                 function runValidation(key) {
61623
61624                     var fn = _rules[key];
61625                     if (typeof fn !== 'function') {
61626                         console.error('no such validation rule = ' + key);  // eslint-disable-line no-console
61627                         return;
61628                     }
61629
61630                     var detected = fn(entity, graph);
61631                     entityIssues = entityIssues.concat(detected);
61632                 }
61633
61634                 // run all rules
61635                 Object.keys(_rules).forEach(runValidation);
61636
61637                 return entityIssues;
61638             }
61639
61640             function entityIDsToValidate(entityIDs, graph) {
61641                 var processedIDs = new Set();
61642                 return entityIDs.reduce(function(acc, entityID) {
61643                     // keep redundancy check separate from `acc` because an `entityID`
61644                     // could have been added to `acc` as a related entity through an earlier pass
61645                     if (processedIDs.has(entityID)) return acc;
61646                     processedIDs.add(entityID);
61647
61648                     var entity = graph.hasEntity(entityID);
61649                     if (!entity) return acc;
61650
61651                     acc.add(entityID);
61652
61653                     var checkParentRels = [entity];
61654
61655                     if (entity.type === 'node') {
61656                         graph.parentWays(entity).forEach(function(parentWay) {
61657                             acc.add(parentWay.id); // include parent ways
61658                             checkParentRels.push(parentWay);
61659                         });
61660                     } else if (entity.type === 'relation') {
61661                         entity.members.forEach(function(member) {
61662                             acc.add(member.id); // include members
61663                         });
61664                     } else if (entity.type === 'way') {
61665                         entity.nodes.forEach(function(nodeID) {
61666                             acc.add(nodeID); // include child nodes
61667                             graph._parentWays[nodeID].forEach(function(wayID) {
61668                                 acc.add(wayID); // include connected ways
61669                             });
61670                         });
61671                     }
61672
61673                     checkParentRels.forEach(function(entity) {   // include parent relations
61674                         if (entity.type !== 'relation') {        // but not super-relations
61675                             graph.parentRelations(entity).forEach(function(parentRelation) {
61676                                 acc.add(parentRelation.id);
61677                             });
61678                         }
61679                     });
61680
61681                     return acc;
61682
61683                 }, new Set());
61684             }
61685
61686             //
61687             // Run validation for several entities, supplied `entityIDs`,
61688             // against `graph` for the given `cache`
61689             //
61690             function validateEntities(entityIDs, graph, cache) {
61691
61692                 // clear caches for existing issues related to these entities
61693                 entityIDs.forEach(cache.uncacheEntityID);
61694
61695                 // detect new issues and update caches
61696                 entityIDs.forEach(function(entityID) {
61697                     var entity = graph.hasEntity(entityID);
61698                     // don't validate deleted entities
61699                     if (!entity) return;
61700
61701                     var issues = validateEntity(entity, graph);
61702                     cache.cacheIssues(issues);
61703                 });
61704             }
61705
61706
61707             //
61708             // Validates anything that has changed since the last time it was run.
61709             // Also updates the "validatedGraph" to be the current graph
61710             // and dispatches a `validated` event when finished.
61711             //
61712             validator.validate = function() {
61713
61714                 var currGraph = context.graph();
61715                 _validatedGraph = _validatedGraph || context.history().base();
61716                 if (currGraph === _validatedGraph) {
61717                     dispatch$1.call('validated');
61718                     return;
61719                 }
61720                 var oldGraph = _validatedGraph;
61721                 var difference = coreDifference(oldGraph, currGraph);
61722                 _validatedGraph = currGraph;
61723
61724                 var createdAndModifiedEntityIDs = difference.extantIDs(true);   // created/modified (true = w/relation members)
61725                 var entityIDsToCheck = entityIDsToValidate(createdAndModifiedEntityIDs, currGraph);
61726
61727                 // check modified and deleted entities against the old graph in order to update their related entities
61728                 // (e.g. deleting the only highway connected to a road should create a disconnected highway issue)
61729                 var modifiedAndDeletedEntityIDs = difference.deleted().concat(difference.modified())
61730                     .map(function(entity) { return entity.id; });
61731                 var entityIDsToCheckForOldGraph = entityIDsToValidate(modifiedAndDeletedEntityIDs, oldGraph);
61732
61733                 // concat the sets
61734                 entityIDsToCheckForOldGraph.forEach(entityIDsToCheck.add, entityIDsToCheck);
61735
61736                 validateEntities(entityIDsToCheck, context.graph(), _headCache);
61737
61738                 dispatch$1.call('validated');
61739             };
61740
61741
61742             // WHEN TO RUN VALIDATION:
61743             // When graph changes:
61744             context.history()
61745                 .on('restore.validator', validator.validate)   // restore saved history
61746                 .on('undone.validator', validator.validate)    // undo
61747                 .on('redone.validator', validator.validate);   // redo
61748                 // but not on 'change' (e.g. while drawing)
61749
61750             // When user chages editing modes:
61751             context
61752                 .on('exit.validator', validator.validate);
61753
61754             // When merging fetched data:
61755             context.history()
61756                 .on('merge.validator', function(entities) {
61757                     if (!entities) return;
61758                     var handle = window.requestIdleCallback(function() {
61759                         var entityIDs = entities.map(function(entity) { return entity.id; });
61760                         var headGraph = context.graph();
61761                         validateEntities(entityIDsToValidate(entityIDs, headGraph), headGraph, _headCache);
61762
61763                         var baseGraph = context.history().base();
61764                         validateEntities(entityIDsToValidate(entityIDs, baseGraph), baseGraph, _baseCache);
61765
61766                         dispatch$1.call('validated');
61767                     });
61768                     _deferred.add(handle);
61769                 });
61770
61771
61772             return validator;
61773         }
61774
61775
61776         function validationCache() {
61777
61778             var cache = {
61779                 issuesByIssueID: {},  // issue.id -> issue
61780                 issuesByEntityID: {} // entity.id -> set(issue.id)
61781             };
61782
61783             cache.cacheIssues = function(issues) {
61784                 issues.forEach(function(issue) {
61785                     var entityIds = issue.entityIds || [];
61786                     entityIds.forEach(function(entityId) {
61787                         if (!cache.issuesByEntityID[entityId]) {
61788                             cache.issuesByEntityID[entityId] = new Set();
61789                         }
61790                         cache.issuesByEntityID[entityId].add(issue.id);
61791                     });
61792                     cache.issuesByIssueID[issue.id] = issue;
61793                 });
61794             };
61795
61796             cache.uncacheIssue = function(issue) {
61797                 // When multiple entities are involved (e.g. crossing_ways),
61798                 // remove this issue from the other entity caches too..
61799                 var entityIds = issue.entityIds || [];
61800                 entityIds.forEach(function(entityId) {
61801                     if (cache.issuesByEntityID[entityId]) {
61802                         cache.issuesByEntityID[entityId].delete(issue.id);
61803                     }
61804                 });
61805                 delete cache.issuesByIssueID[issue.id];
61806             };
61807
61808             cache.uncacheIssues = function(issues) {
61809                 issues.forEach(cache.uncacheIssue);
61810             };
61811
61812             cache.uncacheIssuesOfType = function(type) {
61813                 var issuesOfType = Object.values(cache.issuesByIssueID)
61814                     .filter(function(issue) { return issue.type === type; });
61815                 cache.uncacheIssues(issuesOfType);
61816             };
61817
61818             //
61819             // Remove a single entity and all its related issues from the caches
61820             //
61821             cache.uncacheEntityID = function(entityID) {
61822                 var issueIDs = cache.issuesByEntityID[entityID];
61823                 if (!issueIDs) return;
61824
61825                 issueIDs.forEach(function(issueID) {
61826                     var issue = cache.issuesByIssueID[issueID];
61827                     if (issue) {
61828                         cache.uncacheIssue(issue);
61829                     } else {
61830                         delete cache.issuesByIssueID[issueID];
61831                     }
61832                 });
61833
61834                 delete cache.issuesByEntityID[entityID];
61835             };
61836
61837             return cache;
61838         }
61839
61840         function coreUploader(context) {
61841
61842             var dispatch$1 = dispatch(
61843                 // Start and end events are dispatched exactly once each per legitimate outside call to `save`
61844                 'saveStarted', // dispatched as soon as a call to `save` has been deemed legitimate
61845                 'saveEnded',   // dispatched after the result event has been dispatched
61846
61847                 'willAttemptUpload', // dispatched before the actual upload call occurs, if it will
61848                 'progressChanged',
61849
61850                 // Each save results in one of these outcomes:
61851                 'resultNoChanges', // upload wasn't attempted since there were no edits
61852                 'resultErrors',    // upload failed due to errors
61853                 'resultConflicts', // upload failed due to data conflicts
61854                 'resultSuccess'    // upload completed without errors
61855             );
61856
61857             var _isSaving = false;
61858
61859             var _conflicts = [];
61860             var _errors = [];
61861             var _origChanges;
61862
61863             var _discardTags = {};
61864             _mainFileFetcher.get('discarded')
61865                 .then(function(d) { _discardTags = d; })
61866                 .catch(function() { /* ignore */ });
61867
61868             var uploader = utilRebind({}, dispatch$1, 'on');
61869
61870             uploader.isSaving = function() {
61871                 return _isSaving;
61872             };
61873
61874             uploader.save = function(changeset, tryAgain, checkConflicts) {
61875                 // Guard against accidentally entering save code twice - #4641
61876                 if (_isSaving && !tryAgain) {
61877                     return;
61878                 }
61879
61880                 var osm = context.connection();
61881                 if (!osm) return;
61882
61883                 // If user somehow got logged out mid-save, try to reauthenticate..
61884                 // This can happen if they were logged in from before, but the tokens are no longer valid.
61885                 if (!osm.authenticated()) {
61886                     osm.authenticate(function(err) {
61887                         if (!err) {
61888                             uploader.save(changeset, tryAgain, checkConflicts);  // continue where we left off..
61889                         }
61890                     });
61891                     return;
61892                 }
61893
61894                 if (!_isSaving) {
61895                     _isSaving = true;
61896                     dispatch$1.call('saveStarted', this);
61897                 }
61898
61899                 var history = context.history();
61900
61901                 _conflicts = [];
61902                 _errors = [];
61903
61904                 // Store original changes, in case user wants to download them as an .osc file
61905                 _origChanges = history.changes(actionDiscardTags(history.difference(), _discardTags));
61906
61907                 // First time, `history.perform` a no-op action.
61908                 // Any conflict resolutions will be done as `history.replace`
61909                 // Remember to pop this later if needed
61910                 if (!tryAgain) {
61911                     history.perform(actionNoop());
61912                 }
61913
61914                 // Attempt a fast upload.. If there are conflicts, re-enter with `checkConflicts = true`
61915                 if (!checkConflicts) {
61916                     upload(changeset);
61917
61918                 // Do the full (slow) conflict check..
61919                 } else {
61920                     performFullConflictCheck(changeset);
61921                 }
61922
61923             };
61924
61925
61926             function performFullConflictCheck(changeset) {
61927
61928                 var osm = context.connection();
61929                 if (!osm) return;
61930
61931                 var history = context.history();
61932
61933                 var localGraph = context.graph();
61934                 var remoteGraph = coreGraph(history.base(), true);
61935
61936                 var summary = history.difference().summary();
61937                 var _toCheck = [];
61938                 for (var i = 0; i < summary.length; i++) {
61939                     var item = summary[i];
61940                     if (item.changeType === 'modified') {
61941                         _toCheck.push(item.entity.id);
61942                     }
61943                 }
61944
61945                 var _toLoad = withChildNodes(_toCheck, localGraph);
61946                 var _loaded = {};
61947                 var _toLoadCount = 0;
61948                 var _toLoadTotal = _toLoad.length;
61949
61950                 if (_toCheck.length) {
61951                     dispatch$1.call('progressChanged', this, _toLoadCount, _toLoadTotal);
61952                     _toLoad.forEach(function(id) { _loaded[id] = false; });
61953                     osm.loadMultiple(_toLoad, loaded);
61954                 } else {
61955                     upload(changeset);
61956                 }
61957
61958                 return;
61959
61960                 function withChildNodes(ids, graph) {
61961                     var s = new Set(ids);
61962                     ids.forEach(function(id) {
61963                         var entity = graph.entity(id);
61964                         if (entity.type !== 'way') return;
61965
61966                         graph.childNodes(entity).forEach(function(child) {
61967                             if (child.version !== undefined) {
61968                                 s.add(child.id);
61969                             }
61970                         });
61971                     });
61972
61973                     return Array.from(s);
61974                 }
61975
61976
61977                 // Reload modified entities into an alternate graph and check for conflicts..
61978                 function loaded(err, result) {
61979                     if (_errors.length) return;
61980
61981                     if (err) {
61982                         _errors.push({
61983                             msg: err.message || err.responseText,
61984                             details: [ _t('save.status_code', { code: err.status }) ]
61985                         });
61986                         didResultInErrors();
61987
61988                     } else {
61989                         var loadMore = [];
61990
61991                         result.data.forEach(function(entity) {
61992                             remoteGraph.replace(entity);
61993                             _loaded[entity.id] = true;
61994                             _toLoad = _toLoad.filter(function(val) { return val !== entity.id; });
61995
61996                             if (!entity.visible) return;
61997
61998                             // Because loadMultiple doesn't download /full like loadEntity,
61999                             // need to also load children that aren't already being checked..
62000                             var i, id;
62001                             if (entity.type === 'way') {
62002                                 for (i = 0; i < entity.nodes.length; i++) {
62003                                     id = entity.nodes[i];
62004                                     if (_loaded[id] === undefined) {
62005                                         _loaded[id] = false;
62006                                         loadMore.push(id);
62007                                     }
62008                                 }
62009                             } else if (entity.type === 'relation' && entity.isMultipolygon()) {
62010                                 for (i = 0; i < entity.members.length; i++) {
62011                                     id = entity.members[i].id;
62012                                     if (_loaded[id] === undefined) {
62013                                         _loaded[id] = false;
62014                                         loadMore.push(id);
62015                                     }
62016                                 }
62017                             }
62018                         });
62019
62020                         _toLoadCount += result.data.length;
62021                         _toLoadTotal += loadMore.length;
62022                         dispatch$1.call('progressChanged', this, _toLoadCount, _toLoadTotal);
62023
62024                         if (loadMore.length) {
62025                             _toLoad.push.apply(_toLoad, loadMore);
62026                             osm.loadMultiple(loadMore, loaded);
62027                         }
62028
62029                         if (!_toLoad.length) {
62030                             detectConflicts();
62031                             upload(changeset);
62032                         }
62033                     }
62034                 }
62035
62036
62037                 function detectConflicts() {
62038                     function choice(id, text, action) {
62039                         return {
62040                             id: id,
62041                             text: text,
62042                             action: function() {
62043                                 history.replace(action);
62044                             }
62045                         };
62046                     }
62047                     function formatUser(d) {
62048                         return '<a href="' + osm.userURL(d) + '" target="_blank">' + d + '</a>';
62049                     }
62050                     function entityName(entity) {
62051                         return utilDisplayName(entity) || (utilDisplayType(entity.id) + ' ' + entity.id);
62052                     }
62053
62054                     function sameVersions(local, remote) {
62055                         if (local.version !== remote.version) return false;
62056
62057                         if (local.type === 'way') {
62058                             var children = utilArrayUnion(local.nodes, remote.nodes);
62059                             for (var i = 0; i < children.length; i++) {
62060                                 var a = localGraph.hasEntity(children[i]);
62061                                 var b = remoteGraph.hasEntity(children[i]);
62062                                 if (a && b && a.version !== b.version) return false;
62063                             }
62064                         }
62065
62066                         return true;
62067                     }
62068
62069                     _toCheck.forEach(function(id) {
62070                         var local = localGraph.entity(id);
62071                         var remote = remoteGraph.entity(id);
62072
62073                         if (sameVersions(local, remote)) return;
62074
62075                         var merge = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags, formatUser);
62076
62077                         history.replace(merge);
62078
62079                         var mergeConflicts = merge.conflicts();
62080                         if (!mergeConflicts.length) return;  // merged safely
62081
62082                         var forceLocal = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags).withOption('force_local');
62083                         var forceRemote = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags).withOption('force_remote');
62084                         var keepMine = _t('save.conflict.' + (remote.visible ? 'keep_local' : 'restore'));
62085                         var keepTheirs = _t('save.conflict.' + (remote.visible ? 'keep_remote' : 'delete'));
62086
62087                         _conflicts.push({
62088                             id: id,
62089                             name: entityName(local),
62090                             details: mergeConflicts,
62091                             chosen: 1,
62092                             choices: [
62093                                 choice(id, keepMine, forceLocal),
62094                                 choice(id, keepTheirs, forceRemote)
62095                             ]
62096                         });
62097                     });
62098                 }
62099             }
62100
62101
62102             function upload(changeset) {
62103                 var osm = context.connection();
62104                 if (!osm) {
62105                     _errors.push({ msg: 'No OSM Service' });
62106                 }
62107
62108                 if (_conflicts.length) {
62109                     didResultInConflicts(changeset);
62110
62111                 } else if (_errors.length) {
62112                     didResultInErrors();
62113
62114                 } else {
62115                     var history = context.history();
62116                     var changes = history.changes(actionDiscardTags(history.difference(), _discardTags));
62117                     if (changes.modified.length || changes.created.length || changes.deleted.length) {
62118
62119                         dispatch$1.call('willAttemptUpload', this);
62120
62121                         osm.putChangeset(changeset, changes, uploadCallback);
62122
62123                     } else {
62124                         // changes were insignificant or reverted by user
62125                         didResultInNoChanges();
62126                     }
62127                 }
62128             }
62129
62130
62131             function uploadCallback(err, changeset) {
62132                 if (err) {
62133                     if (err.status === 409) {  // 409 Conflict
62134                         uploader.save(changeset, true, true);  // tryAgain = true, checkConflicts = true
62135                     } else {
62136                         _errors.push({
62137                             msg: err.message || err.responseText,
62138                             details: [ _t('save.status_code', { code: err.status }) ]
62139                         });
62140                         didResultInErrors();
62141                     }
62142
62143                 } else {
62144                     didResultInSuccess(changeset);
62145                 }
62146             }
62147
62148             function didResultInNoChanges() {
62149
62150                 dispatch$1.call('resultNoChanges', this);
62151
62152                 endSave();
62153
62154                 context.flush(); // reset iD
62155             }
62156
62157             function didResultInErrors() {
62158
62159                 context.history().pop();
62160
62161                 dispatch$1.call('resultErrors', this, _errors);
62162
62163                 endSave();
62164             }
62165
62166
62167             function didResultInConflicts(changeset) {
62168
62169                 _conflicts.sort(function(a, b) { return b.id.localeCompare(a.id); });
62170
62171                 dispatch$1.call('resultConflicts', this, changeset, _conflicts, _origChanges);
62172
62173                 endSave();
62174             }
62175
62176
62177             function didResultInSuccess(changeset) {
62178
62179                 // delete the edit stack cached to local storage
62180                 context.history().clearSaved();
62181
62182                 dispatch$1.call('resultSuccess', this, changeset);
62183
62184                 // Add delay to allow for postgres replication #1646 #2678
62185                 window.setTimeout(function() {
62186
62187                     endSave();
62188
62189                     context.flush(); // reset iD
62190                 }, 2500);
62191             }
62192
62193
62194             function endSave() {
62195                 _isSaving = false;
62196
62197                 dispatch$1.call('saveEnded', this);
62198             }
62199
62200
62201             uploader.cancelConflictResolution = function() {
62202                 context.history().pop();
62203             };
62204
62205
62206             uploader.processResolvedConflicts = function(changeset) {
62207                 var history = context.history();
62208
62209                 for (var i = 0; i < _conflicts.length; i++) {
62210                     if (_conflicts[i].chosen === 1) {  // user chose "use theirs"
62211                         var entity = context.hasEntity(_conflicts[i].id);
62212                         if (entity && entity.type === 'way') {
62213                             var children = utilArrayUniq(entity.nodes);
62214                             for (var j = 0; j < children.length; j++) {
62215                                 history.replace(actionRevert(children[j]));
62216                             }
62217                         }
62218                         history.replace(actionRevert(_conflicts[i].id));
62219                     }
62220                 }
62221
62222                 uploader.save(changeset, true, false);  // tryAgain = true, checkConflicts = false
62223             };
62224
62225
62226             uploader.reset = function() {
62227
62228             };
62229
62230
62231             return uploader;
62232         }
62233
62234         var isRetina = window.devicePixelRatio && window.devicePixelRatio >= 2;
62235
62236         // listen for DPI change, e.g. when dragging a browser window from a retina to non-retina screen
62237         window.matchMedia(`
62238         (-webkit-min-device-pixel-ratio: 2), /* Safari */
62239         (min-resolution: 2dppx),             /* standard */
62240         (min-resolution: 192dpi)             /* fallback */
62241     `).addListener(function() {
62242
62243             isRetina = window.devicePixelRatio && window.devicePixelRatio >= 2;
62244         });
62245
62246
62247         function localeDateString(s) {
62248             if (!s) return null;
62249             var options = { day: 'numeric', month: 'short', year: 'numeric' };
62250             var d = new Date(s);
62251             if (isNaN(d.getTime())) return null;
62252             return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
62253         }
62254
62255         function vintageRange(vintage) {
62256             var s;
62257             if (vintage.start || vintage.end) {
62258                 s = (vintage.start || '?');
62259                 if (vintage.start !== vintage.end) {
62260                     s += ' - ' + (vintage.end || '?');
62261                 }
62262             }
62263             return s;
62264         }
62265
62266
62267         function rendererBackgroundSource(data) {
62268             var source = Object.assign({}, data);   // shallow copy
62269             var _offset = [0, 0];
62270             var _name = source.name;
62271             var _description = source.description;
62272             var _best = !!source.best;
62273             var _template = source.encrypted ? utilAesDecrypt(source.template) : source.template;
62274
62275             source.tileSize = data.tileSize || 256;
62276             source.zoomExtent = data.zoomExtent || [0, 22];
62277             source.overzoom = data.overzoom !== false;
62278
62279             source.offset = function(val) {
62280                 if (!arguments.length) return _offset;
62281                 _offset = val;
62282                 return source;
62283             };
62284
62285
62286             source.nudge = function(val, zoomlevel) {
62287                 _offset[0] += val[0] / Math.pow(2, zoomlevel);
62288                 _offset[1] += val[1] / Math.pow(2, zoomlevel);
62289                 return source;
62290             };
62291
62292
62293             source.name = function() {
62294                 var id_safe = source.id.replace(/\./g, '<TX_DOT>');
62295                 return _t('imagery.' + id_safe + '.name', { default: _name });
62296             };
62297
62298
62299             source.description = function() {
62300                 var id_safe = source.id.replace(/\./g, '<TX_DOT>');
62301                 return _t('imagery.' + id_safe + '.description', { default: _description });
62302             };
62303
62304
62305             source.best = function() {
62306                 return _best;
62307             };
62308
62309
62310             source.area = function() {
62311                 if (!data.polygon) return Number.MAX_VALUE;  // worldwide
62312                 var area = d3_geoArea({ type: 'MultiPolygon', coordinates: [ data.polygon ] });
62313                 return isNaN(area) ? 0 : area;
62314             };
62315
62316
62317             source.imageryUsed = function() {
62318                 return name || source.id;
62319             };
62320
62321
62322             source.template = function(val) {
62323                 if (!arguments.length) return _template;
62324                 if (source.id === 'custom') {
62325                     _template = val;
62326                 }
62327                 return source;
62328             };
62329
62330
62331             source.url = function(coord) {
62332                 var result = _template;
62333                 if (result === '') return result;   // source 'none'
62334
62335
62336                 // Guess a type based on the tokens present in the template
62337                 // (This is for 'custom' source, where we don't know)
62338                 if (!source.type) {
62339                     if (/\{(proj|wkid|bbox)\}/.test(_template)) {
62340                         source.type = 'wms';
62341                         source.projection = 'EPSG:3857';  // guess
62342                     } else if (/\{(x|y)\}/.test(_template)) {
62343                         source.type = 'tms';
62344                     } else if (/\{u\}/.test(_template)) {
62345                         source.type = 'bing';
62346                     }
62347                 }
62348
62349
62350                 if (source.type === 'wms') {
62351                     var tileToProjectedCoords = (function(x, y, z) {
62352                         //polyfill for IE11, PhantomJS
62353                         var sinh = Math.sinh || function(x) {
62354                             var y = Math.exp(x);
62355                             return (y - 1 / y) / 2;
62356                         };
62357
62358                         var zoomSize = Math.pow(2, z);
62359                         var lon = x / zoomSize * Math.PI * 2 - Math.PI;
62360                         var lat = Math.atan(sinh(Math.PI * (1 - 2 * y / zoomSize)));
62361
62362                         switch (source.projection) {
62363                             case 'EPSG:4326':
62364                                 return {
62365                                     x: lon * 180 / Math.PI,
62366                                     y: lat * 180 / Math.PI
62367                                 };
62368                             default: // EPSG:3857 and synonyms
62369                                 var mercCoords = mercatorRaw(lon, lat);
62370                                 return {
62371                                     x: 20037508.34 / Math.PI * mercCoords[0],
62372                                     y: 20037508.34 / Math.PI * mercCoords[1]
62373                                 };
62374                         }
62375                     });
62376
62377                     var tileSize = source.tileSize;
62378                     var projection = source.projection;
62379                     var minXmaxY = tileToProjectedCoords(coord[0], coord[1], coord[2]);
62380                     var maxXminY = tileToProjectedCoords(coord[0]+1, coord[1]+1, coord[2]);
62381
62382                     result = result.replace(/\{(\w+)\}/g, function (token, key) {
62383                       switch (key) {
62384                         case 'width':
62385                         case 'height':
62386                           return tileSize;
62387                         case 'proj':
62388                           return projection;
62389                         case 'wkid':
62390                           return projection.replace(/^EPSG:/, '');
62391                         case 'bbox':
62392                           return minXmaxY.x + ',' + maxXminY.y + ',' + maxXminY.x + ',' + minXmaxY.y;
62393                         case 'w':
62394                           return minXmaxY.x;
62395                         case 's':
62396                           return maxXminY.y;
62397                         case 'n':
62398                           return maxXminY.x;
62399                         case 'e':
62400                           return minXmaxY.y;
62401                         default:
62402                           return token;
62403                       }
62404                     });
62405
62406                 } else if (source.type === 'tms') {
62407                     result = result
62408                         .replace('{x}', coord[0])
62409                         .replace('{y}', coord[1])
62410                         // TMS-flipped y coordinate
62411                         .replace(/\{[t-]y\}/, Math.pow(2, coord[2]) - coord[1] - 1)
62412                         .replace(/\{z(oom)?\}/, coord[2])
62413                         // only fetch retina tiles for retina screens
62414                         .replace(/\{@2x\}|\{r\}/, isRetina ? '@2x' : '');
62415
62416                 } else if (source.type === 'bing') {
62417                     result = result
62418                         .replace('{u}', function() {
62419                             var u = '';
62420                             for (var zoom = coord[2]; zoom > 0; zoom--) {
62421                                 var b = 0;
62422                                 var mask = 1 << (zoom - 1);
62423                                 if ((coord[0] & mask) !== 0) b++;
62424                                 if ((coord[1] & mask) !== 0) b += 2;
62425                                 u += b.toString();
62426                             }
62427                             return u;
62428                         });
62429                 }
62430
62431                 // these apply to any type..
62432                 result = result.replace(/\{switch:([^}]+)\}/, function(s, r) {
62433                     var subdomains = r.split(',');
62434                     return subdomains[(coord[0] + coord[1]) % subdomains.length];
62435                 });
62436
62437
62438                 return result;
62439             };
62440
62441
62442             source.validZoom = function(z) {
62443                 return source.zoomExtent[0] <= z &&
62444                     (source.overzoom || source.zoomExtent[1] > z);
62445             };
62446
62447
62448             source.isLocatorOverlay = function() {
62449                 return source.id === 'mapbox_locator_overlay';
62450             };
62451
62452
62453             /* hides a source from the list, but leaves it available for use */
62454             source.isHidden = function() {
62455                 return source.id === 'DigitalGlobe-Premium-vintage' ||
62456                     source.id === 'DigitalGlobe-Standard-vintage';
62457             };
62458
62459
62460             source.copyrightNotices = function() {};
62461
62462
62463             source.getMetadata = function(center, tileCoord, callback) {
62464                 var vintage = {
62465                     start: localeDateString(source.startDate),
62466                     end: localeDateString(source.endDate)
62467                 };
62468                 vintage.range = vintageRange(vintage);
62469
62470                 var metadata = { vintage: vintage };
62471                 callback(null, metadata);
62472             };
62473
62474
62475             return source;
62476         }
62477
62478
62479         rendererBackgroundSource.Bing = function(data, dispatch) {
62480             // http://msdn.microsoft.com/en-us/library/ff701716.aspx
62481             // http://msdn.microsoft.com/en-us/library/ff701701.aspx
62482
62483             data.template = 'https://ecn.t{switch:0,1,2,3}.tiles.virtualearth.net/tiles/a{u}.jpeg?g=587&mkt=en-gb&n=z';
62484
62485             var bing = rendererBackgroundSource(data);
62486             // var key = 'Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU'; // P2, JOSM, etc
62487             var key = 'Ak5oTE46TUbjRp08OFVcGpkARErDobfpuyNKa-W2mQ8wbt1K1KL8p1bIRwWwcF-Q';    // iD
62488
62489
62490             var url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial?include=ImageryProviders&key=' + key;
62491             var cache = {};
62492             var inflight = {};
62493             var providers = [];
62494
62495             d3_json(url)
62496                 .then(function(json) {
62497                     providers = json.resourceSets[0].resources[0].imageryProviders.map(function(provider) {
62498                         return {
62499                             attribution: provider.attribution,
62500                             areas: provider.coverageAreas.map(function(area) {
62501                                 return {
62502                                     zoom: [area.zoomMin, area.zoomMax],
62503                                     extent: geoExtent([area.bbox[1], area.bbox[0]], [area.bbox[3], area.bbox[2]])
62504                                 };
62505                             })
62506                         };
62507                     });
62508                     dispatch.call('change');
62509                 })
62510                 .catch(function() {
62511                     /* ignore */
62512                 });
62513
62514
62515             bing.copyrightNotices = function(zoom, extent) {
62516                 zoom = Math.min(zoom, 21);
62517                 return providers.filter(function(provider) {
62518                     return provider.areas.some(function(area) {
62519                         return extent.intersects(area.extent) &&
62520                             area.zoom[0] <= zoom &&
62521                             area.zoom[1] >= zoom;
62522                     });
62523                 }).map(function(provider) {
62524                     return provider.attribution;
62525                 }).join(', ');
62526             };
62527
62528
62529             bing.getMetadata = function(center, tileCoord, callback) {
62530                 var tileID = tileCoord.slice(0, 3).join('/');
62531                 var zoom = Math.min(tileCoord[2], 21);
62532                 var centerPoint = center[1] + ',' + center[0];  // lat,lng
62533                 var url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial/' + centerPoint +
62534                         '?zl=' + zoom + '&key=' + key;
62535
62536                 if (inflight[tileID]) return;
62537
62538                 if (!cache[tileID]) {
62539                     cache[tileID] = {};
62540                 }
62541                 if (cache[tileID] && cache[tileID].metadata) {
62542                     return callback(null, cache[tileID].metadata);
62543                 }
62544
62545                 inflight[tileID] = true;
62546                 d3_json(url)
62547                     .then(function(result) {
62548                         delete inflight[tileID];
62549                         if (!result) {
62550                             throw new Error('Unknown Error');
62551                         }
62552                         var vintage = {
62553                             start: localeDateString(result.resourceSets[0].resources[0].vintageStart),
62554                             end: localeDateString(result.resourceSets[0].resources[0].vintageEnd)
62555                         };
62556                         vintage.range = vintageRange(vintage);
62557
62558                         var metadata = { vintage: vintage };
62559                         cache[tileID].metadata = metadata;
62560                         if (callback) callback(null, metadata);
62561                     })
62562                     .catch(function(err) {
62563                         delete inflight[tileID];
62564                         if (callback) callback(err.message);
62565                     });
62566             };
62567
62568
62569             bing.terms_url = 'https://blog.openstreetmap.org/2010/11/30/microsoft-imagery-details';
62570
62571
62572             return bing;
62573         };
62574
62575
62576
62577         rendererBackgroundSource.Esri = function(data) {
62578             // in addition to using the tilemap at zoom level 20, overzoom real tiles - #4327 (deprecated technique, but it works)
62579             if (data.template.match(/blankTile/) === null) {
62580                 data.template = data.template + '?blankTile=false';
62581             }
62582
62583             var esri = rendererBackgroundSource(data);
62584             var cache = {};
62585             var inflight = {};
62586             var _prevCenter;
62587
62588             // use a tilemap service to set maximum zoom for esri tiles dynamically
62589             // https://developers.arcgis.com/documentation/tiled-elevation-service/
62590             esri.fetchTilemap = function(center) {
62591                 // skip if we have already fetched a tilemap within 5km
62592                 if (_prevCenter && geoSphericalDistance(center, _prevCenter) < 5000) return;
62593                 _prevCenter = center;
62594
62595                 // tiles are available globally to zoom level 19, afterward they may or may not be present
62596                 var z = 20;
62597
62598                 // first generate a random url using the template
62599                 var dummyUrl = esri.url([1,2,3]);
62600
62601                 // calculate url z/y/x from the lat/long of the center of the map
62602                 var x = (Math.floor((center[0] + 180) / 360 * Math.pow(2, z)));
62603                 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)));
62604
62605                 // fetch an 8x8 grid to leverage cache
62606                 var tilemapUrl = dummyUrl.replace(/tile\/[0-9]+\/[0-9]+\/[0-9]+\?blankTile=false/, 'tilemap') + '/' + z + '/' + y + '/' + x + '/8/8';
62607
62608                 // make the request and introspect the response from the tilemap server
62609                 d3_json(tilemapUrl)
62610                     .then(function(tilemap) {
62611                         if (!tilemap) {
62612                             throw new Error('Unknown Error');
62613                         }
62614                         var hasTiles = true;
62615                         for (var i = 0; i < tilemap.data.length; i++) {
62616                             // 0 means an individual tile in the grid doesn't exist
62617                             if (!tilemap.data[i]) {
62618                                 hasTiles = false;
62619                                 break;
62620                             }
62621                         }
62622
62623                         // if any tiles are missing at level 20 we restrict maxZoom to 19
62624                         esri.zoomExtent[1] = (hasTiles ? 22 : 19);
62625                     })
62626                     .catch(function() {
62627                         /* ignore */
62628                     });
62629             };
62630
62631
62632             esri.getMetadata = function(center, tileCoord, callback) {
62633                 var tileID = tileCoord.slice(0, 3).join('/');
62634                 var zoom = Math.min(tileCoord[2], esri.zoomExtent[1]);
62635                 var centerPoint = center[0] + ',' + center[1];  // long, lat (as it should be)
62636                 var unknown = _t('info_panels.background.unknown');
62637                 var metadataLayer;
62638                 var vintage = {};
62639                 var metadata = {};
62640
62641                 if (inflight[tileID]) return;
62642
62643                 switch (true) {
62644                     case (zoom >= 20 && esri.id === 'EsriWorldImageryClarity'):
62645                         metadataLayer = 4;
62646                         break;
62647                     case zoom >= 19:
62648                         metadataLayer = 3;
62649                         break;
62650                     case zoom >= 17:
62651                         metadataLayer = 2;
62652                         break;
62653                     case zoom >= 13:
62654                         metadataLayer = 0;
62655                         break;
62656                     default:
62657                         metadataLayer = 99;
62658                 }
62659
62660                 var url;
62661                 // build up query using the layer appropriate to the current zoom
62662                 if (esri.id === 'EsriWorldImagery') {
62663                     url = 'https://services.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/';
62664                 } else if (esri.id === 'EsriWorldImageryClarity') {
62665                     url = 'https://serviceslab.arcgisonline.com/arcgis/rest/services/Clarity_World_Imagery/MapServer/';
62666                 }
62667
62668                 url += metadataLayer + '/query?returnGeometry=false&geometry=' + centerPoint + '&inSR=4326&geometryType=esriGeometryPoint&outFields=*&f=json';
62669
62670                 if (!cache[tileID]) {
62671                     cache[tileID] = {};
62672                 }
62673                 if (cache[tileID] && cache[tileID].metadata) {
62674                     return callback(null, cache[tileID].metadata);
62675                 }
62676
62677                 // accurate metadata is only available >= 13
62678                 if (metadataLayer === 99) {
62679                     vintage = {
62680                         start: null,
62681                         end: null,
62682                         range: null
62683                     };
62684                     metadata = {
62685                         vintage: null,
62686                         source: unknown,
62687                         description: unknown,
62688                         resolution: unknown,
62689                         accuracy: unknown
62690                     };
62691
62692                     callback(null, metadata);
62693
62694                 } else {
62695                     inflight[tileID] = true;
62696                     d3_json(url)
62697                         .then(function(result) {
62698                             delete inflight[tileID];
62699                             if (!result) {
62700                                 throw new Error('Unknown Error');
62701                             } else if (result.features && result.features.length < 1) {
62702                                 throw new Error('No Results');
62703                             } else if (result.error && result.error.message) {
62704                                 throw new Error(result.error.message);
62705                             }
62706
62707                             // pass through the discrete capture date from metadata
62708                             var captureDate = localeDateString(result.features[0].attributes.SRC_DATE2);
62709                             vintage = {
62710                                 start: captureDate,
62711                                 end: captureDate,
62712                                 range: captureDate
62713                             };
62714                             metadata = {
62715                                 vintage: vintage,
62716                                 source: clean(result.features[0].attributes.NICE_NAME),
62717                                 description: clean(result.features[0].attributes.NICE_DESC),
62718                                 resolution: clean(+parseFloat(result.features[0].attributes.SRC_RES).toFixed(4)),
62719                                 accuracy: clean(+parseFloat(result.features[0].attributes.SRC_ACC).toFixed(4))
62720                             };
62721
62722                             // append units - meters
62723                             if (isFinite(metadata.resolution)) {
62724                                 metadata.resolution += ' m';
62725                             }
62726                             if (isFinite(metadata.accuracy)) {
62727                                 metadata.accuracy += ' m';
62728                             }
62729
62730                             cache[tileID].metadata = metadata;
62731                             if (callback) callback(null, metadata);
62732                         })
62733                         .catch(function(err) {
62734                             delete inflight[tileID];
62735                             if (callback) callback(err.message);
62736                         });
62737                 }
62738
62739
62740                 function clean(val) {
62741                     return String(val).trim() || unknown;
62742                 }
62743             };
62744
62745             return esri;
62746         };
62747
62748
62749         rendererBackgroundSource.None = function() {
62750             var source = rendererBackgroundSource({ id: 'none', template: '' });
62751
62752
62753             source.name = function() {
62754                 return _t('background.none');
62755             };
62756
62757
62758             source.imageryUsed = function() {
62759                 return null;
62760             };
62761
62762
62763             source.area = function() {
62764                 return -1;  // sources in background pane are sorted by area
62765             };
62766
62767
62768             return source;
62769         };
62770
62771
62772         rendererBackgroundSource.Custom = function(template) {
62773             var source = rendererBackgroundSource({ id: 'custom', template: template });
62774
62775
62776             source.name = function() {
62777                 return _t('background.custom');
62778             };
62779
62780
62781             source.imageryUsed = function() {
62782                 // sanitize personal connection tokens - #6801
62783                 var cleaned = source.template();
62784
62785                 // from query string parameters
62786                 if (cleaned.indexOf('?') !== -1) {
62787                     var parts = cleaned.split('?', 2);
62788                     var qs = utilStringQs(parts[1]);
62789
62790                     ['access_token', 'connectId', 'token'].forEach(function(param) {
62791                         if (qs[param]) {
62792                             qs[param] = '{apikey}';
62793                         }
62794                     });
62795                     cleaned = parts[0] + '?' + utilQsString(qs, true);  // true = soft encode
62796                 }
62797
62798                 // from wms/wmts api path parameters
62799                 cleaned = cleaned.replace(/token\/(\w+)/, 'token/{apikey}');
62800
62801                 return 'Custom (' + cleaned + ' )';
62802             };
62803
62804
62805             source.area = function() {
62806                 return -2;  // sources in background pane are sorted by area
62807             };
62808
62809
62810             return source;
62811         };
62812
62813         function rendererTileLayer(context) {
62814             var transformProp = utilPrefixCSSProperty('Transform');
62815             var tiler = utilTiler();
62816
62817             var _tileSize = 256;
62818             var _projection;
62819             var _cache = {};
62820             var _tileOrigin;
62821             var _zoom;
62822             var _source;
62823
62824
62825             function tileSizeAtZoom(d, z) {
62826                 var EPSILON = 0.002;    // close seams
62827                 return ((_tileSize * Math.pow(2, z - d[2])) / _tileSize) + EPSILON;
62828             }
62829
62830
62831             function atZoom(t, distance) {
62832                 var power = Math.pow(2, distance);
62833                 return [
62834                     Math.floor(t[0] * power),
62835                     Math.floor(t[1] * power),
62836                     t[2] + distance
62837                 ];
62838             }
62839
62840
62841             function lookUp(d) {
62842                 for (var up = -1; up > -d[2]; up--) {
62843                     var tile = atZoom(d, up);
62844                     if (_cache[_source.url(tile)] !== false) {
62845                         return tile;
62846                     }
62847                 }
62848             }
62849
62850
62851             function uniqueBy(a, n) {
62852                 var o = [];
62853                 var seen = {};
62854                 for (var i = 0; i < a.length; i++) {
62855                     if (seen[a[i][n]] === undefined) {
62856                         o.push(a[i]);
62857                         seen[a[i][n]] = true;
62858                     }
62859                 }
62860                 return o;
62861             }
62862
62863
62864             function addSource(d) {
62865                 d.push(_source.url(d));
62866                 return d;
62867             }
62868
62869
62870             // Update tiles based on current state of `projection`.
62871             function background(selection) {
62872                 _zoom = geoScaleToZoom(_projection.scale(), _tileSize);
62873
62874                 var pixelOffset;
62875                 if (_source) {
62876                     pixelOffset = [
62877                         _source.offset()[0] * Math.pow(2, _zoom),
62878                         _source.offset()[1] * Math.pow(2, _zoom)
62879                     ];
62880                 } else {
62881                     pixelOffset = [0, 0];
62882                 }
62883
62884                 var translate = [
62885                     _projection.translate()[0] + pixelOffset[0],
62886                     _projection.translate()[1] + pixelOffset[1]
62887                 ];
62888
62889                 tiler
62890                     .scale(_projection.scale() * 2 * Math.PI)
62891                     .translate(translate);
62892
62893                 _tileOrigin = [
62894                     _projection.scale() * Math.PI - translate[0],
62895                     _projection.scale() * Math.PI - translate[1]
62896                 ];
62897
62898                 render(selection);
62899             }
62900
62901
62902             // Derive the tiles onscreen, remove those offscreen and position them.
62903             // Important that this part not depend on `_projection` because it's
62904             // rentered when tiles load/error (see #644).
62905             function render(selection) {
62906                 if (!_source) return;
62907                 var requests = [];
62908                 var showDebug = context.getDebug('tile') && !_source.overlay;
62909
62910                 if (_source.validZoom(_zoom)) {
62911                     tiler.skipNullIsland(!!_source.overlay);
62912
62913                     tiler().forEach(function(d) {
62914                         addSource(d);
62915                         if (d[3] === '') return;
62916                         if (typeof d[3] !== 'string') return; // Workaround for #2295
62917                         requests.push(d);
62918                         if (_cache[d[3]] === false && lookUp(d)) {
62919                             requests.push(addSource(lookUp(d)));
62920                         }
62921                     });
62922
62923                     requests = uniqueBy(requests, 3).filter(function(r) {
62924                         // don't re-request tiles which have failed in the past
62925                         return _cache[r[3]] !== false;
62926                     });
62927                 }
62928
62929                 function load(d) {
62930                     _cache[d[3]] = true;
62931                     select(this)
62932                         .on('error', null)
62933                         .on('load', null)
62934                         .classed('tile-loaded', true);
62935                     render(selection);
62936                 }
62937
62938                 function error(d) {
62939                     _cache[d[3]] = false;
62940                     select(this)
62941                         .on('error', null)
62942                         .on('load', null)
62943                         .remove();
62944                     render(selection);
62945                 }
62946
62947                 function imageTransform(d) {
62948                     var ts = _tileSize * Math.pow(2, _zoom - d[2]);
62949                     var scale = tileSizeAtZoom(d, _zoom);
62950                     return 'translate(' +
62951                         ((d[0] * ts) - _tileOrigin[0]) + 'px,' +
62952                         ((d[1] * ts) - _tileOrigin[1]) + 'px) ' +
62953                         'scale(' + scale + ',' + scale + ')';
62954                 }
62955
62956                 function tileCenter(d) {
62957                     var ts = _tileSize * Math.pow(2, _zoom - d[2]);
62958                     return [
62959                         ((d[0] * ts) - _tileOrigin[0] + (ts / 2)),
62960                         ((d[1] * ts) - _tileOrigin[1] + (ts / 2))
62961                     ];
62962                 }
62963
62964                 function debugTransform(d) {
62965                     var coord = tileCenter(d);
62966                     return 'translate(' + coord[0] + 'px,' + coord[1] + 'px)';
62967                 }
62968
62969
62970                 // Pick a representative tile near the center of the viewport
62971                 // (This is useful for sampling the imagery vintage)
62972                 var dims = tiler.size();
62973                 var mapCenter = [dims[0] / 2, dims[1] / 2];
62974                 var minDist = Math.max(dims[0], dims[1]);
62975                 var nearCenter;
62976
62977                 requests.forEach(function(d) {
62978                     var c = tileCenter(d);
62979                     var dist = geoVecLength(c, mapCenter);
62980                     if (dist < minDist) {
62981                         minDist = dist;
62982                         nearCenter = d;
62983                     }
62984                 });
62985
62986
62987                 var image = selection.selectAll('img')
62988                     .data(requests, function(d) { return d[3]; });
62989
62990                 image.exit()
62991                     .style(transformProp, imageTransform)
62992                     .classed('tile-removing', true)
62993                     .classed('tile-center', false)
62994                     .each(function() {
62995                         var tile = select(this);
62996                         window.setTimeout(function() {
62997                             if (tile.classed('tile-removing')) {
62998                                 tile.remove();
62999                             }
63000                         }, 300);
63001                     });
63002
63003                 image.enter()
63004                   .append('img')
63005                     .attr('class', 'tile')
63006                     .attr('draggable', 'false')
63007                     .style('width', _tileSize + 'px')
63008                     .style('height', _tileSize + 'px')
63009                     .attr('src', function(d) { return d[3]; })
63010                     .on('error', error)
63011                     .on('load', load)
63012                   .merge(image)
63013                     .style(transformProp, imageTransform)
63014                     .classed('tile-debug', showDebug)
63015                     .classed('tile-removing', false)
63016                     .classed('tile-center', function(d) { return d === nearCenter; });
63017
63018
63019
63020                 var debug = selection.selectAll('.tile-label-debug')
63021                     .data(showDebug ? requests : [], function(d) { return d[3]; });
63022
63023                 debug.exit()
63024                     .remove();
63025
63026                 if (showDebug) {
63027                     var debugEnter = debug.enter()
63028                         .append('div')
63029                         .attr('class', 'tile-label-debug');
63030
63031                     debugEnter
63032                         .append('div')
63033                         .attr('class', 'tile-label-debug-coord');
63034
63035                     debugEnter
63036                         .append('div')
63037                         .attr('class', 'tile-label-debug-vintage');
63038
63039                     debug = debug.merge(debugEnter);
63040
63041                     debug
63042                         .style(transformProp, debugTransform);
63043
63044                     debug
63045                         .selectAll('.tile-label-debug-coord')
63046                         .text(function(d) { return d[2] + ' / ' + d[0] + ' / ' + d[1]; });
63047
63048                     debug
63049                         .selectAll('.tile-label-debug-vintage')
63050                         .each(function(d) {
63051                             var span = select(this);
63052                             var center = context.projection.invert(tileCenter(d));
63053                             _source.getMetadata(center, d, function(err, result) {
63054                                 span.text((result && result.vintage && result.vintage.range) ||
63055                                     _t('info_panels.background.vintage') + ': ' + _t('info_panels.background.unknown')
63056                                 );
63057                             });
63058                         });
63059                 }
63060
63061             }
63062
63063
63064             background.projection = function(val) {
63065                 if (!arguments.length) return _projection;
63066                 _projection = val;
63067                 return background;
63068             };
63069
63070
63071             background.dimensions = function(val) {
63072                 if (!arguments.length) return tiler.size();
63073                 tiler.size(val);
63074                 return background;
63075             };
63076
63077
63078             background.source = function(val) {
63079                 if (!arguments.length) return _source;
63080                 _source = val;
63081                 _tileSize = _source.tileSize;
63082                 _cache = {};
63083                 tiler.tileSize(_source.tileSize).zoomExtent(_source.zoomExtent);
63084                 return background;
63085             };
63086
63087
63088             return background;
63089         }
63090
63091         let _imageryIndex = null;
63092
63093         function rendererBackground(context) {
63094           const dispatch$1 = dispatch('change');
63095           const detected = utilDetect();
63096           const baseLayer = rendererTileLayer(context).projection(context.projection);
63097           let _isValid = true;
63098           let _overlayLayers = [];
63099           let _brightness = 1;
63100           let _contrast = 1;
63101           let _saturation = 1;
63102           let _sharpness = 1;
63103
63104
63105           function ensureImageryIndex() {
63106             return _mainFileFetcher.get('imagery')
63107               .then(sources => {
63108                 if (_imageryIndex) return _imageryIndex;
63109
63110                 _imageryIndex = {
63111                   imagery: sources,
63112                   features: {}
63113                 };
63114
63115                 // use which-polygon to support efficient index and querying for imagery
63116                 const features = sources.map(source => {
63117                   if (!source.polygon) return null;
63118                   // workaround for editor-layer-index weirdness..
63119                   // Add an extra array nest to each element in `source.polygon`
63120                   // so the rings are not treated as a bunch of holes:
63121                   // what we have: [ [[outer],[hole],[hole]] ]
63122                   // what we want: [ [[outer]],[[outer]],[[outer]] ]
63123                   const rings = source.polygon.map(ring => [ring]);
63124
63125                   const feature = {
63126                     type: 'Feature',
63127                     properties: { id: source.id },
63128                     geometry: { type: 'MultiPolygon', coordinates: rings }
63129                   };
63130
63131                   _imageryIndex.features[source.id] = feature;
63132                   return feature;
63133
63134                 }).filter(Boolean);
63135
63136                 _imageryIndex.query = whichPolygon_1({ type: 'FeatureCollection', features: features });
63137
63138
63139                 // Instantiate `rendererBackgroundSource` objects for each source
63140                 _imageryIndex.backgrounds = sources.map(source => {
63141                   if (source.type === 'bing') {
63142                     return rendererBackgroundSource.Bing(source, dispatch$1);
63143                   } else if (/^EsriWorldImagery/.test(source.id)) {
63144                     return rendererBackgroundSource.Esri(source);
63145                   } else {
63146                     return rendererBackgroundSource(source);
63147                   }
63148                 });
63149
63150                 // Add 'None'
63151                 _imageryIndex.backgrounds.unshift(rendererBackgroundSource.None());
63152
63153                 // Add 'Custom'
63154                 let template = corePreferences('background-custom-template') || '';
63155                 const custom = rendererBackgroundSource.Custom(template);
63156                 _imageryIndex.backgrounds.unshift(custom);
63157
63158                 return _imageryIndex;
63159               });
63160           }
63161
63162
63163           function background(selection) {
63164             const currSource = baseLayer.source();
63165
63166             // If we are displaying an Esri basemap at high zoom,
63167             // check its tilemap to see how high the zoom can go
63168             if (context.map().zoom() > 18) {
63169               if (currSource && /^EsriWorldImagery/.test(currSource.id)) {
63170                 const center = context.map().center();
63171                 currSource.fetchTilemap(center);
63172               }
63173             }
63174
63175             // Is the imagery valid here? - #4827
63176             const sources = background.sources(context.map().extent());
63177             const wasValid = _isValid;
63178             _isValid = !!sources.filter(d => d === currSource).length;
63179
63180             if (wasValid !== _isValid) {      // change in valid status
63181               background.updateImagery();
63182             }
63183
63184
63185             let baseFilter = '';
63186             if (detected.cssfilters) {
63187               if (_brightness !== 1) {
63188                 baseFilter += ` brightness(${_brightness})`;
63189               }
63190               if (_contrast !== 1) {
63191                 baseFilter += ` contrast(${_contrast})`;
63192               }
63193               if (_saturation !== 1) {
63194                 baseFilter += ` saturate(${_saturation})`;
63195               }
63196               if (_sharpness < 1) {  // gaussian blur
63197                 const blur = d3_interpolateNumber(0.5, 5)(1 - _sharpness);
63198                 baseFilter += ` blur(${blur}px)`;
63199               }
63200             }
63201
63202             let base = selection.selectAll('.layer-background')
63203               .data([0]);
63204
63205             base = base.enter()
63206               .insert('div', '.layer-data')
63207               .attr('class', 'layer layer-background')
63208               .merge(base);
63209
63210             if (detected.cssfilters) {
63211               base.style('filter', baseFilter || null);
63212             } else {
63213               base.style('opacity', _brightness);
63214             }
63215
63216
63217             let imagery = base.selectAll('.layer-imagery')
63218               .data([0]);
63219
63220             imagery.enter()
63221               .append('div')
63222               .attr('class', 'layer layer-imagery')
63223               .merge(imagery)
63224               .call(baseLayer);
63225
63226
63227             let maskFilter = '';
63228             let mixBlendMode = '';
63229             if (detected.cssfilters && _sharpness > 1) {  // apply unsharp mask
63230               mixBlendMode = 'overlay';
63231               maskFilter = 'saturate(0) blur(3px) invert(1)';
63232
63233               let contrast = _sharpness - 1;
63234               maskFilter += ` contrast(${contrast})`;
63235
63236               let brightness = d3_interpolateNumber(1, 0.85)(_sharpness - 1);
63237               maskFilter += ` brightness(${brightness})`;
63238             }
63239
63240             let mask = base.selectAll('.layer-unsharp-mask')
63241               .data(detected.cssfilters && _sharpness > 1 ? [0] : []);
63242
63243             mask.exit()
63244               .remove();
63245
63246             mask.enter()
63247               .append('div')
63248               .attr('class', 'layer layer-mask layer-unsharp-mask')
63249               .merge(mask)
63250               .call(baseLayer)
63251               .style('filter', maskFilter || null)
63252               .style('mix-blend-mode', mixBlendMode || null);
63253
63254
63255             let overlays = selection.selectAll('.layer-overlay')
63256               .data(_overlayLayers, d => d.source().name());
63257
63258             overlays.exit()
63259               .remove();
63260
63261             overlays.enter()
63262               .insert('div', '.layer-data')
63263               .attr('class', 'layer layer-overlay')
63264               .merge(overlays)
63265               .each((layer, i, nodes) => select(nodes[i]).call(layer));
63266           }
63267
63268
63269           background.updateImagery = function() {
63270             let currSource = baseLayer.source();
63271             if (context.inIntro() || !currSource) return;
63272
63273             let o = _overlayLayers
63274               .filter(d => !d.source().isLocatorOverlay() && !d.source().isHidden())
63275               .map(d => d.source().id)
63276               .join(',');
63277
63278             const meters = geoOffsetToMeters(currSource.offset());
63279             const EPSILON = 0.01;
63280             const x = +meters[0].toFixed(2);
63281             const y = +meters[1].toFixed(2);
63282             let hash = utilStringQs(window.location.hash);
63283
63284             let id = currSource.id;
63285             if (id === 'custom') {
63286               id = `custom:${currSource.template()}`;
63287             }
63288
63289             if (id) {
63290               hash.background = id;
63291             } else {
63292               delete hash.background;
63293             }
63294
63295             if (o) {
63296               hash.overlays = o;
63297             } else {
63298               delete hash.overlays;
63299             }
63300
63301             if (Math.abs(x) > EPSILON || Math.abs(y) > EPSILON) {
63302               hash.offset = `${x},${y}`;
63303             } else {
63304               delete hash.offset;
63305             }
63306
63307             if (!window.mocha) {
63308               window.location.replace('#' + utilQsString(hash, true));
63309             }
63310
63311             let imageryUsed = [];
63312             let photoOverlaysUsed = [];
63313
63314             const currUsed = currSource.imageryUsed();
63315             if (currUsed && _isValid) {
63316               imageryUsed.push(currUsed);
63317             }
63318
63319             _overlayLayers
63320               .filter(d => !d.source().isLocatorOverlay() && !d.source().isHidden())
63321               .forEach(d => imageryUsed.push(d.source().imageryUsed()));
63322
63323             const dataLayer = context.layers().layer('data');
63324             if (dataLayer && dataLayer.enabled() && dataLayer.hasData()) {
63325               imageryUsed.push(dataLayer.getSrc());
63326             }
63327
63328             const photoOverlayLayers = {
63329               streetside: 'Bing Streetside',
63330               mapillary: 'Mapillary Images',
63331               'mapillary-map-features': 'Mapillary Map Features',
63332               'mapillary-signs': 'Mapillary Signs',
63333               openstreetcam: 'OpenStreetCam Images'
63334             };
63335
63336             for (let layerID in photoOverlayLayers) {
63337               const layer = context.layers().layer(layerID);
63338               if (layer && layer.enabled()) {
63339                 photoOverlaysUsed.push(layerID);
63340                 imageryUsed.push(photoOverlayLayers[layerID]);
63341               }
63342             }
63343
63344             context.history().imageryUsed(imageryUsed);
63345             context.history().photoOverlaysUsed(photoOverlaysUsed);
63346           };
63347
63348
63349           background.sources = (extent, zoom, includeCurrent) => {
63350             if (!_imageryIndex) return [];   // called before init()?
63351
63352             let visible = {};
63353             (_imageryIndex.query.bbox(extent.rectangle(), true) || [])
63354               .forEach(d => visible[d.id] = true);
63355
63356             const currSource = baseLayer.source();
63357
63358             return _imageryIndex.backgrounds.filter(source => {
63359               if (!source.polygon) return true;                          // always include imagery with worldwide coverage
63360               if (includeCurrent && currSource === source) return true;  // optionally include the current imagery
63361               if (zoom && zoom < 6) return false;                        // optionally exclude local imagery at low zooms
63362               return visible[source.id];                                 // include imagery visible in given extent
63363             });
63364           };
63365
63366
63367           background.dimensions = (val) => {
63368             if (!val) return;
63369             baseLayer.dimensions(val);
63370             _overlayLayers.forEach(layer => layer.dimensions(val));
63371           };
63372
63373
63374           background.baseLayerSource = function(d) {
63375             if (!arguments.length) return baseLayer.source();
63376
63377             // test source against OSM imagery blacklists..
63378             const osm = context.connection();
63379             if (!osm) return background;
63380
63381             const blacklists = osm.imageryBlacklists();
63382             const template = d.template();
63383             let fail = false;
63384             let tested = 0;
63385             let regex;
63386
63387             for (let i = 0; i < blacklists.length; i++) {
63388               try {
63389                 regex = new RegExp(blacklists[i]);
63390                 fail = regex.test(template);
63391                 tested++;
63392                 if (fail) break;
63393               } catch (e) {
63394                 /* noop */
63395               }
63396             }
63397
63398             // ensure at least one test was run.
63399             if (!tested) {
63400               regex = new RegExp('.*\.google(apis)?\..*/(vt|kh)[\?/].*([xyz]=.*){3}.*');
63401               fail = regex.test(template);
63402             }
63403
63404             baseLayer.source(!fail ? d : background.findSource('none'));
63405             dispatch$1.call('change');
63406             background.updateImagery();
63407             return background;
63408           };
63409
63410
63411           background.findSource = (id) => {
63412             if (!id || !_imageryIndex) return null;   // called before init()?
63413             return _imageryIndex.backgrounds.find(d => d.id && d.id === id);
63414           };
63415
63416
63417           background.bing = () => {
63418             background.baseLayerSource(background.findSource('Bing'));
63419           };
63420
63421
63422           background.showsLayer = (d) => {
63423             const currSource = baseLayer.source();
63424             if (!d || !currSource) return false;
63425             return d.id === currSource.id || _overlayLayers.some(layer => d.id === layer.source().id);
63426           };
63427
63428
63429           background.overlayLayerSources = () => {
63430             return _overlayLayers.map(layer => layer.source());
63431           };
63432
63433
63434           background.toggleOverlayLayer = (d) => {
63435             let layer;
63436             for (let i = 0; i < _overlayLayers.length; i++) {
63437               layer = _overlayLayers[i];
63438               if (layer.source() === d) {
63439                 _overlayLayers.splice(i, 1);
63440                 dispatch$1.call('change');
63441                 background.updateImagery();
63442                 return;
63443               }
63444             }
63445
63446             layer = rendererTileLayer(context)
63447               .source(d)
63448               .projection(context.projection)
63449               .dimensions(baseLayer.dimensions()
63450             );
63451
63452             _overlayLayers.push(layer);
63453             dispatch$1.call('change');
63454             background.updateImagery();
63455           };
63456
63457
63458           background.nudge = (d, zoom) => {
63459             const currSource = baseLayer.source();
63460             if (currSource) {
63461               currSource.nudge(d, zoom);
63462               dispatch$1.call('change');
63463               background.updateImagery();
63464             }
63465             return background;
63466           };
63467
63468
63469           background.offset = function(d) {
63470             const currSource = baseLayer.source();
63471             if (!arguments.length) {
63472               return (currSource && currSource.offset()) || [0, 0];
63473             }
63474             if (currSource) {
63475               currSource.offset(d);
63476               dispatch$1.call('change');
63477               background.updateImagery();
63478             }
63479             return background;
63480           };
63481
63482
63483           background.brightness = function(d) {
63484             if (!arguments.length) return _brightness;
63485             _brightness = d;
63486             if (context.mode()) dispatch$1.call('change');
63487             return background;
63488           };
63489
63490
63491           background.contrast = function(d) {
63492             if (!arguments.length) return _contrast;
63493             _contrast = d;
63494             if (context.mode()) dispatch$1.call('change');
63495             return background;
63496           };
63497
63498
63499           background.saturation = function(d) {
63500             if (!arguments.length) return _saturation;
63501             _saturation = d;
63502             if (context.mode()) dispatch$1.call('change');
63503             return background;
63504           };
63505
63506
63507           background.sharpness = function(d) {
63508             if (!arguments.length) return _sharpness;
63509             _sharpness = d;
63510             if (context.mode()) dispatch$1.call('change');
63511             return background;
63512           };
63513
63514           let _loadPromise;
63515
63516           background.ensureLoaded = () => {
63517
63518             if (_loadPromise) return _loadPromise;
63519
63520             function parseMapParams(qmap) {
63521               if (!qmap) return false;
63522               const params = qmap.split('/').map(Number);
63523               if (params.length < 3 || params.some(isNaN)) return false;
63524               return geoExtent([params[2], params[1]]);  // lon,lat
63525             }
63526
63527             const hash = utilStringQs(window.location.hash);
63528             const requested = hash.background || hash.layer;
63529             let extent = parseMapParams(hash.map);
63530
63531             return _loadPromise = ensureImageryIndex()
63532               .then(imageryIndex => {
63533                 const first = imageryIndex.backgrounds.length && imageryIndex.backgrounds[0];
63534
63535                 let best;
63536                 if (!requested && extent) {
63537                   best = background.sources(extent).find(s => s.best());
63538                 }
63539
63540                 // Decide which background layer to display
63541                 if (requested && requested.indexOf('custom:') === 0) {
63542                   const template = requested.replace(/^custom:/, '');
63543                   const custom = background.findSource('custom');
63544                   background.baseLayerSource(custom.template(template));
63545                   corePreferences('background-custom-template', template);
63546                 } else {
63547                   background.baseLayerSource(
63548                     background.findSource(requested) ||
63549                     best ||
63550                     background.findSource(corePreferences('background-last-used')) ||
63551                     background.findSource('Bing') ||
63552                     first ||
63553                     background.findSource('none')
63554                   );
63555                 }
63556
63557                 const locator = imageryIndex.backgrounds.find(d => d.overlay && d.default);
63558                 if (locator) {
63559                   background.toggleOverlayLayer(locator);
63560                 }
63561
63562                 const overlays = (hash.overlays || '').split(',');
63563                 overlays.forEach(overlay => {
63564                   overlay = background.findSource(overlay);
63565                   if (overlay) {
63566                     background.toggleOverlayLayer(overlay);
63567                   }
63568                 });
63569
63570                 if (hash.gpx) {
63571                   const gpx = context.layers().layer('data');
63572                   if (gpx) {
63573                     gpx.url(hash.gpx, '.gpx');
63574                   }
63575                 }
63576
63577                 if (hash.offset) {
63578                   const offset = hash.offset
63579                     .replace(/;/g, ',')
63580                     .split(',')
63581                     .map(n => !isNaN(n) && n);
63582
63583                   if (offset.length === 2) {
63584                     background.offset(geoMetersToOffset(offset));
63585                   }
63586                 }
63587               })
63588               .catch(() => { /* ignore */ });
63589           };
63590
63591
63592           return utilRebind(background, dispatch$1, 'on');
63593         }
63594
63595         function rendererFeatures(context) {
63596             var dispatch$1 = dispatch('change', 'redraw');
63597             var features = utilRebind({}, dispatch$1, 'on');
63598             var _deferred = new Set();
63599
63600             var traffic_roads = {
63601                 'motorway': true,
63602                 'motorway_link': true,
63603                 'trunk': true,
63604                 'trunk_link': true,
63605                 'primary': true,
63606                 'primary_link': true,
63607                 'secondary': true,
63608                 'secondary_link': true,
63609                 'tertiary': true,
63610                 'tertiary_link': true,
63611                 'residential': true,
63612                 'unclassified': true,
63613                 'living_street': true
63614             };
63615
63616             var service_roads = {
63617                 'service': true,
63618                 'road': true,
63619                 'track': true
63620             };
63621
63622             var paths = {
63623                 'path': true,
63624                 'footway': true,
63625                 'cycleway': true,
63626                 'bridleway': true,
63627                 'steps': true,
63628                 'pedestrian': true
63629             };
63630
63631             var past_futures = {
63632                 'proposed': true,
63633                 'construction': true,
63634                 'abandoned': true,
63635                 'dismantled': true,
63636                 'disused': true,
63637                 'razed': true,
63638                 'demolished': true,
63639                 'obliterated': true
63640             };
63641
63642             var _cullFactor = 1;
63643             var _cache = {};
63644             var _rules = {};
63645             var _stats = {};
63646             var _keys = [];
63647             var _hidden = [];
63648             var _forceVisible = {};
63649
63650
63651             function update() {
63652                 if (!window.mocha) {
63653                     var hash = utilStringQs(window.location.hash);
63654                     var disabled = features.disabled();
63655                     if (disabled.length) {
63656                         hash.disable_features = disabled.join(',');
63657                     } else {
63658                         delete hash.disable_features;
63659                     }
63660                     window.location.replace('#' + utilQsString(hash, true));
63661                     corePreferences('disabled-features', disabled.join(','));
63662                 }
63663                 _hidden = features.hidden();
63664                 dispatch$1.call('change');
63665                 dispatch$1.call('redraw');
63666             }
63667
63668
63669             function defineRule(k, filter, max) {
63670                 var isEnabled = true;
63671
63672                 _keys.push(k);
63673                 _rules[k] = {
63674                     filter: filter,
63675                     enabled: isEnabled,   // whether the user wants it enabled..
63676                     count: 0,
63677                     currentMax: (max || Infinity),
63678                     defaultMax: (max || Infinity),
63679                     enable: function() { this.enabled = true; this.currentMax = this.defaultMax; },
63680                     disable: function() { this.enabled = false; this.currentMax = 0; },
63681                     hidden: function() {
63682                         return (this.count === 0 && !this.enabled) ||
63683                             this.count > this.currentMax * _cullFactor;
63684                     },
63685                     autoHidden: function() { return this.hidden() && this.currentMax > 0; }
63686                 };
63687             }
63688
63689
63690             defineRule('points', function isPoint(tags, geometry) {
63691                 return geometry === 'point';
63692             }, 200);
63693
63694             defineRule('traffic_roads', function isTrafficRoad(tags) {
63695                 return traffic_roads[tags.highway];
63696             });
63697
63698             defineRule('service_roads', function isServiceRoad(tags) {
63699                 return service_roads[tags.highway];
63700             });
63701
63702             defineRule('paths', function isPath(tags) {
63703                 return paths[tags.highway];
63704             });
63705
63706             defineRule('buildings', function isBuilding(tags) {
63707                 return (
63708                     (!!tags.building && tags.building !== 'no') ||
63709                     tags.parking === 'multi-storey' ||
63710                     tags.parking === 'sheds' ||
63711                     tags.parking === 'carports' ||
63712                     tags.parking === 'garage_boxes'
63713                 );
63714             }, 250);
63715
63716             defineRule('building_parts', function isBuildingPart(tags) {
63717                 return tags['building:part'];
63718             });
63719
63720             defineRule('indoor', function isIndoor(tags) {
63721                 return tags.indoor;
63722             });
63723
63724             defineRule('landuse', function isLanduse(tags, geometry) {
63725                 return geometry === 'area' &&
63726                     !_rules.buildings.filter(tags) &&
63727                     !_rules.building_parts.filter(tags) &&
63728                     !_rules.indoor.filter(tags) &&
63729                     !_rules.water.filter(tags) &&
63730                     !_rules.pistes.filter(tags);
63731             });
63732
63733             defineRule('boundaries', function isBoundary(tags) {
63734                 return (
63735                     !!tags.boundary
63736                 ) && !(
63737                     traffic_roads[tags.highway] ||
63738                     service_roads[tags.highway] ||
63739                     paths[tags.highway] ||
63740                     tags.waterway ||
63741                     tags.railway ||
63742                     tags.landuse ||
63743                     tags.natural ||
63744                     tags.building ||
63745                     tags.power
63746                 );
63747             });
63748
63749             defineRule('water', function isWater(tags) {
63750                 return (
63751                     !!tags.waterway ||
63752                     tags.natural === 'water' ||
63753                     tags.natural === 'coastline' ||
63754                     tags.natural === 'bay' ||
63755                     tags.landuse === 'pond' ||
63756                     tags.landuse === 'basin' ||
63757                     tags.landuse === 'reservoir' ||
63758                     tags.landuse === 'salt_pond'
63759                 );
63760             });
63761
63762             defineRule('rail', function isRail(tags) {
63763                 return (
63764                     !!tags.railway ||
63765                     tags.landuse === 'railway'
63766                 ) && !(
63767                     traffic_roads[tags.highway] ||
63768                     service_roads[tags.highway] ||
63769                     paths[tags.highway]
63770                 );
63771             });
63772
63773             defineRule('pistes', function isPiste(tags) {
63774                 return tags['piste:type'];
63775             });
63776
63777             defineRule('aerialways', function isPiste(tags) {
63778                 return tags.aerialway &&
63779                     tags.aerialway !== 'yes' &&
63780                     tags.aerialway !== 'station';
63781             });
63782
63783             defineRule('power', function isPower(tags) {
63784                 return !!tags.power;
63785             });
63786
63787             // contains a past/future tag, but not in active use as a road/path/cycleway/etc..
63788             defineRule('past_future', function isPastFuture(tags) {
63789                 if (
63790                     traffic_roads[tags.highway] ||
63791                     service_roads[tags.highway] ||
63792                     paths[tags.highway]
63793                 ) { return false; }
63794
63795                 var strings = Object.keys(tags);
63796
63797                 for (var i = 0; i < strings.length; i++) {
63798                     var s = strings[i];
63799                     if (past_futures[s] || past_futures[tags[s]]) { return true; }
63800                 }
63801                 return false;
63802             });
63803
63804             // Lines or areas that don't match another feature filter.
63805             // IMPORTANT: The 'others' feature must be the last one defined,
63806             //   so that code in getMatches can skip this test if `hasMatch = true`
63807             defineRule('others', function isOther(tags, geometry) {
63808                 return (geometry === 'line' || geometry === 'area');
63809             });
63810
63811
63812
63813             features.features = function() {
63814                 return _rules;
63815             };
63816
63817
63818             features.keys = function() {
63819                 return _keys;
63820             };
63821
63822
63823             features.enabled = function(k) {
63824                 if (!arguments.length) {
63825                     return _keys.filter(function(k) { return _rules[k].enabled; });
63826                 }
63827                 return _rules[k] && _rules[k].enabled;
63828             };
63829
63830
63831             features.disabled = function(k) {
63832                 if (!arguments.length) {
63833                     return _keys.filter(function(k) { return !_rules[k].enabled; });
63834                 }
63835                 return _rules[k] && !_rules[k].enabled;
63836             };
63837
63838
63839             features.hidden = function(k) {
63840                 if (!arguments.length) {
63841                     return _keys.filter(function(k) { return _rules[k].hidden(); });
63842                 }
63843                 return _rules[k] && _rules[k].hidden();
63844             };
63845
63846
63847             features.autoHidden = function(k) {
63848                 if (!arguments.length) {
63849                     return _keys.filter(function(k) { return _rules[k].autoHidden(); });
63850                 }
63851                 return _rules[k] && _rules[k].autoHidden();
63852             };
63853
63854
63855             features.enable = function(k) {
63856                 if (_rules[k] && !_rules[k].enabled) {
63857                     _rules[k].enable();
63858                     update();
63859                 }
63860             };
63861
63862             features.enableAll = function() {
63863                 var didEnable = false;
63864                 for (var k in _rules) {
63865                     if (!_rules[k].enabled) {
63866                         didEnable = true;
63867                         _rules[k].enable();
63868                     }
63869                 }
63870                 if (didEnable) update();
63871             };
63872
63873
63874             features.disable = function(k) {
63875                 if (_rules[k] && _rules[k].enabled) {
63876                     _rules[k].disable();
63877                     update();
63878                 }
63879             };
63880
63881             features.disableAll = function() {
63882                 var didDisable = false;
63883                 for (var k in _rules) {
63884                     if (_rules[k].enabled) {
63885                         didDisable = true;
63886                         _rules[k].disable();
63887                     }
63888                 }
63889                 if (didDisable) update();
63890             };
63891
63892
63893             features.toggle = function(k) {
63894                 if (_rules[k]) {
63895                     (function(f) { return f.enabled ? f.disable() : f.enable(); }(_rules[k]));
63896                     update();
63897                 }
63898             };
63899
63900
63901             features.resetStats = function() {
63902                 for (var i = 0; i < _keys.length; i++) {
63903                     _rules[_keys[i]].count = 0;
63904                 }
63905                 dispatch$1.call('change');
63906             };
63907
63908
63909             features.gatherStats = function(d, resolver, dimensions) {
63910                 var needsRedraw = false;
63911                 var types = utilArrayGroupBy(d, 'type');
63912                 var entities = [].concat(types.relation || [], types.way || [], types.node || []);
63913                 var currHidden, geometry, matches, i, j;
63914
63915                 for (i = 0; i < _keys.length; i++) {
63916                     _rules[_keys[i]].count = 0;
63917                 }
63918
63919                 // adjust the threshold for point/building culling based on viewport size..
63920                 // a _cullFactor of 1 corresponds to a 1000x1000px viewport..
63921                 _cullFactor = dimensions[0] * dimensions[1] / 1000000;
63922
63923                 for (i = 0; i < entities.length; i++) {
63924                     geometry = entities[i].geometry(resolver);
63925                     matches = Object.keys(features.getMatches(entities[i], resolver, geometry));
63926                     for (j = 0; j < matches.length; j++) {
63927                         _rules[matches[j]].count++;
63928                     }
63929                 }
63930
63931                 currHidden = features.hidden();
63932                 if (currHidden !== _hidden) {
63933                     _hidden = currHidden;
63934                     needsRedraw = true;
63935                     dispatch$1.call('change');
63936                 }
63937
63938                 return needsRedraw;
63939             };
63940
63941
63942             features.stats = function() {
63943                 for (var i = 0; i < _keys.length; i++) {
63944                     _stats[_keys[i]] = _rules[_keys[i]].count;
63945                 }
63946
63947                 return _stats;
63948             };
63949
63950
63951             features.clear = function(d) {
63952                 for (var i = 0; i < d.length; i++) {
63953                     features.clearEntity(d[i]);
63954                 }
63955             };
63956
63957
63958             features.clearEntity = function(entity) {
63959                 delete _cache[osmEntity.key(entity)];
63960             };
63961
63962
63963             features.reset = function() {
63964                 Array.from(_deferred).forEach(function(handle) {
63965                     window.cancelIdleCallback(handle);
63966                     _deferred.delete(handle);
63967                 });
63968
63969                 _cache = {};
63970             };
63971
63972             // only certain relations are worth checking
63973             function relationShouldBeChecked(relation) {
63974                 // multipolygon features have `area` geometry and aren't checked here
63975                 return relation.tags.type === 'boundary';
63976             }
63977
63978             features.getMatches = function(entity, resolver, geometry) {
63979                 if (geometry === 'vertex' ||
63980                     (geometry === 'relation' && !relationShouldBeChecked(entity))) return {};
63981
63982                 var ent = osmEntity.key(entity);
63983                 if (!_cache[ent]) {
63984                     _cache[ent] = {};
63985                 }
63986
63987                 if (!_cache[ent].matches) {
63988                     var matches = {};
63989                     var hasMatch = false;
63990
63991                     for (var i = 0; i < _keys.length; i++) {
63992                         if (_keys[i] === 'others') {
63993                             if (hasMatch) continue;
63994
63995                             // If an entity...
63996                             //   1. is a way that hasn't matched other 'interesting' feature rules,
63997                             if (entity.type === 'way') {
63998                                 var parents = features.getParents(entity, resolver, geometry);
63999
64000                                 //   2a. belongs only to a single multipolygon relation
64001                                 if ((parents.length === 1 && parents[0].isMultipolygon()) ||
64002                                     // 2b. or belongs only to boundary relations
64003                                     (parents.length > 0 && parents.every(function(parent) { return parent.tags.type === 'boundary'; }))) {
64004
64005                                     // ...then match whatever feature rules the parent relation has matched.
64006                                     // see #2548, #2887
64007                                     //
64008                                     // IMPORTANT:
64009                                     // For this to work, getMatches must be called on relations before ways.
64010                                     //
64011                                     var pkey = osmEntity.key(parents[0]);
64012                                     if (_cache[pkey] && _cache[pkey].matches) {
64013                                         matches = Object.assign({}, _cache[pkey].matches);  // shallow copy
64014                                         continue;
64015                                     }
64016                                 }
64017                             }
64018                         }
64019
64020                         if (_rules[_keys[i]].filter(entity.tags, geometry)) {
64021                             matches[_keys[i]] = hasMatch = true;
64022                         }
64023                     }
64024                     _cache[ent].matches = matches;
64025                 }
64026
64027                 return _cache[ent].matches;
64028             };
64029
64030
64031             features.getParents = function(entity, resolver, geometry) {
64032                 if (geometry === 'point') return [];
64033
64034                 var ent = osmEntity.key(entity);
64035                 if (!_cache[ent]) {
64036                     _cache[ent] = {};
64037                 }
64038
64039                 if (!_cache[ent].parents) {
64040                     var parents = [];
64041                     if (geometry === 'vertex') {
64042                         parents = resolver.parentWays(entity);
64043                     } else {   // 'line', 'area', 'relation'
64044                         parents = resolver.parentRelations(entity);
64045                     }
64046                     _cache[ent].parents = parents;
64047                 }
64048                 return _cache[ent].parents;
64049             };
64050
64051
64052             features.isHiddenPreset = function(preset, geometry) {
64053                 if (!_hidden.length) return false;
64054                 if (!preset.tags) return false;
64055
64056                 var test = preset.setTags({}, geometry);
64057                 for (var key in _rules) {
64058                     if (_rules[key].filter(test, geometry)) {
64059                         if (_hidden.indexOf(key) !== -1) {
64060                             return key;
64061                         }
64062                         return false;
64063                     }
64064                 }
64065                 return false;
64066             };
64067
64068
64069             features.isHiddenFeature = function(entity, resolver, geometry) {
64070                 if (!_hidden.length) return false;
64071                 if (!entity.version) return false;
64072                 if (_forceVisible[entity.id]) return false;
64073
64074                 var matches = Object.keys(features.getMatches(entity, resolver, geometry));
64075                 return matches.length && matches.every(function(k) { return features.hidden(k); });
64076             };
64077
64078
64079             features.isHiddenChild = function(entity, resolver, geometry) {
64080                 if (!_hidden.length) return false;
64081                 if (!entity.version || geometry === 'point') return false;
64082                 if (_forceVisible[entity.id]) return false;
64083
64084                 var parents = features.getParents(entity, resolver, geometry);
64085                 if (!parents.length) return false;
64086
64087                 for (var i = 0; i < parents.length; i++) {
64088                     if (!features.isHidden(parents[i], resolver, parents[i].geometry(resolver))) {
64089                         return false;
64090                     }
64091                 }
64092                 return true;
64093             };
64094
64095
64096             features.hasHiddenConnections = function(entity, resolver) {
64097                 if (!_hidden.length) return false;
64098
64099                 var childNodes, connections;
64100                 if (entity.type === 'midpoint') {
64101                     childNodes = [resolver.entity(entity.edge[0]), resolver.entity(entity.edge[1])];
64102                     connections = [];
64103                 } else {
64104                     childNodes = entity.nodes ? resolver.childNodes(entity) : [];
64105                     connections = features.getParents(entity, resolver, entity.geometry(resolver));
64106                 }
64107
64108                 // gather ways connected to child nodes..
64109                 connections = childNodes.reduce(function(result, e) {
64110                     return resolver.isShared(e) ? utilArrayUnion(result, resolver.parentWays(e)) : result;
64111                 }, connections);
64112
64113                 return connections.some(function(e) {
64114                     return features.isHidden(e, resolver, e.geometry(resolver));
64115                 });
64116             };
64117
64118
64119             features.isHidden = function(entity, resolver, geometry) {
64120                 if (!_hidden.length) return false;
64121                 if (!entity.version) return false;
64122
64123                 var fn = (geometry === 'vertex' ? features.isHiddenChild : features.isHiddenFeature);
64124                 return fn(entity, resolver, geometry);
64125             };
64126
64127
64128             features.filter = function(d, resolver) {
64129                 if (!_hidden.length) return d;
64130
64131                 var result = [];
64132                 for (var i = 0; i < d.length; i++) {
64133                     var entity = d[i];
64134                     if (!features.isHidden(entity, resolver, entity.geometry(resolver))) {
64135                         result.push(entity);
64136                     }
64137                 }
64138                 return result;
64139             };
64140
64141
64142             features.forceVisible = function(entityIDs) {
64143                 if (!arguments.length) return Object.keys(_forceVisible);
64144
64145                 _forceVisible = {};
64146                 for (var i = 0; i < entityIDs.length; i++) {
64147                     _forceVisible[entityIDs[i]] = true;
64148                     var entity = context.hasEntity(entityIDs[i]);
64149                     if (entity && entity.type === 'relation') {
64150                         // also show relation members (one level deep)
64151                         for (var j in entity.members) {
64152                             _forceVisible[entity.members[j].id] = true;
64153                         }
64154                     }
64155                 }
64156                 return features;
64157             };
64158
64159
64160             features.init = function() {
64161                 var storage = corePreferences('disabled-features');
64162                 if (storage) {
64163                     var storageDisabled = storage.replace(/;/g, ',').split(',');
64164                     storageDisabled.forEach(features.disable);
64165                 }
64166
64167                 var hash = utilStringQs(window.location.hash);
64168                 if (hash.disable_features) {
64169                     var hashDisabled = hash.disable_features.replace(/;/g, ',').split(',');
64170                     hashDisabled.forEach(features.disable);
64171                 }
64172             };
64173
64174
64175             // warm up the feature matching cache upon merging fetched data
64176             context.history().on('merge.features', function(newEntities) {
64177                 if (!newEntities) return;
64178                 var handle = window.requestIdleCallback(function() {
64179                     var graph = context.graph();
64180                     var types = utilArrayGroupBy(newEntities, 'type');
64181                     // ensure that getMatches is called on relations before ways
64182                     var entities = [].concat(types.relation || [], types.way || [], types.node || []);
64183                     for (var i = 0; i < entities.length; i++) {
64184                         var geometry = entities[i].geometry(graph);
64185                         features.getMatches(entities[i], graph, geometry);
64186                     }
64187                 });
64188                 _deferred.add(handle);
64189             });
64190
64191
64192             return features;
64193         }
64194
64195         // Touch targets control which other vertices we can drag a vertex onto.
64196         //
64197         // - the activeID - nope
64198         // - 1 away (adjacent) to the activeID - yes (vertices will be merged)
64199         // - 2 away from the activeID - nope (would create a self intersecting segment)
64200         // - all others on a linear way - yes
64201         // - all others on a closed way - nope (would create a self intersecting polygon)
64202         //
64203         // returns
64204         // 0 = active vertex - no touch/connect
64205         // 1 = passive vertex - yes touch/connect
64206         // 2 = adjacent vertex - yes but pay attention segmenting a line here
64207         //
64208         function svgPassiveVertex(node, graph, activeID) {
64209             if (!activeID) return 1;
64210             if (activeID === node.id) return 0;
64211
64212             var parents = graph.parentWays(node);
64213
64214             var i, j, nodes, isClosed, ix1, ix2, ix3, ix4, max;
64215
64216             for (i = 0; i < parents.length; i++) {
64217                 nodes = parents[i].nodes;
64218                 isClosed = parents[i].isClosed();
64219                 for (j = 0; j < nodes.length; j++) {   // find this vertex, look nearby
64220                     if (nodes[j] === node.id) {
64221                         ix1 = j - 2;
64222                         ix2 = j - 1;
64223                         ix3 = j + 1;
64224                         ix4 = j + 2;
64225
64226                         if (isClosed) {  // wraparound if needed
64227                             max = nodes.length - 1;
64228                             if (ix1 < 0)   ix1 = max + ix1;
64229                             if (ix2 < 0)   ix2 = max + ix2;
64230                             if (ix3 > max) ix3 = ix3 - max;
64231                             if (ix4 > max) ix4 = ix4 - max;
64232                         }
64233
64234                         if (nodes[ix1] === activeID) return 0;        // no - prevent self intersect
64235                         else if (nodes[ix2] === activeID) return 2;   // ok - adjacent
64236                         else if (nodes[ix3] === activeID) return 2;   // ok - adjacent
64237                         else if (nodes[ix4] === activeID) return 0;   // no - prevent self intersect
64238                         else if (isClosed && nodes.indexOf(activeID) !== -1) return 0;  // no - prevent self intersect
64239                     }
64240                 }
64241             }
64242
64243             return 1;   // ok
64244         }
64245
64246
64247         function svgMarkerSegments(projection, graph, dt,
64248                                           shouldReverse,
64249                                           bothDirections) {
64250             return function(entity) {
64251                 var i = 0;
64252                 var offset = dt;
64253                 var segments = [];
64254                 var clip = d3_geoIdentity().clipExtent(projection.clipExtent()).stream;
64255                 var coordinates = graph.childNodes(entity).map(function(n) { return n.loc; });
64256                 var a, b;
64257
64258                 if (shouldReverse(entity)) {
64259                     coordinates.reverse();
64260                 }
64261
64262                 d3_geoStream({
64263                     type: 'LineString',
64264                     coordinates: coordinates
64265                 }, projection.stream(clip({
64266                     lineStart: function() {},
64267                     lineEnd: function() { a = null; },
64268                     point: function(x, y) {
64269                         b = [x, y];
64270
64271                         if (a) {
64272                             var span = geoVecLength(a, b) - offset;
64273
64274                             if (span >= 0) {
64275                                 var heading = geoVecAngle(a, b);
64276                                 var dx = dt * Math.cos(heading);
64277                                 var dy = dt * Math.sin(heading);
64278                                 var p = [
64279                                     a[0] + offset * Math.cos(heading),
64280                                     a[1] + offset * Math.sin(heading)
64281                                 ];
64282
64283                                 // gather coordinates
64284                                 var coord = [a, p];
64285                                 for (span -= dt; span >= 0; span -= dt) {
64286                                     p = geoVecAdd(p, [dx, dy]);
64287                                     coord.push(p);
64288                                 }
64289                                 coord.push(b);
64290
64291                                 // generate svg paths
64292                                 var segment = '';
64293                                 var j;
64294
64295                                 for (j = 0; j < coord.length; j++) {
64296                                     segment += (j === 0 ? 'M' : 'L') + coord[j][0] + ',' + coord[j][1];
64297                                 }
64298                                 segments.push({ id: entity.id, index: i++, d: segment });
64299
64300                                 if (bothDirections(entity)) {
64301                                     segment = '';
64302                                     for (j = coord.length - 1; j >= 0; j--) {
64303                                         segment += (j === coord.length - 1 ? 'M' : 'L') + coord[j][0] + ',' + coord[j][1];
64304                                     }
64305                                     segments.push({ id: entity.id, index: i++, d: segment });
64306                                 }
64307                             }
64308
64309                             offset = -span;
64310                         }
64311
64312                         a = b;
64313                     }
64314                 })));
64315
64316                 return segments;
64317             };
64318         }
64319
64320
64321         function svgPath(projection, graph, isArea) {
64322
64323             // Explanation of magic numbers:
64324             // "padding" here allows space for strokes to extend beyond the viewport,
64325             // so that the stroke isn't drawn along the edge of the viewport when
64326             // the shape is clipped.
64327             //
64328             // When drawing lines, pad viewport by 5px.
64329             // When drawing areas, pad viewport by 65px in each direction to allow
64330             // for 60px area fill stroke (see ".fill-partial path.fill" css rule)
64331
64332             var cache = {};
64333             var padding = isArea ? 65 : 5;
64334             var viewport = projection.clipExtent();
64335             var paddedExtent = [
64336                 [viewport[0][0] - padding, viewport[0][1] - padding],
64337                 [viewport[1][0] + padding, viewport[1][1] + padding]
64338             ];
64339             var clip = d3_geoIdentity().clipExtent(paddedExtent).stream;
64340             var project = projection.stream;
64341             var path = d3_geoPath()
64342                 .projection({stream: function(output) { return project(clip(output)); }});
64343
64344             var svgpath = function(entity) {
64345                 if (entity.id in cache) {
64346                     return cache[entity.id];
64347                 } else {
64348                     return cache[entity.id] = path(entity.asGeoJSON(graph));
64349                 }
64350             };
64351
64352             svgpath.geojson = function(d) {
64353                 if (d.__featurehash__ !== undefined) {
64354                     if (d.__featurehash__ in cache) {
64355                         return cache[d.__featurehash__];
64356                     } else {
64357                         return cache[d.__featurehash__] = path(d);
64358                     }
64359                 } else {
64360                     return path(d);
64361                 }
64362             };
64363
64364             return svgpath;
64365         }
64366
64367
64368         function svgPointTransform(projection) {
64369             var svgpoint = function(entity) {
64370                 // http://jsperf.com/short-array-join
64371                 var pt = projection(entity.loc);
64372                 return 'translate(' + pt[0] + ',' + pt[1] + ')';
64373             };
64374
64375             svgpoint.geojson = function(d) {
64376                 return svgpoint(d.properties.entity);
64377             };
64378
64379             return svgpoint;
64380         }
64381
64382
64383         function svgRelationMemberTags(graph) {
64384             return function(entity) {
64385                 var tags = entity.tags;
64386                 var shouldCopyMultipolygonTags = !entity.hasInterestingTags();
64387                 graph.parentRelations(entity).forEach(function(relation) {
64388                     var type = relation.tags.type;
64389                     if ((type === 'multipolygon' && shouldCopyMultipolygonTags) || type === 'boundary') {
64390                         tags = Object.assign({}, relation.tags, tags);
64391                     }
64392                 });
64393                 return tags;
64394             };
64395         }
64396
64397
64398         function svgSegmentWay(way, graph, activeID) {
64399             // When there is no activeID, we can memoize this expensive computation
64400             if (activeID === undefined) {
64401                 return graph.transient(way, 'waySegments', getWaySegments);
64402             } else {
64403                 return getWaySegments();
64404             }
64405
64406             function getWaySegments() {
64407                 var isActiveWay = (way.nodes.indexOf(activeID) !== -1);
64408                 var features = { passive: [], active: [] };
64409                 var start = {};
64410                 var end = {};
64411                 var node, type;
64412
64413                 for (var i = 0; i < way.nodes.length; i++) {
64414                     node = graph.entity(way.nodes[i]);
64415                     type = svgPassiveVertex(node, graph, activeID);
64416                     end = { node: node, type: type };
64417
64418                     if (start.type !== undefined) {
64419                         if (start.node.id === activeID || end.node.id === activeID) ; else if (isActiveWay && (start.type === 2 || end.type === 2)) {   // one adjacent vertex
64420                             pushActive(start, end, i);
64421                         } else if (start.type === 0 && end.type === 0) {   // both active vertices
64422                             pushActive(start, end, i);
64423                         } else {
64424                             pushPassive(start, end, i);
64425                         }
64426                     }
64427
64428                     start = end;
64429                 }
64430
64431                 return features;
64432
64433                 function pushActive(start, end, index) {
64434                     features.active.push({
64435                         type: 'Feature',
64436                         id: way.id + '-' + index + '-nope',
64437                         properties: {
64438                             nope: true,
64439                             target: true,
64440                             entity: way,
64441                             nodes: [start.node, end.node],
64442                             index: index
64443                         },
64444                         geometry: {
64445                             type: 'LineString',
64446                             coordinates: [start.node.loc, end.node.loc]
64447                         }
64448                     });
64449                 }
64450
64451                 function pushPassive(start, end, index) {
64452                     features.passive.push({
64453                         type: 'Feature',
64454                         id: way.id + '-' + index,
64455                         properties: {
64456                             target: true,
64457                             entity: way,
64458                             nodes: [start.node, end.node],
64459                             index: index
64460                         },
64461                         geometry: {
64462                             type: 'LineString',
64463                             coordinates: [start.node.loc, end.node.loc]
64464                         }
64465                     });
64466                 }
64467             }
64468         }
64469
64470         function svgTagClasses() {
64471             var primaries = [
64472                 'building', 'highway', 'railway', 'waterway', 'aeroway', 'aerialway',
64473                 'piste:type', 'boundary', 'power', 'amenity', 'natural', 'landuse',
64474                 'leisure', 'military', 'place', 'man_made', 'route', 'attraction',
64475                 'building:part', 'indoor'
64476             ];
64477             var statuses = [
64478                 // nonexistent, might be built
64479                 'proposed', 'planned',
64480                 // under maintentance or between groundbreaking and opening
64481                 'construction',
64482                 // existent but not functional
64483                 'disused',
64484                 // dilapidated to nonexistent
64485                 'abandoned',
64486                 // nonexistent, still may appear in imagery
64487                 'dismantled', 'razed', 'demolished', 'obliterated',
64488                 // existent occasionally, e.g. stormwater drainage basin
64489                 'intermittent'
64490             ];
64491             var secondaries = [
64492                 'oneway', 'bridge', 'tunnel', 'embankment', 'cutting', 'barrier',
64493                 'surface', 'tracktype', 'footway', 'crossing', 'service', 'sport',
64494                 'public_transport', 'location', 'parking', 'golf', 'type', 'leisure',
64495                 'man_made', 'indoor'
64496             ];
64497             var _tags = function(entity) { return entity.tags; };
64498
64499
64500             var tagClasses = function(selection) {
64501                 selection.each(function tagClassesEach(entity) {
64502                     var value = this.className;
64503
64504                     if (value.baseVal !== undefined) {
64505                         value = value.baseVal;
64506                     }
64507
64508                     var t = _tags(entity);
64509
64510                     var computed = tagClasses.getClassesString(t, value);
64511
64512                     if (computed !== value) {
64513                         select(this).attr('class', computed);
64514                     }
64515                 });
64516             };
64517
64518
64519             tagClasses.getClassesString = function(t, value) {
64520                 var primary, status;
64521                 var i, j, k, v;
64522
64523                 // in some situations we want to render perimeter strokes a certain way
64524                 var overrideGeometry;
64525                 if (/\bstroke\b/.test(value)) {
64526                     if (!!t.barrier && t.barrier !== 'no') {
64527                         overrideGeometry = 'line';
64528                     }
64529                 }
64530
64531                 // preserve base classes (nothing with `tag-`)
64532                 var classes = value.trim().split(/\s+/)
64533                     .filter(function(klass) {
64534                         return klass.length && !/^tag-/.test(klass);
64535                     })
64536                     .map(function(klass) {  // special overrides for some perimeter strokes
64537                         return (klass === 'line' || klass === 'area') ? (overrideGeometry || klass) : klass;
64538                     });
64539
64540                 // pick at most one primary classification tag..
64541                 for (i = 0; i < primaries.length; i++) {
64542                     k = primaries[i];
64543                     v = t[k];
64544                     if (!v || v === 'no') continue;
64545
64546                     if (k === 'piste:type') {  // avoid a ':' in the class name
64547                         k = 'piste';
64548                     } else if (k === 'building:part') {  // avoid a ':' in the class name
64549                         k = 'building_part';
64550                     }
64551
64552                     primary = k;
64553                     if (statuses.indexOf(v) !== -1) {   // e.g. `railway=abandoned`
64554                         status = v;
64555                         classes.push('tag-' + k);
64556                     } else {
64557                         classes.push('tag-' + k);
64558                         classes.push('tag-' + k + '-' + v);
64559                     }
64560
64561                     break;
64562                 }
64563
64564                 if (!primary) {
64565                     for (i = 0; i < statuses.length; i++) {
64566                         for (j = 0; j < primaries.length; j++) {
64567                             k = statuses[i] + ':' + primaries[j];  // e.g. `demolished:building=yes`
64568                             v = t[k];
64569                             if (!v || v === 'no') continue;
64570
64571                             status = statuses[i];
64572                             break;
64573                         }
64574                     }
64575                 }
64576
64577                 // add at most one status tag, only if relates to primary tag..
64578                 if (!status) {
64579                     for (i = 0; i < statuses.length; i++) {
64580                         k = statuses[i];
64581                         v = t[k];
64582                         if (!v || v === 'no') continue;
64583
64584                         if (v === 'yes') {   // e.g. `railway=rail + abandoned=yes`
64585                             status = k;
64586                         }
64587                         else if (primary && primary === v) {  // e.g. `railway=rail + abandoned=railway`
64588                             status = k;
64589                         } else if (!primary && primaries.indexOf(v) !== -1) {  // e.g. `abandoned=railway`
64590                             status = k;
64591                             primary = v;
64592                             classes.push('tag-' + v);
64593                         }  // else ignore e.g.  `highway=path + abandoned=railway`
64594
64595                         if (status) break;
64596                     }
64597                 }
64598
64599                 if (status) {
64600                     classes.push('tag-status');
64601                     classes.push('tag-status-' + status);
64602                 }
64603
64604                 // add any secondary tags
64605                 for (i = 0; i < secondaries.length; i++) {
64606                     k = secondaries[i];
64607                     v = t[k];
64608                     if (!v || v === 'no' || k === primary) continue;
64609                     classes.push('tag-' + k);
64610                     classes.push('tag-' + k + '-' + v);
64611                 }
64612
64613                 // For highways, look for surface tagging..
64614                 if ((primary === 'highway' && !osmPathHighwayTagValues[t.highway]) || primary === 'aeroway') {
64615                     var surface = t.highway === 'track' ? 'unpaved' : 'paved';
64616                     for (k in t) {
64617                         v = t[k];
64618                         if (k in osmPavedTags) {
64619                             surface = osmPavedTags[k][v] ? 'paved' : 'unpaved';
64620                         }
64621                         if (k in osmSemipavedTags && !!osmSemipavedTags[k][v]) {
64622                             surface = 'semipaved';
64623                         }
64624                     }
64625                     classes.push('tag-' + surface);
64626                 }
64627
64628                 // If this is a wikidata-tagged item, add a class for that..
64629                 if (t.wikidata || t['brand:wikidata']) {
64630                     classes.push('tag-wikidata');
64631                 }
64632
64633                 return classes.join(' ').trim();
64634             };
64635
64636
64637             tagClasses.tags = function(val) {
64638                 if (!arguments.length) return _tags;
64639                 _tags = val;
64640                 return tagClasses;
64641             };
64642
64643             return tagClasses;
64644         }
64645
64646         // Patterns only work in Firefox when set directly on element.
64647         // (This is not a bug: https://bugzilla.mozilla.org/show_bug.cgi?id=750632)
64648         var patterns = {
64649             // tag - pattern name
64650             // -or-
64651             // tag - value - pattern name
64652             // -or-
64653             // tag - value - rules (optional tag-values, pattern name)
64654             // (matches earlier rules first, so fallback should be last entry)
64655             amenity: {
64656                 grave_yard: 'cemetery',
64657                 fountain: 'water_standing'
64658             },
64659             landuse: {
64660                 cemetery: [
64661                     { religion: 'christian', pattern: 'cemetery_christian' },
64662                     { religion: 'buddhist', pattern: 'cemetery_buddhist' },
64663                     { religion: 'muslim', pattern: 'cemetery_muslim' },
64664                     { religion: 'jewish', pattern: 'cemetery_jewish' },
64665                     { pattern: 'cemetery' }
64666                 ],
64667                 construction: 'construction',
64668                 farmland: 'farmland',
64669                 farmyard: 'farmyard',
64670                 forest: [
64671                     { leaf_type: 'broadleaved', pattern: 'forest_broadleaved' },
64672                     { leaf_type: 'needleleaved', pattern: 'forest_needleleaved' },
64673                     { leaf_type: 'leafless', pattern: 'forest_leafless' },
64674                     { pattern: 'forest' } // same as 'leaf_type:mixed'
64675                 ],
64676                 grave_yard: 'cemetery',
64677                 grass: [
64678                     { golf: 'green', pattern: 'golf_green' },
64679                     { pattern: 'grass' },
64680                 ],
64681                 landfill: 'landfill',
64682                 meadow: 'meadow',
64683                 military: 'construction',
64684                 orchard: 'orchard',
64685                 quarry: 'quarry',
64686                 vineyard: 'vineyard'
64687             },
64688             natural: {
64689                 beach: 'beach',
64690                 grassland: 'grass',
64691                 sand: 'beach',
64692                 scrub: 'scrub',
64693                 water: [
64694                     { water: 'pond', pattern: 'pond' },
64695                     { water: 'reservoir', pattern: 'water_standing' },
64696                     { pattern: 'waves' }
64697                 ],
64698                 wetland: [
64699                     { wetland: 'marsh', pattern: 'wetland_marsh' },
64700                     { wetland: 'swamp', pattern: 'wetland_swamp' },
64701                     { wetland: 'bog', pattern: 'wetland_bog' },
64702                     { wetland: 'reedbed', pattern: 'wetland_reedbed' },
64703                     { pattern: 'wetland' }
64704                 ],
64705                 wood: [
64706                     { leaf_type: 'broadleaved', pattern: 'forest_broadleaved' },
64707                     { leaf_type: 'needleleaved', pattern: 'forest_needleleaved' },
64708                     { leaf_type: 'leafless', pattern: 'forest_leafless' },
64709                     { pattern: 'forest' } // same as 'leaf_type:mixed'
64710                 ]
64711             },
64712             traffic_calming: {
64713                 island: [
64714                     { surface: 'grass', pattern: 'grass' },
64715                 ],
64716                 chicane: [
64717                     { surface: 'grass', pattern: 'grass' },
64718                 ],
64719                 choker: [
64720                     { surface: 'grass', pattern: 'grass' },
64721                 ]
64722             }
64723         };
64724
64725         function svgTagPattern(tags) {
64726             // Skip pattern filling if this is a building (buildings don't get patterns applied)
64727             if (tags.building && tags.building !== 'no') {
64728                 return null;
64729             }
64730
64731             for (var tag in patterns) {
64732                 var entityValue = tags[tag];
64733                 if (!entityValue) continue;
64734
64735                 if (typeof patterns[tag] === 'string') { // extra short syntax (just tag) - pattern name
64736                     return 'pattern-' + patterns[tag];
64737                 } else {
64738                     var values = patterns[tag];
64739                     for (var value in values) {
64740                         if (entityValue !== value) continue;
64741
64742                         var rules = values[value];
64743                         if (typeof rules === 'string') { // short syntax - pattern name
64744                             return 'pattern-' + rules;
64745                         }
64746
64747                         // long syntax - rule array
64748                         for (var ruleKey in rules) {
64749                             var rule = rules[ruleKey];
64750
64751                             var pass = true;
64752                             for (var criterion in rule) {
64753                                 if (criterion !== 'pattern') { // reserved for pattern name
64754                                     // The only rule is a required tag-value pair
64755                                     var v = tags[criterion];
64756                                     if (!v || v !== rule[criterion]) {
64757                                         pass = false;
64758                                         break;
64759                                     }
64760                                 }
64761                             }
64762
64763                             if (pass) {
64764                                 return 'pattern-' + rule.pattern;
64765                             }
64766                         }
64767                     }
64768                 }
64769             }
64770
64771             return null;
64772         }
64773
64774         function svgAreas(projection, context) {
64775
64776
64777             function getPatternStyle(tags) {
64778                 var imageID = svgTagPattern(tags);
64779                 if (imageID) {
64780                     return 'url("#ideditor-' + imageID + '")';
64781                 }
64782                 return '';
64783             }
64784
64785
64786             function drawTargets(selection, graph, entities, filter) {
64787                 var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
64788                 var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
64789                 var getPath = svgPath(projection).geojson;
64790                 var activeID = context.activeID();
64791                 var base = context.history().base();
64792
64793                 // The targets and nopes will be MultiLineString sub-segments of the ways
64794                 var data = { targets: [], nopes: [] };
64795
64796                 entities.forEach(function(way) {
64797                     var features = svgSegmentWay(way, graph, activeID);
64798                     data.targets.push.apply(data.targets, features.passive);
64799                     data.nopes.push.apply(data.nopes, features.active);
64800                 });
64801
64802
64803                 // Targets allow hover and vertex snapping
64804                 var targetData = data.targets.filter(getPath);
64805                 var targets = selection.selectAll('.area.target-allowed')
64806                     .filter(function(d) { return filter(d.properties.entity); })
64807                     .data(targetData, function key(d) { return d.id; });
64808
64809                 // exit
64810                 targets.exit()
64811                     .remove();
64812
64813                 var segmentWasEdited = function(d) {
64814                     var wayID = d.properties.entity.id;
64815                     // if the whole line was edited, don't draw segment changes
64816                     if (!base.entities[wayID] ||
64817                         !fastDeepEqual(graph.entities[wayID].nodes, base.entities[wayID].nodes)) {
64818                         return false;
64819                     }
64820                     return d.properties.nodes.some(function(n) {
64821                         return !base.entities[n.id] ||
64822                                !fastDeepEqual(graph.entities[n.id].loc, base.entities[n.id].loc);
64823                     });
64824                 };
64825
64826                 // enter/update
64827                 targets.enter()
64828                     .append('path')
64829                     .merge(targets)
64830                     .attr('d', getPath)
64831                     .attr('class', function(d) { return 'way area target target-allowed ' + targetClass + d.id; })
64832                     .classed('segment-edited', segmentWasEdited);
64833
64834
64835                 // NOPE
64836                 var nopeData = data.nopes.filter(getPath);
64837                 var nopes = selection.selectAll('.area.target-nope')
64838                     .filter(function(d) { return filter(d.properties.entity); })
64839                     .data(nopeData, function key(d) { return d.id; });
64840
64841                 // exit
64842                 nopes.exit()
64843                     .remove();
64844
64845                 // enter/update
64846                 nopes.enter()
64847                     .append('path')
64848                     .merge(nopes)
64849                     .attr('d', getPath)
64850                     .attr('class', function(d) { return 'way area target target-nope ' + nopeClass + d.id; })
64851                     .classed('segment-edited', segmentWasEdited);
64852             }
64853
64854
64855             function drawAreas(selection, graph, entities, filter) {
64856                 var path = svgPath(projection, graph, true);
64857                 var areas = {};
64858                 var multipolygon;
64859                 var base = context.history().base();
64860
64861                 for (var i = 0; i < entities.length; i++) {
64862                     var entity = entities[i];
64863                     if (entity.geometry(graph) !== 'area') continue;
64864
64865                     multipolygon = osmIsOldMultipolygonOuterMember(entity, graph);
64866                     if (multipolygon) {
64867                         areas[multipolygon.id] = {
64868                             entity: multipolygon.mergeTags(entity.tags),
64869                             area: Math.abs(entity.area(graph))
64870                         };
64871                     } else if (!areas[entity.id]) {
64872                         areas[entity.id] = {
64873                             entity: entity,
64874                             area: Math.abs(entity.area(graph))
64875                         };
64876                     }
64877                 }
64878
64879                 var fills = Object.values(areas).filter(function hasPath(a) { return path(a.entity); });
64880                 fills.sort(function areaSort(a, b) { return b.area - a.area; });
64881                 fills = fills.map(function(a) { return a.entity; });
64882
64883                 var strokes = fills.filter(function(area) { return area.type === 'way'; });
64884
64885                 var data = {
64886                     clip: fills,
64887                     shadow: strokes,
64888                     stroke: strokes,
64889                     fill: fills
64890                 };
64891
64892                 var clipPaths = context.surface().selectAll('defs').selectAll('.clipPath-osm')
64893                    .filter(filter)
64894                    .data(data.clip, osmEntity.key);
64895
64896                 clipPaths.exit()
64897                    .remove();
64898
64899                 var clipPathsEnter = clipPaths.enter()
64900                    .append('clipPath')
64901                    .attr('class', 'clipPath-osm')
64902                    .attr('id', function(entity) { return 'ideditor-' + entity.id + '-clippath'; });
64903
64904                 clipPathsEnter
64905                    .append('path');
64906
64907                 clipPaths.merge(clipPathsEnter)
64908                    .selectAll('path')
64909                    .attr('d', path);
64910
64911
64912                 var drawLayer = selection.selectAll('.layer-osm.areas');
64913                 var touchLayer = selection.selectAll('.layer-touch.areas');
64914
64915                 // Draw areas..
64916                 var areagroup = drawLayer
64917                     .selectAll('g.areagroup')
64918                     .data(['fill', 'shadow', 'stroke']);
64919
64920                 areagroup = areagroup.enter()
64921                     .append('g')
64922                     .attr('class', function(d) { return 'areagroup area-' + d; })
64923                     .merge(areagroup);
64924
64925                 var paths = areagroup
64926                     .selectAll('path')
64927                     .filter(filter)
64928                     .data(function(layer) { return data[layer]; }, osmEntity.key);
64929
64930                 paths.exit()
64931                     .remove();
64932
64933
64934                 var fillpaths = selection.selectAll('.area-fill path.area').nodes();
64935                 var bisect = d3_bisector(function(node) { return -node.__data__.area(graph); }).left;
64936
64937                 function sortedByArea(entity) {
64938                     if (this._parent.__data__ === 'fill') {
64939                         return fillpaths[bisect(fillpaths, -entity.area(graph))];
64940                     }
64941                 }
64942
64943                 paths = paths.enter()
64944                     .insert('path', sortedByArea)
64945                     .merge(paths)
64946                     .each(function(entity) {
64947                         var layer = this.parentNode.__data__;
64948                         this.setAttribute('class', entity.type + ' area ' + layer + ' ' + entity.id);
64949
64950                         if (layer === 'fill') {
64951                             this.setAttribute('clip-path', 'url(#ideditor-' + entity.id + '-clippath)');
64952                             this.style.fill = this.style.stroke = getPatternStyle(entity.tags);
64953                         }
64954                     })
64955                     .classed('added', function(d) {
64956                         return !base.entities[d.id];
64957                     })
64958                     .classed('geometry-edited', function(d) {
64959                         return graph.entities[d.id] &&
64960                             base.entities[d.id] &&
64961                             !fastDeepEqual(graph.entities[d.id].nodes, base.entities[d.id].nodes);
64962                     })
64963                     .classed('retagged', function(d) {
64964                         return graph.entities[d.id] &&
64965                             base.entities[d.id] &&
64966                             !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
64967                     })
64968                     .call(svgTagClasses())
64969                     .attr('d', path);
64970
64971
64972                 // Draw touch targets..
64973                 touchLayer
64974                     .call(drawTargets, graph, data.stroke, filter);
64975             }
64976
64977             return drawAreas;
64978         }
64979
64980         //[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]
64981         //[4a]          NameChar           ::=          NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]
64982         //[5]           Name       ::=          NameStartChar (NameChar)*
64983         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
64984         var nameChar = new RegExp("[\\-\\.0-9"+nameStartChar.source.slice(1,-1)+"\\u00B7\\u0300-\\u036F\\u203F-\\u2040]");
64985         var tagNamePattern = new RegExp('^'+nameStartChar.source+nameChar.source+'*(?:\:'+nameStartChar.source+nameChar.source+'*)?$');
64986         //var tagNamePattern = /^[a-zA-Z_][\w\-\.]*(?:\:[a-zA-Z_][\w\-\.]*)?$/
64987         //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(',')
64988
64989         //S_TAG,        S_ATTR, S_EQ,   S_ATTR_NOQUOT_VALUE
64990         //S_ATTR_SPACE, S_ATTR_END,     S_TAG_SPACE, S_TAG_CLOSE
64991         var S_TAG = 0;//tag name offerring
64992         var S_ATTR = 1;//attr name offerring 
64993         var S_ATTR_SPACE=2;//attr name end and space offer
64994         var S_EQ = 3;//=space?
64995         var S_ATTR_NOQUOT_VALUE = 4;//attr value(no quot value only)
64996         var S_ATTR_END = 5;//attr value end and no space(quot end)
64997         var S_TAG_SPACE = 6;//(attr value end || tag end ) && (space offer)
64998         var S_TAG_CLOSE = 7;//closed el<el />
64999
65000         function XMLReader(){
65001                 
65002         }
65003
65004         XMLReader.prototype = {
65005                 parse:function(source,defaultNSMap,entityMap){
65006                         var domBuilder = this.domBuilder;
65007                         domBuilder.startDocument();
65008                         _copy(defaultNSMap ,defaultNSMap = {});
65009                         parse(source,defaultNSMap,entityMap,
65010                                         domBuilder,this.errorHandler);
65011                         domBuilder.endDocument();
65012                 }
65013         };
65014         function parse(source,defaultNSMapCopy,entityMap,domBuilder,errorHandler){
65015                 function fixedFromCharCode(code) {
65016                         // String.prototype.fromCharCode does not supports
65017                         // > 2 bytes unicode chars directly
65018                         if (code > 0xffff) {
65019                                 code -= 0x10000;
65020                                 var surrogate1 = 0xd800 + (code >> 10)
65021                                         , surrogate2 = 0xdc00 + (code & 0x3ff);
65022
65023                                 return String.fromCharCode(surrogate1, surrogate2);
65024                         } else {
65025                                 return String.fromCharCode(code);
65026                         }
65027                 }
65028                 function entityReplacer(a){
65029                         var k = a.slice(1,-1);
65030                         if(k in entityMap){
65031                                 return entityMap[k]; 
65032                         }else if(k.charAt(0) === '#'){
65033                                 return fixedFromCharCode(parseInt(k.substr(1).replace('x','0x')))
65034                         }else {
65035                                 errorHandler.error('entity not found:'+a);
65036                                 return a;
65037                         }
65038                 }
65039                 function appendText(end){//has some bugs
65040                         if(end>start){
65041                                 var xt = source.substring(start,end).replace(/&#?\w+;/g,entityReplacer);
65042                                 locator&&position(start);
65043                                 domBuilder.characters(xt,0,end-start);
65044                                 start = end;
65045                         }
65046                 }
65047                 function position(p,m){
65048                         while(p>=lineEnd && (m = linePattern.exec(source))){
65049                                 lineStart = m.index;
65050                                 lineEnd = lineStart + m[0].length;
65051                                 locator.lineNumber++;
65052                                 //console.log('line++:',locator,startPos,endPos)
65053                         }
65054                         locator.columnNumber = p-lineStart+1;
65055                 }
65056                 var lineStart = 0;
65057                 var lineEnd = 0;
65058                 var linePattern = /.*(?:\r\n?|\n)|.*$/g;
65059                 var locator = domBuilder.locator;
65060                 
65061                 var parseStack = [{currentNSMap:defaultNSMapCopy}];
65062                 var closeMap = {};
65063                 var start = 0;
65064                 while(true){
65065                         try{
65066                                 var tagStart = source.indexOf('<',start);
65067                                 if(tagStart<0){
65068                                         if(!source.substr(start).match(/^\s*$/)){
65069                                                 var doc = domBuilder.doc;
65070                                         var text = doc.createTextNode(source.substr(start));
65071                                         doc.appendChild(text);
65072                                         domBuilder.currentElement = text;
65073                                         }
65074                                         return;
65075                                 }
65076                                 if(tagStart>start){
65077                                         appendText(tagStart);
65078                                 }
65079                                 switch(source.charAt(tagStart+1)){
65080                                 case '/':
65081                                         var end = source.indexOf('>',tagStart+3);
65082                                         var tagName = source.substring(tagStart+2,end);
65083                                         var config = parseStack.pop();
65084                                         if(end<0){
65085                                                 
65086                                         tagName = source.substring(tagStart+2).replace(/[\s<].*/,'');
65087                                         //console.error('#@@@@@@'+tagName)
65088                                         errorHandler.error("end tag name: "+tagName+' is not complete:'+config.tagName);
65089                                         end = tagStart+1+tagName.length;
65090                                 }else if(tagName.match(/\s</)){
65091                                         tagName = tagName.replace(/[\s<].*/,'');
65092                                         errorHandler.error("end tag name: "+tagName+' maybe not complete');
65093                                         end = tagStart+1+tagName.length;
65094                                         }
65095                                         //console.error(parseStack.length,parseStack)
65096                                         //console.error(config);
65097                                         var localNSMap = config.localNSMap;
65098                                         var endMatch = config.tagName == tagName;
65099                                         var endIgnoreCaseMach = endMatch || config.tagName&&config.tagName.toLowerCase() == tagName.toLowerCase();
65100                                 if(endIgnoreCaseMach){
65101                                         domBuilder.endElement(config.uri,config.localName,tagName);
65102                                                 if(localNSMap){
65103                                                         for(var prefix in localNSMap){
65104                                                                 domBuilder.endPrefixMapping(prefix) ;
65105                                                         }
65106                                                 }
65107                                                 if(!endMatch){
65108                                         errorHandler.fatalError("end tag name: "+tagName+' is not match the current start tagName:'+config.tagName );
65109                                                 }
65110                                 }else {
65111                                         parseStack.push(config);
65112                                 }
65113                                         
65114                                         end++;
65115                                         break;
65116                                         // end elment
65117                                 case '?':// <?...?>
65118                                         locator&&position(tagStart);
65119                                         end = parseInstruction(source,tagStart,domBuilder);
65120                                         break;
65121                                 case '!':// <!doctype,<![CDATA,<!--
65122                                         locator&&position(tagStart);
65123                                         end = parseDCC(source,tagStart,domBuilder,errorHandler);
65124                                         break;
65125                                 default:
65126                                         locator&&position(tagStart);
65127                                         var el = new ElementAttributes();
65128                                         var currentNSMap = parseStack[parseStack.length-1].currentNSMap;
65129                                         //elStartEnd
65130                                         var end = parseElementStartPart(source,tagStart,el,currentNSMap,entityReplacer,errorHandler);
65131                                         var len = el.length;
65132                                         
65133                                         
65134                                         if(!el.closed && fixSelfClosed(source,end,el.tagName,closeMap)){
65135                                                 el.closed = true;
65136                                                 if(!entityMap.nbsp){
65137                                                         errorHandler.warning('unclosed xml attribute');
65138                                                 }
65139                                         }
65140                                         if(locator && len){
65141                                                 var locator2 = copyLocator(locator,{});
65142                                                 //try{//attribute position fixed
65143                                                 for(var i = 0;i<len;i++){
65144                                                         var a = el[i];
65145                                                         position(a.offset);
65146                                                         a.locator = copyLocator(locator,{});
65147                                                 }
65148                                                 //}catch(e){console.error('@@@@@'+e)}
65149                                                 domBuilder.locator = locator2;
65150                                                 if(appendElement(el,domBuilder,currentNSMap)){
65151                                                         parseStack.push(el);
65152                                                 }
65153                                                 domBuilder.locator = locator;
65154                                         }else {
65155                                                 if(appendElement(el,domBuilder,currentNSMap)){
65156                                                         parseStack.push(el);
65157                                                 }
65158                                         }
65159                                         
65160                                         
65161                                         
65162                                         if(el.uri === 'http://www.w3.org/1999/xhtml' && !el.closed){
65163                                                 end = parseHtmlSpecialContent(source,end,el.tagName,entityReplacer,domBuilder);
65164                                         }else {
65165                                                 end++;
65166                                         }
65167                                 }
65168                         }catch(e){
65169                                 errorHandler.error('element parse error: '+e);
65170                                 //errorHandler.error('element parse error: '+e);
65171                                 end = -1;
65172                                 //throw e;
65173                         }
65174                         if(end>start){
65175                                 start = end;
65176                         }else {
65177                                 //TODO: 这里有可能sax回退,有位置错误风险
65178                                 appendText(Math.max(tagStart,start)+1);
65179                         }
65180                 }
65181         }
65182         function copyLocator(f,t){
65183                 t.lineNumber = f.lineNumber;
65184                 t.columnNumber = f.columnNumber;
65185                 return t;
65186         }
65187
65188         /**
65189          * @see #appendElement(source,elStartEnd,el,selfClosed,entityReplacer,domBuilder,parseStack);
65190          * @return end of the elementStartPart(end of elementEndPart for selfClosed el)
65191          */
65192         function parseElementStartPart(source,start,el,currentNSMap,entityReplacer,errorHandler){
65193                 var attrName;
65194                 var value;
65195                 var p = ++start;
65196                 var s = S_TAG;//status
65197                 while(true){
65198                         var c = source.charAt(p);
65199                         switch(c){
65200                         case '=':
65201                                 if(s === S_ATTR){//attrName
65202                                         attrName = source.slice(start,p);
65203                                         s = S_EQ;
65204                                 }else if(s === S_ATTR_SPACE){
65205                                         s = S_EQ;
65206                                 }else {
65207                                         //fatalError: equal must after attrName or space after attrName
65208                                         throw new Error('attribute equal must after attrName');
65209                                 }
65210                                 break;
65211                         case '\'':
65212                         case '"':
65213                                 if(s === S_EQ || s === S_ATTR //|| s == S_ATTR_SPACE
65214                                         ){//equal
65215                                         if(s === S_ATTR){
65216                                                 errorHandler.warning('attribute value must after "="');
65217                                                 attrName = source.slice(start,p);
65218                                         }
65219                                         start = p+1;
65220                                         p = source.indexOf(c,start);
65221                                         if(p>0){
65222                                                 value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer);
65223                                                 el.add(attrName,value,start-1);
65224                                                 s = S_ATTR_END;
65225                                         }else {
65226                                                 //fatalError: no end quot match
65227                                                 throw new Error('attribute value no end \''+c+'\' match');
65228                                         }
65229                                 }else if(s == S_ATTR_NOQUOT_VALUE){
65230                                         value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer);
65231                                         //console.log(attrName,value,start,p)
65232                                         el.add(attrName,value,start);
65233                                         //console.dir(el)
65234                                         errorHandler.warning('attribute "'+attrName+'" missed start quot('+c+')!!');
65235                                         start = p+1;
65236                                         s = S_ATTR_END;
65237                                 }else {
65238                                         //fatalError: no equal before
65239                                         throw new Error('attribute value must after "="');
65240                                 }
65241                                 break;
65242                         case '/':
65243                                 switch(s){
65244                                 case S_TAG:
65245                                         el.setTagName(source.slice(start,p));
65246                                 case S_ATTR_END:
65247                                 case S_TAG_SPACE:
65248                                 case S_TAG_CLOSE:
65249                                         s =S_TAG_CLOSE;
65250                                         el.closed = true;
65251                                 case S_ATTR_NOQUOT_VALUE:
65252                                 case S_ATTR:
65253                                 case S_ATTR_SPACE:
65254                                         break;
65255                                 //case S_EQ:
65256                                 default:
65257                                         throw new Error("attribute invalid close char('/')")
65258                                 }
65259                                 break;
65260                         case ''://end document
65261                                 //throw new Error('unexpected end of input')
65262                                 errorHandler.error('unexpected end of input');
65263                                 if(s == S_TAG){
65264                                         el.setTagName(source.slice(start,p));
65265                                 }
65266                                 return p;
65267                         case '>':
65268                                 switch(s){
65269                                 case S_TAG:
65270                                         el.setTagName(source.slice(start,p));
65271                                 case S_ATTR_END:
65272                                 case S_TAG_SPACE:
65273                                 case S_TAG_CLOSE:
65274                                         break;//normal
65275                                 case S_ATTR_NOQUOT_VALUE://Compatible state
65276                                 case S_ATTR:
65277                                         value = source.slice(start,p);
65278                                         if(value.slice(-1) === '/'){
65279                                                 el.closed  = true;
65280                                                 value = value.slice(0,-1);
65281                                         }
65282                                 case S_ATTR_SPACE:
65283                                         if(s === S_ATTR_SPACE){
65284                                                 value = attrName;
65285                                         }
65286                                         if(s == S_ATTR_NOQUOT_VALUE){
65287                                                 errorHandler.warning('attribute "'+value+'" missed quot(")!!');
65288                                                 el.add(attrName,value.replace(/&#?\w+;/g,entityReplacer),start);
65289                                         }else {
65290                                                 if(currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !value.match(/^(?:disabled|checked|selected)$/i)){
65291                                                         errorHandler.warning('attribute "'+value+'" missed value!! "'+value+'" instead!!');
65292                                                 }
65293                                                 el.add(value,value,start);
65294                                         }
65295                                         break;
65296                                 case S_EQ:
65297                                         throw new Error('attribute value missed!!');
65298                                 }
65299         //                      console.log(tagName,tagNamePattern,tagNamePattern.test(tagName))
65300                                 return p;
65301                         /*xml space '\x20' | #x9 | #xD | #xA; */
65302                         case '\u0080':
65303                                 c = ' ';
65304                         default:
65305                                 if(c<= ' '){//space
65306                                         switch(s){
65307                                         case S_TAG:
65308                                                 el.setTagName(source.slice(start,p));//tagName
65309                                                 s = S_TAG_SPACE;
65310                                                 break;
65311                                         case S_ATTR:
65312                                                 attrName = source.slice(start,p);
65313                                                 s = S_ATTR_SPACE;
65314                                                 break;
65315                                         case S_ATTR_NOQUOT_VALUE:
65316                                                 var value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer);
65317                                                 errorHandler.warning('attribute "'+value+'" missed quot(")!!');
65318                                                 el.add(attrName,value,start);
65319                                         case S_ATTR_END:
65320                                                 s = S_TAG_SPACE;
65321                                                 break;
65322                                         //case S_TAG_SPACE:
65323                                         //case S_EQ:
65324                                         //case S_ATTR_SPACE:
65325                                         //      void();break;
65326                                         //case S_TAG_CLOSE:
65327                                                 //ignore warning
65328                                         }
65329                                 }else {//not space
65330         //S_TAG,        S_ATTR, S_EQ,   S_ATTR_NOQUOT_VALUE
65331         //S_ATTR_SPACE, S_ATTR_END,     S_TAG_SPACE, S_TAG_CLOSE
65332                                         switch(s){
65333                                         //case S_TAG:void();break;
65334                                         //case S_ATTR:void();break;
65335                                         //case S_ATTR_NOQUOT_VALUE:void();break;
65336                                         case S_ATTR_SPACE:
65337                                                 var tagName =  el.tagName;
65338                                                 if(currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !attrName.match(/^(?:disabled|checked|selected)$/i)){
65339                                                         errorHandler.warning('attribute "'+attrName+'" missed value!! "'+attrName+'" instead2!!');
65340                                                 }
65341                                                 el.add(attrName,attrName,start);
65342                                                 start = p;
65343                                                 s = S_ATTR;
65344                                                 break;
65345                                         case S_ATTR_END:
65346                                                 errorHandler.warning('attribute space is required"'+attrName+'"!!');
65347                                         case S_TAG_SPACE:
65348                                                 s = S_ATTR;
65349                                                 start = p;
65350                                                 break;
65351                                         case S_EQ:
65352                                                 s = S_ATTR_NOQUOT_VALUE;
65353                                                 start = p;
65354                                                 break;
65355                                         case S_TAG_CLOSE:
65356                                                 throw new Error("elements closed character '/' and '>' must be connected to");
65357                                         }
65358                                 }
65359                         }//end outer switch
65360                         //console.log('p++',p)
65361                         p++;
65362                 }
65363         }
65364         /**
65365          * @return true if has new namespace define
65366          */
65367         function appendElement(el,domBuilder,currentNSMap){
65368                 var tagName = el.tagName;
65369                 var localNSMap = null;
65370                 //var currentNSMap = parseStack[parseStack.length-1].currentNSMap;
65371                 var i = el.length;
65372                 while(i--){
65373                         var a = el[i];
65374                         var qName = a.qName;
65375                         var value = a.value;
65376                         var nsp = qName.indexOf(':');
65377                         if(nsp>0){
65378                                 var prefix = a.prefix = qName.slice(0,nsp);
65379                                 var localName = qName.slice(nsp+1);
65380                                 var nsPrefix = prefix === 'xmlns' && localName;
65381                         }else {
65382                                 localName = qName;
65383                                 prefix = null;
65384                                 nsPrefix = qName === 'xmlns' && '';
65385                         }
65386                         //can not set prefix,because prefix !== ''
65387                         a.localName = localName ;
65388                         //prefix == null for no ns prefix attribute 
65389                         if(nsPrefix !== false){//hack!!
65390                                 if(localNSMap == null){
65391                                         localNSMap = {};
65392                                         //console.log(currentNSMap,0)
65393                                         _copy(currentNSMap,currentNSMap={});
65394                                         //console.log(currentNSMap,1)
65395                                 }
65396                                 currentNSMap[nsPrefix] = localNSMap[nsPrefix] = value;
65397                                 a.uri = 'http://www.w3.org/2000/xmlns/';
65398                                 domBuilder.startPrefixMapping(nsPrefix, value); 
65399                         }
65400                 }
65401                 var i = el.length;
65402                 while(i--){
65403                         a = el[i];
65404                         var prefix = a.prefix;
65405                         if(prefix){//no prefix attribute has no namespace
65406                                 if(prefix === 'xml'){
65407                                         a.uri = 'http://www.w3.org/XML/1998/namespace';
65408                                 }if(prefix !== 'xmlns'){
65409                                         a.uri = currentNSMap[prefix || ''];
65410                                         
65411                                         //{console.log('###'+a.qName,domBuilder.locator.systemId+'',currentNSMap,a.uri)}
65412                                 }
65413                         }
65414                 }
65415                 var nsp = tagName.indexOf(':');
65416                 if(nsp>0){
65417                         prefix = el.prefix = tagName.slice(0,nsp);
65418                         localName = el.localName = tagName.slice(nsp+1);
65419                 }else {
65420                         prefix = null;//important!!
65421                         localName = el.localName = tagName;
65422                 }
65423                 //no prefix element has default namespace
65424                 var ns = el.uri = currentNSMap[prefix || ''];
65425                 domBuilder.startElement(ns,localName,tagName,el);
65426                 //endPrefixMapping and startPrefixMapping have not any help for dom builder
65427                 //localNSMap = null
65428                 if(el.closed){
65429                         domBuilder.endElement(ns,localName,tagName);
65430                         if(localNSMap){
65431                                 for(prefix in localNSMap){
65432                                         domBuilder.endPrefixMapping(prefix); 
65433                                 }
65434                         }
65435                 }else {
65436                         el.currentNSMap = currentNSMap;
65437                         el.localNSMap = localNSMap;
65438                         //parseStack.push(el);
65439                         return true;
65440                 }
65441         }
65442         function parseHtmlSpecialContent(source,elStartEnd,tagName,entityReplacer,domBuilder){
65443                 if(/^(?:script|textarea)$/i.test(tagName)){
65444                         var elEndStart =  source.indexOf('</'+tagName+'>',elStartEnd);
65445                         var text = source.substring(elStartEnd+1,elEndStart);
65446                         if(/[&<]/.test(text)){
65447                                 if(/^script$/i.test(tagName)){
65448                                         //if(!/\]\]>/.test(text)){
65449                                                 //lexHandler.startCDATA();
65450                                                 domBuilder.characters(text,0,text.length);
65451                                                 //lexHandler.endCDATA();
65452                                                 return elEndStart;
65453                                         //}
65454                                 }//}else{//text area
65455                                         text = text.replace(/&#?\w+;/g,entityReplacer);
65456                                         domBuilder.characters(text,0,text.length);
65457                                         return elEndStart;
65458                                 //}
65459                                 
65460                         }
65461                 }
65462                 return elStartEnd+1;
65463         }
65464         function fixSelfClosed(source,elStartEnd,tagName,closeMap){
65465                 //if(tagName in closeMap){
65466                 var pos = closeMap[tagName];
65467                 if(pos == null){
65468                         //console.log(tagName)
65469                         pos =  source.lastIndexOf('</'+tagName+'>');
65470                         if(pos<elStartEnd){//忘记闭合
65471                                 pos = source.lastIndexOf('</'+tagName);
65472                         }
65473                         closeMap[tagName] =pos;
65474                 }
65475                 return pos<elStartEnd;
65476                 //} 
65477         }
65478         function _copy(source,target){
65479                 for(var n in source){target[n] = source[n];}
65480         }
65481         function parseDCC(source,start,domBuilder,errorHandler){//sure start with '<!'
65482                 var next= source.charAt(start+2);
65483                 switch(next){
65484                 case '-':
65485                         if(source.charAt(start + 3) === '-'){
65486                                 var end = source.indexOf('-->',start+4);
65487                                 //append comment source.substring(4,end)//<!--
65488                                 if(end>start){
65489                                         domBuilder.comment(source,start+4,end-start-4);
65490                                         return end+3;
65491                                 }else {
65492                                         errorHandler.error("Unclosed comment");
65493                                         return -1;
65494                                 }
65495                         }else {
65496                                 //error
65497                                 return -1;
65498                         }
65499                 default:
65500                         if(source.substr(start+3,6) == 'CDATA['){
65501                                 var end = source.indexOf(']]>',start+9);
65502                                 domBuilder.startCDATA();
65503                                 domBuilder.characters(source,start+9,end-start-9);
65504                                 domBuilder.endCDATA(); 
65505                                 return end+3;
65506                         }
65507                         //<!DOCTYPE
65508                         //startDTD(java.lang.String name, java.lang.String publicId, java.lang.String systemId) 
65509                         var matchs = split(source,start);
65510                         var len = matchs.length;
65511                         if(len>1 && /!doctype/i.test(matchs[0][0])){
65512                                 var name = matchs[1][0];
65513                                 var pubid = len>3 && /^public$/i.test(matchs[2][0]) && matchs[3][0];
65514                                 var sysid = len>4 && matchs[4][0];
65515                                 var lastMatch = matchs[len-1];
65516                                 domBuilder.startDTD(name,pubid && pubid.replace(/^(['"])(.*?)\1$/,'$2'),
65517                                                 sysid && sysid.replace(/^(['"])(.*?)\1$/,'$2'));
65518                                 domBuilder.endDTD();
65519                                 
65520                                 return lastMatch.index+lastMatch[0].length
65521                         }
65522                 }
65523                 return -1;
65524         }
65525
65526
65527
65528         function parseInstruction(source,start,domBuilder){
65529                 var end = source.indexOf('?>',start);
65530                 if(end){
65531                         var match = source.substring(start,end).match(/^<\?(\S*)\s*([\s\S]*?)\s*$/);
65532                         if(match){
65533                                 var len = match[0].length;
65534                                 domBuilder.processingInstruction(match[1], match[2]) ;
65535                                 return end+2;
65536                         }else {//error
65537                                 return -1;
65538                         }
65539                 }
65540                 return -1;
65541         }
65542
65543         /**
65544          * @param source
65545          */
65546         function ElementAttributes(source){
65547                 
65548         }
65549         ElementAttributes.prototype = {
65550                 setTagName:function(tagName){
65551                         if(!tagNamePattern.test(tagName)){
65552                                 throw new Error('invalid tagName:'+tagName)
65553                         }
65554                         this.tagName = tagName;
65555                 },
65556                 add:function(qName,value,offset){
65557                         if(!tagNamePattern.test(qName)){
65558                                 throw new Error('invalid attribute:'+qName)
65559                         }
65560                         this[this.length++] = {qName:qName,value:value,offset:offset};
65561                 },
65562                 length:0,
65563                 getLocalName:function(i){return this[i].localName},
65564                 getLocator:function(i){return this[i].locator},
65565                 getQName:function(i){return this[i].qName},
65566                 getURI:function(i){return this[i].uri},
65567                 getValue:function(i){return this[i].value}
65568         //      ,getIndex:function(uri, localName)){
65569         //              if(localName){
65570         //                      
65571         //              }else{
65572         //                      var qName = uri
65573         //              }
65574         //      },
65575         //      getValue:function(){return this.getValue(this.getIndex.apply(this,arguments))},
65576         //      getType:function(uri,localName){}
65577         //      getType:function(i){},
65578         };
65579
65580
65581
65582
65583         function _set_proto_(thiz,parent){
65584                 thiz.__proto__ = parent;
65585                 return thiz;
65586         }
65587         if(!(_set_proto_({},_set_proto_.prototype) instanceof _set_proto_)){
65588                 _set_proto_ = function(thiz,parent){
65589                         function p(){}          p.prototype = parent;
65590                         p = new p();
65591                         for(parent in thiz){
65592                                 p[parent] = thiz[parent];
65593                         }
65594                         return p;
65595                 };
65596         }
65597
65598         function split(source,start){
65599                 var match;
65600                 var buf = [];
65601                 var reg = /'[^']+'|"[^"]+"|[^\s<>\/=]+=?|(\/?\s*>|<)/g;
65602                 reg.lastIndex = start;
65603                 reg.exec(source);//skip <
65604                 while(match = reg.exec(source)){
65605                         buf.push(match);
65606                         if(match[1])return buf;
65607                 }
65608         }
65609
65610         var XMLReader_1 = XMLReader;
65611
65612         var sax = {
65613                 XMLReader: XMLReader_1
65614         };
65615
65616         /*
65617          * DOM Level 2
65618          * Object DOMException
65619          * @see http://www.w3.org/TR/REC-DOM-Level-1/ecma-script-language-binding.html
65620          * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/ecma-script-binding.html
65621          */
65622
65623         function copy$2(src,dest){
65624                 for(var p in src){
65625                         dest[p] = src[p];
65626                 }
65627         }
65628         /**
65629         ^\w+\.prototype\.([_\w]+)\s*=\s*((?:.*\{\s*?[\r\n][\s\S]*?^})|\S.*?(?=[;\r\n]));?
65630         ^\w+\.prototype\.([_\w]+)\s*=\s*(\S.*?(?=[;\r\n]));?
65631          */
65632         function _extends(Class,Super){
65633                 var pt = Class.prototype;
65634                 if(Object.create){
65635                         var ppt = Object.create(Super.prototype);
65636                         pt.__proto__ = ppt;
65637                 }
65638                 if(!(pt instanceof Super)){
65639                         function t(){}          t.prototype = Super.prototype;
65640                         t = new t();
65641                         copy$2(pt,t);
65642                         Class.prototype = pt = t;
65643                 }
65644                 if(pt.constructor != Class){
65645                         if(typeof Class != 'function'){
65646                                 console.error("unknow Class:"+Class);
65647                         }
65648                         pt.constructor = Class;
65649                 }
65650         }
65651         var htmlns = 'http://www.w3.org/1999/xhtml' ;
65652         // Node Types
65653         var NodeType = {};
65654         var ELEMENT_NODE                = NodeType.ELEMENT_NODE                = 1;
65655         var ATTRIBUTE_NODE              = NodeType.ATTRIBUTE_NODE              = 2;
65656         var TEXT_NODE                   = NodeType.TEXT_NODE                   = 3;
65657         var CDATA_SECTION_NODE          = NodeType.CDATA_SECTION_NODE          = 4;
65658         var ENTITY_REFERENCE_NODE       = NodeType.ENTITY_REFERENCE_NODE       = 5;
65659         var ENTITY_NODE                 = NodeType.ENTITY_NODE                 = 6;
65660         var PROCESSING_INSTRUCTION_NODE = NodeType.PROCESSING_INSTRUCTION_NODE = 7;
65661         var COMMENT_NODE                = NodeType.COMMENT_NODE                = 8;
65662         var DOCUMENT_NODE               = NodeType.DOCUMENT_NODE               = 9;
65663         var DOCUMENT_TYPE_NODE          = NodeType.DOCUMENT_TYPE_NODE          = 10;
65664         var DOCUMENT_FRAGMENT_NODE      = NodeType.DOCUMENT_FRAGMENT_NODE      = 11;
65665         var NOTATION_NODE               = NodeType.NOTATION_NODE               = 12;
65666
65667         // ExceptionCode
65668         var ExceptionCode = {};
65669         var ExceptionMessage = {};
65670         var INDEX_SIZE_ERR              = ExceptionCode.INDEX_SIZE_ERR              = ((ExceptionMessage[1]="Index size error"),1);
65671         var DOMSTRING_SIZE_ERR          = ExceptionCode.DOMSTRING_SIZE_ERR          = ((ExceptionMessage[2]="DOMString size error"),2);
65672         var HIERARCHY_REQUEST_ERR       = ExceptionCode.HIERARCHY_REQUEST_ERR       = ((ExceptionMessage[3]="Hierarchy request error"),3);
65673         var WRONG_DOCUMENT_ERR          = ExceptionCode.WRONG_DOCUMENT_ERR          = ((ExceptionMessage[4]="Wrong document"),4);
65674         var INVALID_CHARACTER_ERR       = ExceptionCode.INVALID_CHARACTER_ERR       = ((ExceptionMessage[5]="Invalid character"),5);
65675         var NO_DATA_ALLOWED_ERR         = ExceptionCode.NO_DATA_ALLOWED_ERR         = ((ExceptionMessage[6]="No data allowed"),6);
65676         var NO_MODIFICATION_ALLOWED_ERR = ExceptionCode.NO_MODIFICATION_ALLOWED_ERR = ((ExceptionMessage[7]="No modification allowed"),7);
65677         var NOT_FOUND_ERR               = ExceptionCode.NOT_FOUND_ERR               = ((ExceptionMessage[8]="Not found"),8);
65678         var NOT_SUPPORTED_ERR           = ExceptionCode.NOT_SUPPORTED_ERR           = ((ExceptionMessage[9]="Not supported"),9);
65679         var INUSE_ATTRIBUTE_ERR         = ExceptionCode.INUSE_ATTRIBUTE_ERR         = ((ExceptionMessage[10]="Attribute in use"),10);
65680         //level2
65681         var INVALID_STATE_ERR           = ExceptionCode.INVALID_STATE_ERR               = ((ExceptionMessage[11]="Invalid state"),11);
65682         var SYNTAX_ERR                  = ExceptionCode.SYNTAX_ERR                      = ((ExceptionMessage[12]="Syntax error"),12);
65683         var INVALID_MODIFICATION_ERR    = ExceptionCode.INVALID_MODIFICATION_ERR        = ((ExceptionMessage[13]="Invalid modification"),13);
65684         var NAMESPACE_ERR               = ExceptionCode.NAMESPACE_ERR                   = ((ExceptionMessage[14]="Invalid namespace"),14);
65685         var INVALID_ACCESS_ERR          = ExceptionCode.INVALID_ACCESS_ERR              = ((ExceptionMessage[15]="Invalid access"),15);
65686
65687
65688         function DOMException$2(code, message) {
65689                 if(message instanceof Error){
65690                         var error = message;
65691                 }else {
65692                         error = this;
65693                         Error.call(this, ExceptionMessage[code]);
65694                         this.message = ExceptionMessage[code];
65695                         if(Error.captureStackTrace) Error.captureStackTrace(this, DOMException$2);
65696                 }
65697                 error.code = code;
65698                 if(message) this.message = this.message + ": " + message;
65699                 return error;
65700         }DOMException$2.prototype = Error.prototype;
65701         copy$2(ExceptionCode,DOMException$2);
65702         /**
65703          * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-536297177
65704          * 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.
65705          * The items in the NodeList are accessible via an integral index, starting from 0.
65706          */
65707         function NodeList() {
65708         }NodeList.prototype = {
65709                 /**
65710                  * The number of nodes in the list. The range of valid child node indices is 0 to length-1 inclusive.
65711                  * @standard level1
65712                  */
65713                 length:0, 
65714                 /**
65715                  * 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.
65716                  * @standard level1
65717                  * @param index  unsigned long 
65718                  *   Index into the collection.
65719                  * @return Node
65720                  *      The node at the indexth position in the NodeList, or null if that is not a valid index. 
65721                  */
65722                 item: function(index) {
65723                         return this[index] || null;
65724                 },
65725                 toString:function(isHTML,nodeFilter){
65726                         for(var buf = [], i = 0;i<this.length;i++){
65727                                 serializeToString(this[i],buf,isHTML,nodeFilter);
65728                         }
65729                         return buf.join('');
65730                 }
65731         };
65732         function LiveNodeList(node,refresh){
65733                 this._node = node;
65734                 this._refresh = refresh;
65735                 _updateLiveList(this);
65736         }
65737         function _updateLiveList(list){
65738                 var inc = list._node._inc || list._node.ownerDocument._inc;
65739                 if(list._inc != inc){
65740                         var ls = list._refresh(list._node);
65741                         //console.log(ls.length)
65742                         __set__(list,'length',ls.length);
65743                         copy$2(ls,list);
65744                         list._inc = inc;
65745                 }
65746         }
65747         LiveNodeList.prototype.item = function(i){
65748                 _updateLiveList(this);
65749                 return this[i];
65750         };
65751
65752         _extends(LiveNodeList,NodeList);
65753         /**
65754          * 
65755          * 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.
65756          * NamedNodeMap objects in the DOM are live.
65757          * used for attributes or DocumentType entities 
65758          */
65759         function NamedNodeMap() {
65760         }
65761         function _findNodeIndex(list,node){
65762                 var i = list.length;
65763                 while(i--){
65764                         if(list[i] === node){return i}
65765                 }
65766         }
65767
65768         function _addNamedNode(el,list,newAttr,oldAttr){
65769                 if(oldAttr){
65770                         list[_findNodeIndex(list,oldAttr)] = newAttr;
65771                 }else {
65772                         list[list.length++] = newAttr;
65773                 }
65774                 if(el){
65775                         newAttr.ownerElement = el;
65776                         var doc = el.ownerDocument;
65777                         if(doc){
65778                                 oldAttr && _onRemoveAttribute(doc,el,oldAttr);
65779                                 _onAddAttribute(doc,el,newAttr);
65780                         }
65781                 }
65782         }
65783         function _removeNamedNode(el,list,attr){
65784                 //console.log('remove attr:'+attr)
65785                 var i = _findNodeIndex(list,attr);
65786                 if(i>=0){
65787                         var lastIndex = list.length-1;
65788                         while(i<lastIndex){
65789                                 list[i] = list[++i];
65790                         }
65791                         list.length = lastIndex;
65792                         if(el){
65793                                 var doc = el.ownerDocument;
65794                                 if(doc){
65795                                         _onRemoveAttribute(doc,el,attr);
65796                                         attr.ownerElement = null;
65797                                 }
65798                         }
65799                 }else {
65800                         throw DOMException$2(NOT_FOUND_ERR,new Error(el.tagName+'@'+attr))
65801                 }
65802         }
65803         NamedNodeMap.prototype = {
65804                 length:0,
65805                 item:NodeList.prototype.item,
65806                 getNamedItem: function(key) {
65807         //              if(key.indexOf(':')>0 || key == 'xmlns'){
65808         //                      return null;
65809         //              }
65810                         //console.log()
65811                         var i = this.length;
65812                         while(i--){
65813                                 var attr = this[i];
65814                                 //console.log(attr.nodeName,key)
65815                                 if(attr.nodeName == key){
65816                                         return attr;
65817                                 }
65818                         }
65819                 },
65820                 setNamedItem: function(attr) {
65821                         var el = attr.ownerElement;
65822                         if(el && el!=this._ownerElement){
65823                                 throw new DOMException$2(INUSE_ATTRIBUTE_ERR);
65824                         }
65825                         var oldAttr = this.getNamedItem(attr.nodeName);
65826                         _addNamedNode(this._ownerElement,this,attr,oldAttr);
65827                         return oldAttr;
65828                 },
65829                 /* returns Node */
65830                 setNamedItemNS: function(attr) {// raises: WRONG_DOCUMENT_ERR,NO_MODIFICATION_ALLOWED_ERR,INUSE_ATTRIBUTE_ERR
65831                         var el = attr.ownerElement, oldAttr;
65832                         if(el && el!=this._ownerElement){
65833                                 throw new DOMException$2(INUSE_ATTRIBUTE_ERR);
65834                         }
65835                         oldAttr = this.getNamedItemNS(attr.namespaceURI,attr.localName);
65836                         _addNamedNode(this._ownerElement,this,attr,oldAttr);
65837                         return oldAttr;
65838                 },
65839
65840                 /* returns Node */
65841                 removeNamedItem: function(key) {
65842                         var attr = this.getNamedItem(key);
65843                         _removeNamedNode(this._ownerElement,this,attr);
65844                         return attr;
65845                         
65846                         
65847                 },// raises: NOT_FOUND_ERR,NO_MODIFICATION_ALLOWED_ERR
65848                 
65849                 //for level2
65850                 removeNamedItemNS:function(namespaceURI,localName){
65851                         var attr = this.getNamedItemNS(namespaceURI,localName);
65852                         _removeNamedNode(this._ownerElement,this,attr);
65853                         return attr;
65854                 },
65855                 getNamedItemNS: function(namespaceURI, localName) {
65856                         var i = this.length;
65857                         while(i--){
65858                                 var node = this[i];
65859                                 if(node.localName == localName && node.namespaceURI == namespaceURI){
65860                                         return node;
65861                                 }
65862                         }
65863                         return null;
65864                 }
65865         };
65866         /**
65867          * @see http://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-102161490
65868          */
65869         function DOMImplementation(/* Object */ features) {
65870                 this._features = {};
65871                 if (features) {
65872                         for (var feature in features) {
65873                                  this._features = features[feature];
65874                         }
65875                 }
65876         }
65877         DOMImplementation.prototype = {
65878                 hasFeature: function(/* string */ feature, /* string */ version) {
65879                         var versions = this._features[feature.toLowerCase()];
65880                         if (versions && (!version || version in versions)) {
65881                                 return true;
65882                         } else {
65883                                 return false;
65884                         }
65885                 },
65886                 // Introduced in DOM Level 2:
65887                 createDocument:function(namespaceURI,  qualifiedName, doctype){// raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR,WRONG_DOCUMENT_ERR
65888                         var doc = new Document();
65889                         doc.implementation = this;
65890                         doc.childNodes = new NodeList();
65891                         doc.doctype = doctype;
65892                         if(doctype){
65893                                 doc.appendChild(doctype);
65894                         }
65895                         if(qualifiedName){
65896                                 var root = doc.createElementNS(namespaceURI,qualifiedName);
65897                                 doc.appendChild(root);
65898                         }
65899                         return doc;
65900                 },
65901                 // Introduced in DOM Level 2:
65902                 createDocumentType:function(qualifiedName, publicId, systemId){// raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR
65903                         var node = new DocumentType();
65904                         node.name = qualifiedName;
65905                         node.nodeName = qualifiedName;
65906                         node.publicId = publicId;
65907                         node.systemId = systemId;
65908                         // Introduced in DOM Level 2:
65909                         //readonly attribute DOMString        internalSubset;
65910                         
65911                         //TODO:..
65912                         //  readonly attribute NamedNodeMap     entities;
65913                         //  readonly attribute NamedNodeMap     notations;
65914                         return node;
65915                 }
65916         };
65917
65918
65919         /**
65920          * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-1950641247
65921          */
65922
65923         function Node() {
65924         }
65925         Node.prototype = {
65926                 firstChild : null,
65927                 lastChild : null,
65928                 previousSibling : null,
65929                 nextSibling : null,
65930                 attributes : null,
65931                 parentNode : null,
65932                 childNodes : null,
65933                 ownerDocument : null,
65934                 nodeValue : null,
65935                 namespaceURI : null,
65936                 prefix : null,
65937                 localName : null,
65938                 // Modified in DOM Level 2:
65939                 insertBefore:function(newChild, refChild){//raises 
65940                         return _insertBefore(this,newChild,refChild);
65941                 },
65942                 replaceChild:function(newChild, oldChild){//raises 
65943                         this.insertBefore(newChild,oldChild);
65944                         if(oldChild){
65945                                 this.removeChild(oldChild);
65946                         }
65947                 },
65948                 removeChild:function(oldChild){
65949                         return _removeChild(this,oldChild);
65950                 },
65951                 appendChild:function(newChild){
65952                         return this.insertBefore(newChild,null);
65953                 },
65954                 hasChildNodes:function(){
65955                         return this.firstChild != null;
65956                 },
65957                 cloneNode:function(deep){
65958                         return cloneNode(this.ownerDocument||this,this,deep);
65959                 },
65960                 // Modified in DOM Level 2:
65961                 normalize:function(){
65962                         var child = this.firstChild;
65963                         while(child){
65964                                 var next = child.nextSibling;
65965                                 if(next && next.nodeType == TEXT_NODE && child.nodeType == TEXT_NODE){
65966                                         this.removeChild(next);
65967                                         child.appendData(next.data);
65968                                 }else {
65969                                         child.normalize();
65970                                         child = next;
65971                                 }
65972                         }
65973                 },
65974                 // Introduced in DOM Level 2:
65975                 isSupported:function(feature, version){
65976                         return this.ownerDocument.implementation.hasFeature(feature,version);
65977                 },
65978             // Introduced in DOM Level 2:
65979             hasAttributes:function(){
65980                 return this.attributes.length>0;
65981             },
65982             lookupPrefix:function(namespaceURI){
65983                 var el = this;
65984                 while(el){
65985                         var map = el._nsMap;
65986                         //console.dir(map)
65987                         if(map){
65988                                 for(var n in map){
65989                                         if(map[n] == namespaceURI){
65990                                                 return n;
65991                                         }
65992                                 }
65993                         }
65994                         el = el.nodeType == ATTRIBUTE_NODE?el.ownerDocument : el.parentNode;
65995                 }
65996                 return null;
65997             },
65998             // Introduced in DOM Level 3:
65999             lookupNamespaceURI:function(prefix){
66000                 var el = this;
66001                 while(el){
66002                         var map = el._nsMap;
66003                         //console.dir(map)
66004                         if(map){
66005                                 if(prefix in map){
66006                                         return map[prefix] ;
66007                                 }
66008                         }
66009                         el = el.nodeType == ATTRIBUTE_NODE?el.ownerDocument : el.parentNode;
66010                 }
66011                 return null;
66012             },
66013             // Introduced in DOM Level 3:
66014             isDefaultNamespace:function(namespaceURI){
66015                 var prefix = this.lookupPrefix(namespaceURI);
66016                 return prefix == null;
66017             }
66018         };
66019
66020
66021         function _xmlEncoder(c){
66022                 return c == '<' && '&lt;' ||
66023                  c == '>' && '&gt;' ||
66024                  c == '&' && '&amp;' ||
66025                  c == '"' && '&quot;' ||
66026                  '&#'+c.charCodeAt()+';'
66027         }
66028
66029
66030         copy$2(NodeType,Node);
66031         copy$2(NodeType,Node.prototype);
66032
66033         /**
66034          * @param callback return true for continue,false for break
66035          * @return boolean true: break visit;
66036          */
66037         function _visitNode(node,callback){
66038                 if(callback(node)){
66039                         return true;
66040                 }
66041                 if(node = node.firstChild){
66042                         do{
66043                                 if(_visitNode(node,callback)){return true}
66044                 }while(node=node.nextSibling)
66045             }
66046         }
66047
66048
66049
66050         function Document(){
66051         }
66052         function _onAddAttribute(doc,el,newAttr){
66053                 doc && doc._inc++;
66054                 var ns = newAttr.namespaceURI ;
66055                 if(ns == 'http://www.w3.org/2000/xmlns/'){
66056                         //update namespace
66057                         el._nsMap[newAttr.prefix?newAttr.localName:''] = newAttr.value;
66058                 }
66059         }
66060         function _onRemoveAttribute(doc,el,newAttr,remove){
66061                 doc && doc._inc++;
66062                 var ns = newAttr.namespaceURI ;
66063                 if(ns == 'http://www.w3.org/2000/xmlns/'){
66064                         //update namespace
66065                         delete el._nsMap[newAttr.prefix?newAttr.localName:''];
66066                 }
66067         }
66068         function _onUpdateChild(doc,el,newChild){
66069                 if(doc && doc._inc){
66070                         doc._inc++;
66071                         //update childNodes
66072                         var cs = el.childNodes;
66073                         if(newChild){
66074                                 cs[cs.length++] = newChild;
66075                         }else {
66076                                 //console.log(1)
66077                                 var child = el.firstChild;
66078                                 var i = 0;
66079                                 while(child){
66080                                         cs[i++] = child;
66081                                         child =child.nextSibling;
66082                                 }
66083                                 cs.length = i;
66084                         }
66085                 }
66086         }
66087
66088         /**
66089          * attributes;
66090          * children;
66091          * 
66092          * writeable properties:
66093          * nodeValue,Attr:value,CharacterData:data
66094          * prefix
66095          */
66096         function _removeChild(parentNode,child){
66097                 var previous = child.previousSibling;
66098                 var next = child.nextSibling;
66099                 if(previous){
66100                         previous.nextSibling = next;
66101                 }else {
66102                         parentNode.firstChild = next;
66103                 }
66104                 if(next){
66105                         next.previousSibling = previous;
66106                 }else {
66107                         parentNode.lastChild = previous;
66108                 }
66109                 _onUpdateChild(parentNode.ownerDocument,parentNode);
66110                 return child;
66111         }
66112         /**
66113          * preformance key(refChild == null)
66114          */
66115         function _insertBefore(parentNode,newChild,nextChild){
66116                 var cp = newChild.parentNode;
66117                 if(cp){
66118                         cp.removeChild(newChild);//remove and update
66119                 }
66120                 if(newChild.nodeType === DOCUMENT_FRAGMENT_NODE){
66121                         var newFirst = newChild.firstChild;
66122                         if (newFirst == null) {
66123                                 return newChild;
66124                         }
66125                         var newLast = newChild.lastChild;
66126                 }else {
66127                         newFirst = newLast = newChild;
66128                 }
66129                 var pre = nextChild ? nextChild.previousSibling : parentNode.lastChild;
66130
66131                 newFirst.previousSibling = pre;
66132                 newLast.nextSibling = nextChild;
66133                 
66134                 
66135                 if(pre){
66136                         pre.nextSibling = newFirst;
66137                 }else {
66138                         parentNode.firstChild = newFirst;
66139                 }
66140                 if(nextChild == null){
66141                         parentNode.lastChild = newLast;
66142                 }else {
66143                         nextChild.previousSibling = newLast;
66144                 }
66145                 do{
66146                         newFirst.parentNode = parentNode;
66147                 }while(newFirst !== newLast && (newFirst= newFirst.nextSibling))
66148                 _onUpdateChild(parentNode.ownerDocument||parentNode,parentNode);
66149                 //console.log(parentNode.lastChild.nextSibling == null)
66150                 if (newChild.nodeType == DOCUMENT_FRAGMENT_NODE) {
66151                         newChild.firstChild = newChild.lastChild = null;
66152                 }
66153                 return newChild;
66154         }
66155         function _appendSingleChild(parentNode,newChild){
66156                 var cp = newChild.parentNode;
66157                 if(cp){
66158                         var pre = parentNode.lastChild;
66159                         cp.removeChild(newChild);//remove and update
66160                         var pre = parentNode.lastChild;
66161                 }
66162                 var pre = parentNode.lastChild;
66163                 newChild.parentNode = parentNode;
66164                 newChild.previousSibling = pre;
66165                 newChild.nextSibling = null;
66166                 if(pre){
66167                         pre.nextSibling = newChild;
66168                 }else {
66169                         parentNode.firstChild = newChild;
66170                 }
66171                 parentNode.lastChild = newChild;
66172                 _onUpdateChild(parentNode.ownerDocument,parentNode,newChild);
66173                 return newChild;
66174                 //console.log("__aa",parentNode.lastChild.nextSibling == null)
66175         }
66176         Document.prototype = {
66177                 //implementation : null,
66178                 nodeName :  '#document',
66179                 nodeType :  DOCUMENT_NODE,
66180                 doctype :  null,
66181                 documentElement :  null,
66182                 _inc : 1,
66183                 
66184                 insertBefore :  function(newChild, refChild){//raises 
66185                         if(newChild.nodeType == DOCUMENT_FRAGMENT_NODE){
66186                                 var child = newChild.firstChild;
66187                                 while(child){
66188                                         var next = child.nextSibling;
66189                                         this.insertBefore(child,refChild);
66190                                         child = next;
66191                                 }
66192                                 return newChild;
66193                         }
66194                         if(this.documentElement == null && newChild.nodeType == ELEMENT_NODE){
66195                                 this.documentElement = newChild;
66196                         }
66197                         
66198                         return _insertBefore(this,newChild,refChild),(newChild.ownerDocument = this),newChild;
66199                 },
66200                 removeChild :  function(oldChild){
66201                         if(this.documentElement == oldChild){
66202                                 this.documentElement = null;
66203                         }
66204                         return _removeChild(this,oldChild);
66205                 },
66206                 // Introduced in DOM Level 2:
66207                 importNode : function(importedNode,deep){
66208                         return importNode(this,importedNode,deep);
66209                 },
66210                 // Introduced in DOM Level 2:
66211                 getElementById :        function(id){
66212                         var rtv = null;
66213                         _visitNode(this.documentElement,function(node){
66214                                 if(node.nodeType == ELEMENT_NODE){
66215                                         if(node.getAttribute('id') == id){
66216                                                 rtv = node;
66217                                                 return true;
66218                                         }
66219                                 }
66220                         });
66221                         return rtv;
66222                 },
66223                 
66224                 //document factory method:
66225                 createElement : function(tagName){
66226                         var node = new Element();
66227                         node.ownerDocument = this;
66228                         node.nodeName = tagName;
66229                         node.tagName = tagName;
66230                         node.childNodes = new NodeList();
66231                         var attrs       = node.attributes = new NamedNodeMap();
66232                         attrs._ownerElement = node;
66233                         return node;
66234                 },
66235                 createDocumentFragment :        function(){
66236                         var node = new DocumentFragment();
66237                         node.ownerDocument = this;
66238                         node.childNodes = new NodeList();
66239                         return node;
66240                 },
66241                 createTextNode :        function(data){
66242                         var node = new Text();
66243                         node.ownerDocument = this;
66244                         node.appendData(data);
66245                         return node;
66246                 },
66247                 createComment : function(data){
66248                         var node = new Comment();
66249                         node.ownerDocument = this;
66250                         node.appendData(data);
66251                         return node;
66252                 },
66253                 createCDATASection :    function(data){
66254                         var node = new CDATASection();
66255                         node.ownerDocument = this;
66256                         node.appendData(data);
66257                         return node;
66258                 },
66259                 createProcessingInstruction :   function(target,data){
66260                         var node = new ProcessingInstruction();
66261                         node.ownerDocument = this;
66262                         node.tagName = node.target = target;
66263                         node.nodeValue= node.data = data;
66264                         return node;
66265                 },
66266                 createAttribute :       function(name){
66267                         var node = new Attr();
66268                         node.ownerDocument      = this;
66269                         node.name = name;
66270                         node.nodeName   = name;
66271                         node.localName = name;
66272                         node.specified = true;
66273                         return node;
66274                 },
66275                 createEntityReference : function(name){
66276                         var node = new EntityReference();
66277                         node.ownerDocument      = this;
66278                         node.nodeName   = name;
66279                         return node;
66280                 },
66281                 // Introduced in DOM Level 2:
66282                 createElementNS :       function(namespaceURI,qualifiedName){
66283                         var node = new Element();
66284                         var pl = qualifiedName.split(':');
66285                         var attrs       = node.attributes = new NamedNodeMap();
66286                         node.childNodes = new NodeList();
66287                         node.ownerDocument = this;
66288                         node.nodeName = qualifiedName;
66289                         node.tagName = qualifiedName;
66290                         node.namespaceURI = namespaceURI;
66291                         if(pl.length == 2){
66292                                 node.prefix = pl[0];
66293                                 node.localName = pl[1];
66294                         }else {
66295                                 //el.prefix = null;
66296                                 node.localName = qualifiedName;
66297                         }
66298                         attrs._ownerElement = node;
66299                         return node;
66300                 },
66301                 // Introduced in DOM Level 2:
66302                 createAttributeNS :     function(namespaceURI,qualifiedName){
66303                         var node = new Attr();
66304                         var pl = qualifiedName.split(':');
66305                         node.ownerDocument = this;
66306                         node.nodeName = qualifiedName;
66307                         node.name = qualifiedName;
66308                         node.namespaceURI = namespaceURI;
66309                         node.specified = true;
66310                         if(pl.length == 2){
66311                                 node.prefix = pl[0];
66312                                 node.localName = pl[1];
66313                         }else {
66314                                 //el.prefix = null;
66315                                 node.localName = qualifiedName;
66316                         }
66317                         return node;
66318                 }
66319         };
66320         _extends(Document,Node);
66321
66322
66323         function Element() {
66324                 this._nsMap = {};
66325         }Element.prototype = {
66326                 nodeType : ELEMENT_NODE,
66327                 hasAttribute : function(name){
66328                         return this.getAttributeNode(name)!=null;
66329                 },
66330                 getAttribute : function(name){
66331                         var attr = this.getAttributeNode(name);
66332                         return attr && attr.value || '';
66333                 },
66334                 getAttributeNode : function(name){
66335                         return this.attributes.getNamedItem(name);
66336                 },
66337                 setAttribute : function(name, value){
66338                         var attr = this.ownerDocument.createAttribute(name);
66339                         attr.value = attr.nodeValue = "" + value;
66340                         this.setAttributeNode(attr);
66341                 },
66342                 removeAttribute : function(name){
66343                         var attr = this.getAttributeNode(name);
66344                         attr && this.removeAttributeNode(attr);
66345                 },
66346                 
66347                 //four real opeartion method
66348                 appendChild:function(newChild){
66349                         if(newChild.nodeType === DOCUMENT_FRAGMENT_NODE){
66350                                 return this.insertBefore(newChild,null);
66351                         }else {
66352                                 return _appendSingleChild(this,newChild);
66353                         }
66354                 },
66355                 setAttributeNode : function(newAttr){
66356                         return this.attributes.setNamedItem(newAttr);
66357                 },
66358                 setAttributeNodeNS : function(newAttr){
66359                         return this.attributes.setNamedItemNS(newAttr);
66360                 },
66361                 removeAttributeNode : function(oldAttr){
66362                         //console.log(this == oldAttr.ownerElement)
66363                         return this.attributes.removeNamedItem(oldAttr.nodeName);
66364                 },
66365                 //get real attribute name,and remove it by removeAttributeNode
66366                 removeAttributeNS : function(namespaceURI, localName){
66367                         var old = this.getAttributeNodeNS(namespaceURI, localName);
66368                         old && this.removeAttributeNode(old);
66369                 },
66370                 
66371                 hasAttributeNS : function(namespaceURI, localName){
66372                         return this.getAttributeNodeNS(namespaceURI, localName)!=null;
66373                 },
66374                 getAttributeNS : function(namespaceURI, localName){
66375                         var attr = this.getAttributeNodeNS(namespaceURI, localName);
66376                         return attr && attr.value || '';
66377                 },
66378                 setAttributeNS : function(namespaceURI, qualifiedName, value){
66379                         var attr = this.ownerDocument.createAttributeNS(namespaceURI, qualifiedName);
66380                         attr.value = attr.nodeValue = "" + value;
66381                         this.setAttributeNode(attr);
66382                 },
66383                 getAttributeNodeNS : function(namespaceURI, localName){
66384                         return this.attributes.getNamedItemNS(namespaceURI, localName);
66385                 },
66386                 
66387                 getElementsByTagName : function(tagName){
66388                         return new LiveNodeList(this,function(base){
66389                                 var ls = [];
66390                                 _visitNode(base,function(node){
66391                                         if(node !== base && node.nodeType == ELEMENT_NODE && (tagName === '*' || node.tagName == tagName)){
66392                                                 ls.push(node);
66393                                         }
66394                                 });
66395                                 return ls;
66396                         });
66397                 },
66398                 getElementsByTagNameNS : function(namespaceURI, localName){
66399                         return new LiveNodeList(this,function(base){
66400                                 var ls = [];
66401                                 _visitNode(base,function(node){
66402                                         if(node !== base && node.nodeType === ELEMENT_NODE && (namespaceURI === '*' || node.namespaceURI === namespaceURI) && (localName === '*' || node.localName == localName)){
66403                                                 ls.push(node);
66404                                         }
66405                                 });
66406                                 return ls;
66407                                 
66408                         });
66409                 }
66410         };
66411         Document.prototype.getElementsByTagName = Element.prototype.getElementsByTagName;
66412         Document.prototype.getElementsByTagNameNS = Element.prototype.getElementsByTagNameNS;
66413
66414
66415         _extends(Element,Node);
66416         function Attr() {
66417         }Attr.prototype.nodeType = ATTRIBUTE_NODE;
66418         _extends(Attr,Node);
66419
66420
66421         function CharacterData() {
66422         }CharacterData.prototype = {
66423                 data : '',
66424                 substringData : function(offset, count) {
66425                         return this.data.substring(offset, offset+count);
66426                 },
66427                 appendData: function(text) {
66428                         text = this.data+text;
66429                         this.nodeValue = this.data = text;
66430                         this.length = text.length;
66431                 },
66432                 insertData: function(offset,text) {
66433                         this.replaceData(offset,0,text);
66434                 
66435                 },
66436                 appendChild:function(newChild){
66437                         throw new Error(ExceptionMessage[HIERARCHY_REQUEST_ERR])
66438                 },
66439                 deleteData: function(offset, count) {
66440                         this.replaceData(offset,count,"");
66441                 },
66442                 replaceData: function(offset, count, text) {
66443                         var start = this.data.substring(0,offset);
66444                         var end = this.data.substring(offset+count);
66445                         text = start + text + end;
66446                         this.nodeValue = this.data = text;
66447                         this.length = text.length;
66448                 }
66449         };
66450         _extends(CharacterData,Node);
66451         function Text() {
66452         }Text.prototype = {
66453                 nodeName : "#text",
66454                 nodeType : TEXT_NODE,
66455                 splitText : function(offset) {
66456                         var text = this.data;
66457                         var newText = text.substring(offset);
66458                         text = text.substring(0, offset);
66459                         this.data = this.nodeValue = text;
66460                         this.length = text.length;
66461                         var newNode = this.ownerDocument.createTextNode(newText);
66462                         if(this.parentNode){
66463                                 this.parentNode.insertBefore(newNode, this.nextSibling);
66464                         }
66465                         return newNode;
66466                 }
66467         };
66468         _extends(Text,CharacterData);
66469         function Comment() {
66470         }Comment.prototype = {
66471                 nodeName : "#comment",
66472                 nodeType : COMMENT_NODE
66473         };
66474         _extends(Comment,CharacterData);
66475
66476         function CDATASection() {
66477         }CDATASection.prototype = {
66478                 nodeName : "#cdata-section",
66479                 nodeType : CDATA_SECTION_NODE
66480         };
66481         _extends(CDATASection,CharacterData);
66482
66483
66484         function DocumentType() {
66485         }DocumentType.prototype.nodeType = DOCUMENT_TYPE_NODE;
66486         _extends(DocumentType,Node);
66487
66488         function Notation() {
66489         }Notation.prototype.nodeType = NOTATION_NODE;
66490         _extends(Notation,Node);
66491
66492         function Entity() {
66493         }Entity.prototype.nodeType = ENTITY_NODE;
66494         _extends(Entity,Node);
66495
66496         function EntityReference() {
66497         }EntityReference.prototype.nodeType = ENTITY_REFERENCE_NODE;
66498         _extends(EntityReference,Node);
66499
66500         function DocumentFragment() {
66501         }DocumentFragment.prototype.nodeName =  "#document-fragment";
66502         DocumentFragment.prototype.nodeType =   DOCUMENT_FRAGMENT_NODE;
66503         _extends(DocumentFragment,Node);
66504
66505
66506         function ProcessingInstruction() {
66507         }
66508         ProcessingInstruction.prototype.nodeType = PROCESSING_INSTRUCTION_NODE;
66509         _extends(ProcessingInstruction,Node);
66510         function XMLSerializer$1(){}
66511         XMLSerializer$1.prototype.serializeToString = function(node,isHtml,nodeFilter){
66512                 return nodeSerializeToString.call(node,isHtml,nodeFilter);
66513         };
66514         Node.prototype.toString = nodeSerializeToString;
66515         function nodeSerializeToString(isHtml,nodeFilter){
66516                 var buf = [];
66517                 var refNode = this.nodeType == 9?this.documentElement:this;
66518                 var prefix = refNode.prefix;
66519                 var uri = refNode.namespaceURI;
66520                 
66521                 if(uri && prefix == null){
66522                         //console.log(prefix)
66523                         var prefix = refNode.lookupPrefix(uri);
66524                         if(prefix == null){
66525                                 //isHTML = true;
66526                                 var visibleNamespaces=[
66527                                 {namespace:uri,prefix:null}
66528                                 //{namespace:uri,prefix:''}
66529                                 ];
66530                         }
66531                 }
66532                 serializeToString(this,buf,isHtml,nodeFilter,visibleNamespaces);
66533                 //console.log('###',this.nodeType,uri,prefix,buf.join(''))
66534                 return buf.join('');
66535         }
66536         function needNamespaceDefine(node,isHTML, visibleNamespaces) {
66537                 var prefix = node.prefix||'';
66538                 var uri = node.namespaceURI;
66539                 if (!prefix && !uri){
66540                         return false;
66541                 }
66542                 if (prefix === "xml" && uri === "http://www.w3.org/XML/1998/namespace" 
66543                         || uri == 'http://www.w3.org/2000/xmlns/'){
66544                         return false;
66545                 }
66546                 
66547                 var i = visibleNamespaces.length; 
66548                 //console.log('@@@@',node.tagName,prefix,uri,visibleNamespaces)
66549                 while (i--) {
66550                         var ns = visibleNamespaces[i];
66551                         // get namespace prefix
66552                         //console.log(node.nodeType,node.tagName,ns.prefix,prefix)
66553                         if (ns.prefix == prefix){
66554                                 return ns.namespace != uri;
66555                         }
66556                 }
66557                 //console.log(isHTML,uri,prefix=='')
66558                 //if(isHTML && prefix ==null && uri == 'http://www.w3.org/1999/xhtml'){
66559                 //      return false;
66560                 //}
66561                 //node.flag = '11111'
66562                 //console.error(3,true,node.flag,node.prefix,node.namespaceURI)
66563                 return true;
66564         }
66565         function serializeToString(node,buf,isHTML,nodeFilter,visibleNamespaces){
66566                 if(nodeFilter){
66567                         node = nodeFilter(node);
66568                         if(node){
66569                                 if(typeof node == 'string'){
66570                                         buf.push(node);
66571                                         return;
66572                                 }
66573                         }else {
66574                                 return;
66575                         }
66576                         //buf.sort.apply(attrs, attributeSorter);
66577                 }
66578                 switch(node.nodeType){
66579                 case ELEMENT_NODE:
66580                         if (!visibleNamespaces) visibleNamespaces = [];
66581                         var startVisibleNamespaces = visibleNamespaces.length;
66582                         var attrs = node.attributes;
66583                         var len = attrs.length;
66584                         var child = node.firstChild;
66585                         var nodeName = node.tagName;
66586                         
66587                         isHTML =  (htmlns === node.namespaceURI) ||isHTML; 
66588                         buf.push('<',nodeName);
66589                         
66590                         
66591                         
66592                         for(var i=0;i<len;i++){
66593                                 // add namespaces for attributes
66594                                 var attr = attrs.item(i);
66595                                 if (attr.prefix == 'xmlns') {
66596                                         visibleNamespaces.push({ prefix: attr.localName, namespace: attr.value });
66597                                 }else if(attr.nodeName == 'xmlns'){
66598                                         visibleNamespaces.push({ prefix: '', namespace: attr.value });
66599                                 }
66600                         }
66601                         for(var i=0;i<len;i++){
66602                                 var attr = attrs.item(i);
66603                                 if (needNamespaceDefine(attr,isHTML, visibleNamespaces)) {
66604                                         var prefix = attr.prefix||'';
66605                                         var uri = attr.namespaceURI;
66606                                         var ns = prefix ? ' xmlns:' + prefix : " xmlns";
66607                                         buf.push(ns, '="' , uri , '"');
66608                                         visibleNamespaces.push({ prefix: prefix, namespace:uri });
66609                                 }
66610                                 serializeToString(attr,buf,isHTML,nodeFilter,visibleNamespaces);
66611                         }
66612                         // add namespace for current node               
66613                         if (needNamespaceDefine(node,isHTML, visibleNamespaces)) {
66614                                 var prefix = node.prefix||'';
66615                                 var uri = node.namespaceURI;
66616                                 var ns = prefix ? ' xmlns:' + prefix : " xmlns";
66617                                 buf.push(ns, '="' , uri , '"');
66618                                 visibleNamespaces.push({ prefix: prefix, namespace:uri });
66619                         }
66620                         
66621                         if(child || isHTML && !/^(?:meta|link|img|br|hr|input)$/i.test(nodeName)){
66622                                 buf.push('>');
66623                                 //if is cdata child node
66624                                 if(isHTML && /^script$/i.test(nodeName)){
66625                                         while(child){
66626                                                 if(child.data){
66627                                                         buf.push(child.data);
66628                                                 }else {
66629                                                         serializeToString(child,buf,isHTML,nodeFilter,visibleNamespaces);
66630                                                 }
66631                                                 child = child.nextSibling;
66632                                         }
66633                                 }else
66634                                 {
66635                                         while(child){
66636                                                 serializeToString(child,buf,isHTML,nodeFilter,visibleNamespaces);
66637                                                 child = child.nextSibling;
66638                                         }
66639                                 }
66640                                 buf.push('</',nodeName,'>');
66641                         }else {
66642                                 buf.push('/>');
66643                         }
66644                         // remove added visible namespaces
66645                         //visibleNamespaces.length = startVisibleNamespaces;
66646                         return;
66647                 case DOCUMENT_NODE:
66648                 case DOCUMENT_FRAGMENT_NODE:
66649                         var child = node.firstChild;
66650                         while(child){
66651                                 serializeToString(child,buf,isHTML,nodeFilter,visibleNamespaces);
66652                                 child = child.nextSibling;
66653                         }
66654                         return;
66655                 case ATTRIBUTE_NODE:
66656                         return buf.push(' ',node.name,'="',node.value.replace(/[<&"]/g,_xmlEncoder),'"');
66657                 case TEXT_NODE:
66658                         return buf.push(node.data.replace(/[<&]/g,_xmlEncoder));
66659                 case CDATA_SECTION_NODE:
66660                         return buf.push( '<![CDATA[',node.data,']]>');
66661                 case COMMENT_NODE:
66662                         return buf.push( "<!--",node.data,"-->");
66663                 case DOCUMENT_TYPE_NODE:
66664                         var pubid = node.publicId;
66665                         var sysid = node.systemId;
66666                         buf.push('<!DOCTYPE ',node.name);
66667                         if(pubid){
66668                                 buf.push(' PUBLIC "',pubid);
66669                                 if (sysid && sysid!='.') {
66670                                         buf.push( '" "',sysid);
66671                                 }
66672                                 buf.push('">');
66673                         }else if(sysid && sysid!='.'){
66674                                 buf.push(' SYSTEM "',sysid,'">');
66675                         }else {
66676                                 var sub = node.internalSubset;
66677                                 if(sub){
66678                                         buf.push(" [",sub,"]");
66679                                 }
66680                                 buf.push(">");
66681                         }
66682                         return;
66683                 case PROCESSING_INSTRUCTION_NODE:
66684                         return buf.push( "<?",node.target," ",node.data,"?>");
66685                 case ENTITY_REFERENCE_NODE:
66686                         return buf.push( '&',node.nodeName,';');
66687                 //case ENTITY_NODE:
66688                 //case NOTATION_NODE:
66689                 default:
66690                         buf.push('??',node.nodeName);
66691                 }
66692         }
66693         function importNode(doc,node,deep){
66694                 var node2;
66695                 switch (node.nodeType) {
66696                 case ELEMENT_NODE:
66697                         node2 = node.cloneNode(false);
66698                         node2.ownerDocument = doc;
66699                         //var attrs = node2.attributes;
66700                         //var len = attrs.length;
66701                         //for(var i=0;i<len;i++){
66702                                 //node2.setAttributeNodeNS(importNode(doc,attrs.item(i),deep));
66703                         //}
66704                 case DOCUMENT_FRAGMENT_NODE:
66705                         break;
66706                 case ATTRIBUTE_NODE:
66707                         deep = true;
66708                         break;
66709                 //case ENTITY_REFERENCE_NODE:
66710                 //case PROCESSING_INSTRUCTION_NODE:
66711                 ////case TEXT_NODE:
66712                 //case CDATA_SECTION_NODE:
66713                 //case COMMENT_NODE:
66714                 //      deep = false;
66715                 //      break;
66716                 //case DOCUMENT_NODE:
66717                 //case DOCUMENT_TYPE_NODE:
66718                 //cannot be imported.
66719                 //case ENTITY_NODE:
66720                 //case NOTATION_NODE:
66721                 //can not hit in level3
66722                 //default:throw e;
66723                 }
66724                 if(!node2){
66725                         node2 = node.cloneNode(false);//false
66726                 }
66727                 node2.ownerDocument = doc;
66728                 node2.parentNode = null;
66729                 if(deep){
66730                         var child = node.firstChild;
66731                         while(child){
66732                                 node2.appendChild(importNode(doc,child,deep));
66733                                 child = child.nextSibling;
66734                         }
66735                 }
66736                 return node2;
66737         }
66738         //
66739         //var _relationMap = {firstChild:1,lastChild:1,previousSibling:1,nextSibling:1,
66740         //                                      attributes:1,childNodes:1,parentNode:1,documentElement:1,doctype,};
66741         function cloneNode(doc,node,deep){
66742                 var node2 = new node.constructor();
66743                 for(var n in node){
66744                         var v = node[n];
66745                         if(typeof v != 'object' ){
66746                                 if(v != node2[n]){
66747                                         node2[n] = v;
66748                                 }
66749                         }
66750                 }
66751                 if(node.childNodes){
66752                         node2.childNodes = new NodeList();
66753                 }
66754                 node2.ownerDocument = doc;
66755                 switch (node2.nodeType) {
66756                 case ELEMENT_NODE:
66757                         var attrs       = node.attributes;
66758                         var attrs2      = node2.attributes = new NamedNodeMap();
66759                         var len = attrs.length;
66760                         attrs2._ownerElement = node2;
66761                         for(var i=0;i<len;i++){
66762                                 node2.setAttributeNode(cloneNode(doc,attrs.item(i),true));
66763                         }
66764                         break;  case ATTRIBUTE_NODE:
66765                         deep = true;
66766                 }
66767                 if(deep){
66768                         var child = node.firstChild;
66769                         while(child){
66770                                 node2.appendChild(cloneNode(doc,child,deep));
66771                                 child = child.nextSibling;
66772                         }
66773                 }
66774                 return node2;
66775         }
66776
66777         function __set__(object,key,value){
66778                 object[key] = value;
66779         }
66780         //do dynamic
66781         try{
66782                 if(Object.defineProperty){
66783                         Object.defineProperty(LiveNodeList.prototype,'length',{
66784                                 get:function(){
66785                                         _updateLiveList(this);
66786                                         return this.$$length;
66787                                 }
66788                         });
66789                         Object.defineProperty(Node.prototype,'textContent',{
66790                                 get:function(){
66791                                         return getTextContent(this);
66792                                 },
66793                                 set:function(data){
66794                                         switch(this.nodeType){
66795                                         case ELEMENT_NODE:
66796                                         case DOCUMENT_FRAGMENT_NODE:
66797                                                 while(this.firstChild){
66798                                                         this.removeChild(this.firstChild);
66799                                                 }
66800                                                 if(data || String(data)){
66801                                                         this.appendChild(this.ownerDocument.createTextNode(data));
66802                                                 }
66803                                                 break;
66804                                         default:
66805                                                 //TODO:
66806                                                 this.data = data;
66807                                                 this.value = data;
66808                                                 this.nodeValue = data;
66809                                         }
66810                                 }
66811                         });
66812                         
66813                         function getTextContent(node){
66814                                 switch(node.nodeType){
66815                                 case ELEMENT_NODE:
66816                                 case DOCUMENT_FRAGMENT_NODE:
66817                                         var buf = [];
66818                                         node = node.firstChild;
66819                                         while(node){
66820                                                 if(node.nodeType!==7 && node.nodeType !==8){
66821                                                         buf.push(getTextContent(node));
66822                                                 }
66823                                                 node = node.nextSibling;
66824                                         }
66825                                         return buf.join('');
66826                                 default:
66827                                         return node.nodeValue;
66828                                 }
66829                         }
66830                         __set__ = function(object,key,value){
66831                                 //console.log(value)
66832                                 object['$$'+key] = value;
66833                         };
66834                 }
66835         }catch(e){//ie8
66836         }
66837
66838         //if(typeof require == 'function'){
66839                 var DOMImplementation_1 = DOMImplementation;
66840                 var XMLSerializer_1 = XMLSerializer$1;
66841         //}
66842
66843         var dom = {
66844                 DOMImplementation: DOMImplementation_1,
66845                 XMLSerializer: XMLSerializer_1
66846         };
66847
66848         var domParser = createCommonjsModule(function (module, exports) {
66849         function DOMParser(options){
66850                 this.options = options ||{locator:{}};
66851                 
66852         }
66853         DOMParser.prototype.parseFromString = function(source,mimeType){
66854                 var options = this.options;
66855                 var sax =  new XMLReader();
66856                 var domBuilder = options.domBuilder || new DOMHandler();//contentHandler and LexicalHandler
66857                 var errorHandler = options.errorHandler;
66858                 var locator = options.locator;
66859                 var defaultNSMap = options.xmlns||{};
66860                 var entityMap = {'lt':'<','gt':'>','amp':'&','quot':'"','apos':"'"};
66861                 if(locator){
66862                         domBuilder.setDocumentLocator(locator);
66863                 }
66864                 
66865                 sax.errorHandler = buildErrorHandler(errorHandler,domBuilder,locator);
66866                 sax.domBuilder = options.domBuilder || domBuilder;
66867                 if(/\/x?html?$/.test(mimeType)){
66868                         entityMap.nbsp = '\xa0';
66869                         entityMap.copy = '\xa9';
66870                         defaultNSMap['']= 'http://www.w3.org/1999/xhtml';
66871                 }
66872                 defaultNSMap.xml = defaultNSMap.xml || 'http://www.w3.org/XML/1998/namespace';
66873                 if(source){
66874                         sax.parse(source,defaultNSMap,entityMap);
66875                 }else {
66876                         sax.errorHandler.error("invalid doc source");
66877                 }
66878                 return domBuilder.doc;
66879         };
66880         function buildErrorHandler(errorImpl,domBuilder,locator){
66881                 if(!errorImpl){
66882                         if(domBuilder instanceof DOMHandler){
66883                                 return domBuilder;
66884                         }
66885                         errorImpl = domBuilder ;
66886                 }
66887                 var errorHandler = {};
66888                 var isCallback = errorImpl instanceof Function;
66889                 locator = locator||{};
66890                 function build(key){
66891                         var fn = errorImpl[key];
66892                         if(!fn && isCallback){
66893                                 fn = errorImpl.length == 2?function(msg){errorImpl(key,msg);}:errorImpl;
66894                         }
66895                         errorHandler[key] = fn && function(msg){
66896                                 fn('[xmldom '+key+']\t'+msg+_locator(locator));
66897                         }||function(){};
66898                 }
66899                 build('warning');
66900                 build('error');
66901                 build('fatalError');
66902                 return errorHandler;
66903         }
66904
66905         //console.log('#\n\n\n\n\n\n\n####')
66906         /**
66907          * +ContentHandler+ErrorHandler
66908          * +LexicalHandler+EntityResolver2
66909          * -DeclHandler-DTDHandler 
66910          * 
66911          * DefaultHandler:EntityResolver, DTDHandler, ContentHandler, ErrorHandler
66912          * DefaultHandler2:DefaultHandler,LexicalHandler, DeclHandler, EntityResolver2
66913          * @link http://www.saxproject.org/apidoc/org/xml/sax/helpers/DefaultHandler.html
66914          */
66915         function DOMHandler() {
66916             this.cdata = false;
66917         }
66918         function position(locator,node){
66919                 node.lineNumber = locator.lineNumber;
66920                 node.columnNumber = locator.columnNumber;
66921         }
66922         /**
66923          * @see org.xml.sax.ContentHandler#startDocument
66924          * @link http://www.saxproject.org/apidoc/org/xml/sax/ContentHandler.html
66925          */ 
66926         DOMHandler.prototype = {
66927                 startDocument : function() {
66928                 this.doc = new DOMImplementation().createDocument(null, null, null);
66929                 if (this.locator) {
66930                         this.doc.documentURI = this.locator.systemId;
66931                 }
66932                 },
66933                 startElement:function(namespaceURI, localName, qName, attrs) {
66934                         var doc = this.doc;
66935                     var el = doc.createElementNS(namespaceURI, qName||localName);
66936                     var len = attrs.length;
66937                     appendElement(this, el);
66938                     this.currentElement = el;
66939                     
66940                         this.locator && position(this.locator,el);
66941                     for (var i = 0 ; i < len; i++) {
66942                         var namespaceURI = attrs.getURI(i);
66943                         var value = attrs.getValue(i);
66944                         var qName = attrs.getQName(i);
66945                                 var attr = doc.createAttributeNS(namespaceURI, qName);
66946                                 this.locator &&position(attrs.getLocator(i),attr);
66947                                 attr.value = attr.nodeValue = value;
66948                                 el.setAttributeNode(attr);
66949                     }
66950                 },
66951                 endElement:function(namespaceURI, localName, qName) {
66952                         var current = this.currentElement;
66953                         var tagName = current.tagName;
66954                         this.currentElement = current.parentNode;
66955                 },
66956                 startPrefixMapping:function(prefix, uri) {
66957                 },
66958                 endPrefixMapping:function(prefix) {
66959                 },
66960                 processingInstruction:function(target, data) {
66961                     var ins = this.doc.createProcessingInstruction(target, data);
66962                     this.locator && position(this.locator,ins);
66963                     appendElement(this, ins);
66964                 },
66965                 ignorableWhitespace:function(ch, start, length) {
66966                 },
66967                 characters:function(chars, start, length) {
66968                         chars = _toString.apply(this,arguments);
66969                         //console.log(chars)
66970                         if(chars){
66971                                 if (this.cdata) {
66972                                         var charNode = this.doc.createCDATASection(chars);
66973                                 } else {
66974                                         var charNode = this.doc.createTextNode(chars);
66975                                 }
66976                                 if(this.currentElement){
66977                                         this.currentElement.appendChild(charNode);
66978                                 }else if(/^\s*$/.test(chars)){
66979                                         this.doc.appendChild(charNode);
66980                                         //process xml
66981                                 }
66982                                 this.locator && position(this.locator,charNode);
66983                         }
66984                 },
66985                 skippedEntity:function(name) {
66986                 },
66987                 endDocument:function() {
66988                         this.doc.normalize();
66989                 },
66990                 setDocumentLocator:function (locator) {
66991                     if(this.locator = locator){// && !('lineNumber' in locator)){
66992                         locator.lineNumber = 0;
66993                     }
66994                 },
66995                 //LexicalHandler
66996                 comment:function(chars, start, length) {
66997                         chars = _toString.apply(this,arguments);
66998                     var comm = this.doc.createComment(chars);
66999                     this.locator && position(this.locator,comm);
67000                     appendElement(this, comm);
67001                 },
67002                 
67003                 startCDATA:function() {
67004                     //used in characters() methods
67005                     this.cdata = true;
67006                 },
67007                 endCDATA:function() {
67008                     this.cdata = false;
67009                 },
67010                 
67011                 startDTD:function(name, publicId, systemId) {
67012                         var impl = this.doc.implementation;
67013                     if (impl && impl.createDocumentType) {
67014                         var dt = impl.createDocumentType(name, publicId, systemId);
67015                         this.locator && position(this.locator,dt);
67016                         appendElement(this, dt);
67017                     }
67018                 },
67019                 /**
67020                  * @see org.xml.sax.ErrorHandler
67021                  * @link http://www.saxproject.org/apidoc/org/xml/sax/ErrorHandler.html
67022                  */
67023                 warning:function(error) {
67024                         console.warn('[xmldom warning]\t'+error,_locator(this.locator));
67025                 },
67026                 error:function(error) {
67027                         console.error('[xmldom error]\t'+error,_locator(this.locator));
67028                 },
67029                 fatalError:function(error) {
67030                         console.error('[xmldom fatalError]\t'+error,_locator(this.locator));
67031                     throw error;
67032                 }
67033         };
67034         function _locator(l){
67035                 if(l){
67036                         return '\n@'+(l.systemId ||'')+'#[line:'+l.lineNumber+',col:'+l.columnNumber+']'
67037                 }
67038         }
67039         function _toString(chars,start,length){
67040                 if(typeof chars == 'string'){
67041                         return chars.substr(start,length)
67042                 }else {//java sax connect width xmldom on rhino(what about: "? && !(chars instanceof String)")
67043                         if(chars.length >= start+length || start){
67044                                 return new java.lang.String(chars,start,length)+'';
67045                         }
67046                         return chars;
67047                 }
67048         }
67049
67050         /*
67051          * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/LexicalHandler.html
67052          * used method of org.xml.sax.ext.LexicalHandler:
67053          *  #comment(chars, start, length)
67054          *  #startCDATA()
67055          *  #endCDATA()
67056          *  #startDTD(name, publicId, systemId)
67057          *
67058          *
67059          * IGNORED method of org.xml.sax.ext.LexicalHandler:
67060          *  #endDTD()
67061          *  #startEntity(name)
67062          *  #endEntity(name)
67063          *
67064          *
67065          * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/DeclHandler.html
67066          * IGNORED method of org.xml.sax.ext.DeclHandler
67067          *      #attributeDecl(eName, aName, type, mode, value)
67068          *  #elementDecl(name, model)
67069          *  #externalEntityDecl(name, publicId, systemId)
67070          *  #internalEntityDecl(name, value)
67071          * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/EntityResolver2.html
67072          * IGNORED method of org.xml.sax.EntityResolver2
67073          *  #resolveEntity(String name,String publicId,String baseURI,String systemId)
67074          *  #resolveEntity(publicId, systemId)
67075          *  #getExternalSubset(name, baseURI)
67076          * @link http://www.saxproject.org/apidoc/org/xml/sax/DTDHandler.html
67077          * IGNORED method of org.xml.sax.DTDHandler
67078          *  #notationDecl(name, publicId, systemId) {};
67079          *  #unparsedEntityDecl(name, publicId, systemId, notationName) {};
67080          */
67081         "endDTD,startEntity,endEntity,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,resolveEntity,getExternalSubset,notationDecl,unparsedEntityDecl".replace(/\w+/g,function(key){
67082                 DOMHandler.prototype[key] = function(){return null};
67083         });
67084
67085         /* 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 */
67086         function appendElement (hander,node) {
67087             if (!hander.currentElement) {
67088                 hander.doc.appendChild(node);
67089             } else {
67090                 hander.currentElement.appendChild(node);
67091             }
67092         }//appendChild and setAttributeNS are preformance key
67093
67094         //if(typeof require == 'function'){
67095                 var XMLReader = sax.XMLReader;
67096                 var DOMImplementation = exports.DOMImplementation = dom.DOMImplementation;
67097                 exports.XMLSerializer = dom.XMLSerializer ;
67098                 exports.DOMParser = DOMParser;
67099         //}
67100         });
67101
67102         var togeojson = createCommonjsModule(function (module, exports) {
67103         var toGeoJSON = (function() {
67104
67105             var removeSpace = /\s*/g,
67106                 trimSpace = /^\s*|\s*$/g,
67107                 splitSpace = /\s+/;
67108             // generate a short, numeric hash of a string
67109             function okhash(x) {
67110                 if (!x || !x.length) return 0;
67111                 for (var i = 0, h = 0; i < x.length; i++) {
67112                     h = ((h << 5) - h) + x.charCodeAt(i) | 0;
67113                 } return h;
67114             }
67115             // all Y children of X
67116             function get(x, y) { return x.getElementsByTagName(y); }
67117             function attr(x, y) { return x.getAttribute(y); }
67118             function attrf(x, y) { return parseFloat(attr(x, y)); }
67119             // one Y child of X, if any, otherwise null
67120             function get1(x, y) { var n = get(x, y); return n.length ? n[0] : null; }
67121             // https://developer.mozilla.org/en-US/docs/Web/API/Node.normalize
67122             function norm(el) { if (el.normalize) { el.normalize(); } return el; }
67123             // cast array x into numbers
67124             function numarray(x) {
67125                 for (var j = 0, o = []; j < x.length; j++) { o[j] = parseFloat(x[j]); }
67126                 return o;
67127             }
67128             // get the content of a text node, if any
67129             function nodeVal(x) {
67130                 if (x) { norm(x); }
67131                 return (x && x.textContent) || '';
67132             }
67133             // get the contents of multiple text nodes, if present
67134             function getMulti(x, ys) {
67135                 var o = {}, n, k;
67136                 for (k = 0; k < ys.length; k++) {
67137                     n = get1(x, ys[k]);
67138                     if (n) o[ys[k]] = nodeVal(n);
67139                 }
67140                 return o;
67141             }
67142             // add properties of Y to X, overwriting if present in both
67143             function extend(x, y) { for (var k in y) x[k] = y[k]; }
67144             // get one coordinate from a coordinate array, if any
67145             function coord1(v) { return numarray(v.replace(removeSpace, '').split(',')); }
67146             // get all coordinates from a coordinate array as [[],[]]
67147             function coord(v) {
67148                 var coords = v.replace(trimSpace, '').split(splitSpace),
67149                     o = [];
67150                 for (var i = 0; i < coords.length; i++) {
67151                     o.push(coord1(coords[i]));
67152                 }
67153                 return o;
67154             }
67155             function coordPair(x) {
67156                 var ll = [attrf(x, 'lon'), attrf(x, 'lat')],
67157                     ele = get1(x, 'ele'),
67158                     // handle namespaced attribute in browser
67159                     heartRate = get1(x, 'gpxtpx:hr') || get1(x, 'hr'),
67160                     time = get1(x, 'time'),
67161                     e;
67162                 if (ele) {
67163                     e = parseFloat(nodeVal(ele));
67164                     if (!isNaN(e)) {
67165                         ll.push(e);
67166                     }
67167                 }
67168                 return {
67169                     coordinates: ll,
67170                     time: time ? nodeVal(time) : null,
67171                     heartRate: heartRate ? parseFloat(nodeVal(heartRate)) : null
67172                 };
67173             }
67174
67175             // create a new feature collection parent object
67176             function fc() {
67177                 return {
67178                     type: 'FeatureCollection',
67179                     features: []
67180                 };
67181             }
67182
67183             var serializer;
67184             if (typeof XMLSerializer !== 'undefined') {
67185                 /* istanbul ignore next */
67186                 serializer = new XMLSerializer();
67187             // only require xmldom in a node environment
67188             } else if ( typeof process === 'object' && !process.browser) {
67189                 serializer = new (domParser.XMLSerializer)();
67190             }
67191             function xml2str(str) {
67192                 // IE9 will create a new XMLSerializer but it'll crash immediately.
67193                 // This line is ignored because we don't run coverage tests in IE9
67194                 /* istanbul ignore next */
67195                 if (str.xml !== undefined) return str.xml;
67196                 return serializer.serializeToString(str);
67197             }
67198
67199             var t = {
67200                 kml: function(doc) {
67201
67202                     var gj = fc(),
67203                         // styleindex keeps track of hashed styles in order to match features
67204                         styleIndex = {}, styleByHash = {},
67205                         // stylemapindex keeps track of style maps to expose in properties
67206                         styleMapIndex = {},
67207                         // atomic geospatial types supported by KML - MultiGeometry is
67208                         // handled separately
67209                         geotypes = ['Polygon', 'LineString', 'Point', 'Track', 'gx:Track'],
67210                         // all root placemarks in the file
67211                         placemarks = get(doc, 'Placemark'),
67212                         styles = get(doc, 'Style'),
67213                         styleMaps = get(doc, 'StyleMap');
67214
67215                     for (var k = 0; k < styles.length; k++) {
67216                         var hash = okhash(xml2str(styles[k])).toString(16);
67217                         styleIndex['#' + attr(styles[k], 'id')] = hash;
67218                         styleByHash[hash] = styles[k];
67219                     }
67220                     for (var l = 0; l < styleMaps.length; l++) {
67221                         styleIndex['#' + attr(styleMaps[l], 'id')] = okhash(xml2str(styleMaps[l])).toString(16);
67222                         var pairs = get(styleMaps[l], 'Pair');
67223                         var pairsMap = {};
67224                         for (var m = 0; m < pairs.length; m++) {
67225                             pairsMap[nodeVal(get1(pairs[m], 'key'))] = nodeVal(get1(pairs[m], 'styleUrl'));
67226                         }
67227                         styleMapIndex['#' + attr(styleMaps[l], 'id')] = pairsMap;
67228
67229                     }
67230                     for (var j = 0; j < placemarks.length; j++) {
67231                         gj.features = gj.features.concat(getPlacemark(placemarks[j]));
67232                     }
67233                     function kmlColor(v) {
67234                         var color, opacity;
67235                         v = v || '';
67236                         if (v.substr(0, 1) === '#') { v = v.substr(1); }
67237                         if (v.length === 6 || v.length === 3) { color = v; }
67238                         if (v.length === 8) {
67239                             opacity = parseInt(v.substr(0, 2), 16) / 255;
67240                             color = '#' + v.substr(6, 2) +
67241                                 v.substr(4, 2) +
67242                                 v.substr(2, 2);
67243                         }
67244                         return [color, isNaN(opacity) ? undefined : opacity];
67245                     }
67246                     function gxCoord(v) { return numarray(v.split(' ')); }
67247                     function gxCoords(root) {
67248                         var elems = get(root, 'coord'), coords = [], times = [];
67249                         if (elems.length === 0) elems = get(root, 'gx:coord');
67250                         for (var i = 0; i < elems.length; i++) coords.push(gxCoord(nodeVal(elems[i])));
67251                         var timeElems = get(root, 'when');
67252                         for (var j = 0; j < timeElems.length; j++) times.push(nodeVal(timeElems[j]));
67253                         return {
67254                             coords: coords,
67255                             times: times
67256                         };
67257                     }
67258                     function getGeometry(root) {
67259                         var geomNode, geomNodes, i, j, k, geoms = [], coordTimes = [];
67260                         if (get1(root, 'MultiGeometry')) { return getGeometry(get1(root, 'MultiGeometry')); }
67261                         if (get1(root, 'MultiTrack')) { return getGeometry(get1(root, 'MultiTrack')); }
67262                         if (get1(root, 'gx:MultiTrack')) { return getGeometry(get1(root, 'gx:MultiTrack')); }
67263                         for (i = 0; i < geotypes.length; i++) {
67264                             geomNodes = get(root, geotypes[i]);
67265                             if (geomNodes) {
67266                                 for (j = 0; j < geomNodes.length; j++) {
67267                                     geomNode = geomNodes[j];
67268                                     if (geotypes[i] === 'Point') {
67269                                         geoms.push({
67270                                             type: 'Point',
67271                                             coordinates: coord1(nodeVal(get1(geomNode, 'coordinates')))
67272                                         });
67273                                     } else if (geotypes[i] === 'LineString') {
67274                                         geoms.push({
67275                                             type: 'LineString',
67276                                             coordinates: coord(nodeVal(get1(geomNode, 'coordinates')))
67277                                         });
67278                                     } else if (geotypes[i] === 'Polygon') {
67279                                         var rings = get(geomNode, 'LinearRing'),
67280                                             coords = [];
67281                                         for (k = 0; k < rings.length; k++) {
67282                                             coords.push(coord(nodeVal(get1(rings[k], 'coordinates'))));
67283                                         }
67284                                         geoms.push({
67285                                             type: 'Polygon',
67286                                             coordinates: coords
67287                                         });
67288                                     } else if (geotypes[i] === 'Track' ||
67289                                         geotypes[i] === 'gx:Track') {
67290                                         var track = gxCoords(geomNode);
67291                                         geoms.push({
67292                                             type: 'LineString',
67293                                             coordinates: track.coords
67294                                         });
67295                                         if (track.times.length) coordTimes.push(track.times);
67296                                     }
67297                                 }
67298                             }
67299                         }
67300                         return {
67301                             geoms: geoms,
67302                             coordTimes: coordTimes
67303                         };
67304                     }
67305                     function getPlacemark(root) {
67306                         var geomsAndTimes = getGeometry(root), i, properties = {},
67307                             name = nodeVal(get1(root, 'name')),
67308                             address = nodeVal(get1(root, 'address')),
67309                             styleUrl = nodeVal(get1(root, 'styleUrl')),
67310                             description = nodeVal(get1(root, 'description')),
67311                             timeSpan = get1(root, 'TimeSpan'),
67312                             timeStamp = get1(root, 'TimeStamp'),
67313                             extendedData = get1(root, 'ExtendedData'),
67314                             lineStyle = get1(root, 'LineStyle'),
67315                             polyStyle = get1(root, 'PolyStyle'),
67316                             visibility = get1(root, 'visibility');
67317
67318                         if (!geomsAndTimes.geoms.length) return [];
67319                         if (name) properties.name = name;
67320                         if (address) properties.address = address;
67321                         if (styleUrl) {
67322                             if (styleUrl[0] !== '#') {
67323                                 styleUrl = '#' + styleUrl;
67324                             }
67325
67326                             properties.styleUrl = styleUrl;
67327                             if (styleIndex[styleUrl]) {
67328                                 properties.styleHash = styleIndex[styleUrl];
67329                             }
67330                             if (styleMapIndex[styleUrl]) {
67331                                 properties.styleMapHash = styleMapIndex[styleUrl];
67332                                 properties.styleHash = styleIndex[styleMapIndex[styleUrl].normal];
67333                             }
67334                             // Try to populate the lineStyle or polyStyle since we got the style hash
67335                             var style = styleByHash[properties.styleHash];
67336                             if (style) {
67337                                 if (!lineStyle) lineStyle = get1(style, 'LineStyle');
67338                                 if (!polyStyle) polyStyle = get1(style, 'PolyStyle');
67339                             }
67340                         }
67341                         if (description) properties.description = description;
67342                         if (timeSpan) {
67343                             var begin = nodeVal(get1(timeSpan, 'begin'));
67344                             var end = nodeVal(get1(timeSpan, 'end'));
67345                             properties.timespan = { begin: begin, end: end };
67346                         }
67347                         if (timeStamp) {
67348                             properties.timestamp = nodeVal(get1(timeStamp, 'when'));
67349                         }
67350                         if (lineStyle) {
67351                             var linestyles = kmlColor(nodeVal(get1(lineStyle, 'color'))),
67352                                 color = linestyles[0],
67353                                 opacity = linestyles[1],
67354                                 width = parseFloat(nodeVal(get1(lineStyle, 'width')));
67355                             if (color) properties.stroke = color;
67356                             if (!isNaN(opacity)) properties['stroke-opacity'] = opacity;
67357                             if (!isNaN(width)) properties['stroke-width'] = width;
67358                         }
67359                         if (polyStyle) {
67360                             var polystyles = kmlColor(nodeVal(get1(polyStyle, 'color'))),
67361                                 pcolor = polystyles[0],
67362                                 popacity = polystyles[1],
67363                                 fill = nodeVal(get1(polyStyle, 'fill')),
67364                                 outline = nodeVal(get1(polyStyle, 'outline'));
67365                             if (pcolor) properties.fill = pcolor;
67366                             if (!isNaN(popacity)) properties['fill-opacity'] = popacity;
67367                             if (fill) properties['fill-opacity'] = fill === '1' ? properties['fill-opacity'] || 1 : 0;
67368                             if (outline) properties['stroke-opacity'] = outline === '1' ? properties['stroke-opacity'] || 1 : 0;
67369                         }
67370                         if (extendedData) {
67371                             var datas = get(extendedData, 'Data'),
67372                                 simpleDatas = get(extendedData, 'SimpleData');
67373
67374                             for (i = 0; i < datas.length; i++) {
67375                                 properties[datas[i].getAttribute('name')] = nodeVal(get1(datas[i], 'value'));
67376                             }
67377                             for (i = 0; i < simpleDatas.length; i++) {
67378                                 properties[simpleDatas[i].getAttribute('name')] = nodeVal(simpleDatas[i]);
67379                             }
67380                         }
67381                         if (visibility) {
67382                             properties.visibility = nodeVal(visibility);
67383                         }
67384                         if (geomsAndTimes.coordTimes.length) {
67385                             properties.coordTimes = (geomsAndTimes.coordTimes.length === 1) ?
67386                                 geomsAndTimes.coordTimes[0] : geomsAndTimes.coordTimes;
67387                         }
67388                         var feature = {
67389                             type: 'Feature',
67390                             geometry: (geomsAndTimes.geoms.length === 1) ? geomsAndTimes.geoms[0] : {
67391                                 type: 'GeometryCollection',
67392                                 geometries: geomsAndTimes.geoms
67393                             },
67394                             properties: properties
67395                         };
67396                         if (attr(root, 'id')) feature.id = attr(root, 'id');
67397                         return [feature];
67398                     }
67399                     return gj;
67400                 },
67401                 gpx: function(doc) {
67402                     var i,
67403                         tracks = get(doc, 'trk'),
67404                         routes = get(doc, 'rte'),
67405                         waypoints = get(doc, 'wpt'),
67406                         // a feature collection
67407                         gj = fc(),
67408                         feature;
67409                     for (i = 0; i < tracks.length; i++) {
67410                         feature = getTrack(tracks[i]);
67411                         if (feature) gj.features.push(feature);
67412                     }
67413                     for (i = 0; i < routes.length; i++) {
67414                         feature = getRoute(routes[i]);
67415                         if (feature) gj.features.push(feature);
67416                     }
67417                     for (i = 0; i < waypoints.length; i++) {
67418                         gj.features.push(getPoint(waypoints[i]));
67419                     }
67420                     function getPoints(node, pointname) {
67421                         var pts = get(node, pointname),
67422                             line = [],
67423                             times = [],
67424                             heartRates = [],
67425                             l = pts.length;
67426                         if (l < 2) return {};  // Invalid line in GeoJSON
67427                         for (var i = 0; i < l; i++) {
67428                             var c = coordPair(pts[i]);
67429                             line.push(c.coordinates);
67430                             if (c.time) times.push(c.time);
67431                             if (c.heartRate) heartRates.push(c.heartRate);
67432                         }
67433                         return {
67434                             line: line,
67435                             times: times,
67436                             heartRates: heartRates
67437                         };
67438                     }
67439                     function getTrack(node) {
67440                         var segments = get(node, 'trkseg'),
67441                             track = [],
67442                             times = [],
67443                             heartRates = [],
67444                             line;
67445                         for (var i = 0; i < segments.length; i++) {
67446                             line = getPoints(segments[i], 'trkpt');
67447                             if (line) {
67448                                 if (line.line) track.push(line.line);
67449                                 if (line.times && line.times.length) times.push(line.times);
67450                                 if (line.heartRates && line.heartRates.length) heartRates.push(line.heartRates);
67451                             }
67452                         }
67453                         if (track.length === 0) return;
67454                         var properties = getProperties(node);
67455                         extend(properties, getLineStyle(get1(node, 'extensions')));
67456                         if (times.length) properties.coordTimes = track.length === 1 ? times[0] : times;
67457                         if (heartRates.length) properties.heartRates = track.length === 1 ? heartRates[0] : heartRates;
67458                         return {
67459                             type: 'Feature',
67460                             properties: properties,
67461                             geometry: {
67462                                 type: track.length === 1 ? 'LineString' : 'MultiLineString',
67463                                 coordinates: track.length === 1 ? track[0] : track
67464                             }
67465                         };
67466                     }
67467                     function getRoute(node) {
67468                         var line = getPoints(node, 'rtept');
67469                         if (!line.line) return;
67470                         var prop = getProperties(node);
67471                         extend(prop, getLineStyle(get1(node, 'extensions')));
67472                         var routeObj = {
67473                             type: 'Feature',
67474                             properties: prop,
67475                             geometry: {
67476                                 type: 'LineString',
67477                                 coordinates: line.line
67478                             }
67479                         };
67480                         return routeObj;
67481                     }
67482                     function getPoint(node) {
67483                         var prop = getProperties(node);
67484                         extend(prop, getMulti(node, ['sym']));
67485                         return {
67486                             type: 'Feature',
67487                             properties: prop,
67488                             geometry: {
67489                                 type: 'Point',
67490                                 coordinates: coordPair(node).coordinates
67491                             }
67492                         };
67493                     }
67494                     function getLineStyle(extensions) {
67495                         var style = {};
67496                         if (extensions) {
67497                             var lineStyle = get1(extensions, 'line');
67498                             if (lineStyle) {
67499                                 var color = nodeVal(get1(lineStyle, 'color')),
67500                                     opacity = parseFloat(nodeVal(get1(lineStyle, 'opacity'))),
67501                                     width = parseFloat(nodeVal(get1(lineStyle, 'width')));
67502                                 if (color) style.stroke = color;
67503                                 if (!isNaN(opacity)) style['stroke-opacity'] = opacity;
67504                                 // GPX width is in mm, convert to px with 96 px per inch
67505                                 if (!isNaN(width)) style['stroke-width'] = width * 96 / 25.4;
67506                             }
67507                         }
67508                         return style;
67509                     }
67510                     function getProperties(node) {
67511                         var prop = getMulti(node, ['name', 'cmt', 'desc', 'type', 'time', 'keywords']),
67512                             links = get(node, 'link');
67513                         if (links.length) prop.links = [];
67514                         for (var i = 0, link; i < links.length; i++) {
67515                             link = { href: attr(links[i], 'href') };
67516                             extend(link, getMulti(links[i], ['text', 'type']));
67517                             prop.links.push(link);
67518                         }
67519                         return prop;
67520                     }
67521                     return gj;
67522                 }
67523             };
67524             return t;
67525         })();
67526
67527         module.exports = toGeoJSON;
67528         });
67529
67530         var _initialized = false;
67531         var _enabled = false;
67532         var _geojson;
67533
67534
67535         function svgData(projection, context, dispatch) {
67536             var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000);
67537             var _showLabels = true;
67538             var detected = utilDetect();
67539             var layer = select(null);
67540             var _vtService;
67541             var _fileList;
67542             var _template;
67543             var _src;
67544
67545
67546             function init() {
67547                 if (_initialized) return;  // run once
67548
67549                 _geojson = {};
67550                 _enabled = true;
67551
67552                 function over() {
67553                     event.stopPropagation();
67554                     event.preventDefault();
67555                     event.dataTransfer.dropEffect = 'copy';
67556                 }
67557
67558                 context.container()
67559                     .attr('dropzone', 'copy')
67560                     .on('drop.svgData', function() {
67561                         event.stopPropagation();
67562                         event.preventDefault();
67563                         if (!detected.filedrop) return;
67564                         drawData.fileList(event.dataTransfer.files);
67565                     })
67566                     .on('dragenter.svgData', over)
67567                     .on('dragexit.svgData', over)
67568                     .on('dragover.svgData', over);
67569
67570                 _initialized = true;
67571             }
67572
67573
67574             function getService() {
67575                 if (services.vectorTile && !_vtService) {
67576                     _vtService = services.vectorTile;
67577                     _vtService.event.on('loadedData', throttledRedraw);
67578                 } else if (!services.vectorTile && _vtService) {
67579                     _vtService = null;
67580                 }
67581
67582                 return _vtService;
67583             }
67584
67585
67586             function showLayer() {
67587                 layerOn();
67588
67589                 layer
67590                     .style('opacity', 0)
67591                     .transition()
67592                     .duration(250)
67593                     .style('opacity', 1)
67594                     .on('end', function () { dispatch.call('change'); });
67595             }
67596
67597
67598             function hideLayer() {
67599                 throttledRedraw.cancel();
67600
67601                 layer
67602                     .transition()
67603                     .duration(250)
67604                     .style('opacity', 0)
67605                     .on('end', layerOff);
67606             }
67607
67608
67609             function layerOn() {
67610                 layer.style('display', 'block');
67611             }
67612
67613
67614             function layerOff() {
67615                 layer.selectAll('.viewfield-group').remove();
67616                 layer.style('display', 'none');
67617             }
67618
67619
67620             // ensure that all geojson features in a collection have IDs
67621             function ensureIDs(gj) {
67622                 if (!gj) return null;
67623
67624                 if (gj.type === 'FeatureCollection') {
67625                     for (var i = 0; i < gj.features.length; i++) {
67626                         ensureFeatureID(gj.features[i]);
67627                     }
67628                 } else {
67629                     ensureFeatureID(gj);
67630                 }
67631                 return gj;
67632             }
67633
67634
67635             // ensure that each single Feature object has a unique ID
67636             function ensureFeatureID(feature) {
67637                 if (!feature) return;
67638                 feature.__featurehash__ = utilHashcode(fastJsonStableStringify(feature));
67639                 return feature;
67640             }
67641
67642
67643             // Prefer an array of Features instead of a FeatureCollection
67644             function getFeatures(gj) {
67645                 if (!gj) return [];
67646
67647                 if (gj.type === 'FeatureCollection') {
67648                     return gj.features;
67649                 } else {
67650                     return [gj];
67651                 }
67652             }
67653
67654
67655             function featureKey(d) {
67656                 return d.__featurehash__;
67657             }
67658
67659
67660             function isPolygon(d) {
67661                 return d.geometry.type === 'Polygon' || d.geometry.type === 'MultiPolygon';
67662             }
67663
67664
67665             function clipPathID(d) {
67666                 return 'ideditor-data-' + d.__featurehash__ + '-clippath';
67667             }
67668
67669
67670             function featureClasses(d) {
67671                 return [
67672                     'data' + d.__featurehash__,
67673                     d.geometry.type,
67674                     isPolygon(d) ? 'area' : '',
67675                     d.__layerID__ || ''
67676                 ].filter(Boolean).join(' ');
67677             }
67678
67679
67680             function drawData(selection) {
67681                 var vtService = getService();
67682                 var getPath = svgPath(projection).geojson;
67683                 var getAreaPath = svgPath(projection, null, true).geojson;
67684                 var hasData = drawData.hasData();
67685
67686                 layer = selection.selectAll('.layer-mapdata')
67687                     .data(_enabled && hasData ? [0] : []);
67688
67689                 layer.exit()
67690                     .remove();
67691
67692                 layer = layer.enter()
67693                     .append('g')
67694                     .attr('class', 'layer-mapdata')
67695                     .merge(layer);
67696
67697                 var surface = context.surface();
67698                 if (!surface || surface.empty()) return;  // not ready to draw yet, starting up
67699
67700
67701                 // Gather data
67702                 var geoData, polygonData;
67703                 if (_template && vtService) {   // fetch data from vector tile service
67704                     var sourceID = _template;
67705                     vtService.loadTiles(sourceID, _template, projection);
67706                     geoData = vtService.data(sourceID, projection);
67707                 } else {
67708                     geoData = getFeatures(_geojson);
67709                 }
67710                 geoData = geoData.filter(getPath);
67711                 polygonData = geoData.filter(isPolygon);
67712
67713
67714                 // Draw clip paths for polygons
67715                 var clipPaths = surface.selectAll('defs').selectAll('.clipPath-data')
67716                    .data(polygonData, featureKey);
67717
67718                 clipPaths.exit()
67719                    .remove();
67720
67721                 var clipPathsEnter = clipPaths.enter()
67722                    .append('clipPath')
67723                    .attr('class', 'clipPath-data')
67724                    .attr('id', clipPathID);
67725
67726                 clipPathsEnter
67727                    .append('path');
67728
67729                 clipPaths.merge(clipPathsEnter)
67730                    .selectAll('path')
67731                    .attr('d', getAreaPath);
67732
67733
67734                 // Draw fill, shadow, stroke layers
67735                 var datagroups = layer
67736                     .selectAll('g.datagroup')
67737                     .data(['fill', 'shadow', 'stroke']);
67738
67739                 datagroups = datagroups.enter()
67740                     .append('g')
67741                     .attr('class', function(d) { return 'datagroup datagroup-' + d; })
67742                     .merge(datagroups);
67743
67744
67745                 // Draw paths
67746                 var pathData = {
67747                     fill: polygonData,
67748                     shadow: geoData,
67749                     stroke: geoData
67750                 };
67751
67752                 var paths = datagroups
67753                     .selectAll('path')
67754                     .data(function(layer) { return pathData[layer]; }, featureKey);
67755
67756                 // exit
67757                 paths.exit()
67758                     .remove();
67759
67760                 // enter/update
67761                 paths = paths.enter()
67762                     .append('path')
67763                     .attr('class', function(d) {
67764                         var datagroup = this.parentNode.__data__;
67765                         return 'pathdata ' + datagroup + ' ' + featureClasses(d);
67766                     })
67767                     .attr('clip-path', function(d) {
67768                         var datagroup = this.parentNode.__data__;
67769                         return datagroup === 'fill' ? ('url(#' + clipPathID(d) + ')') : null;
67770                     })
67771                     .merge(paths)
67772                     .attr('d', function(d) {
67773                         var datagroup = this.parentNode.__data__;
67774                         return datagroup === 'fill' ? getAreaPath(d) : getPath(d);
67775                     });
67776
67777
67778                 // Draw labels
67779                 layer
67780                     .call(drawLabels, 'label-halo', geoData)
67781                     .call(drawLabels, 'label', geoData);
67782
67783
67784                 function drawLabels(selection, textClass, data) {
67785                     var labelPath = d3_geoPath(projection);
67786                     var labelData = data.filter(function(d) {
67787                         return _showLabels && d.properties && (d.properties.desc || d.properties.name);
67788                     });
67789
67790                     var labels = selection.selectAll('text.' + textClass)
67791                         .data(labelData, featureKey);
67792
67793                     // exit
67794                     labels.exit()
67795                         .remove();
67796
67797                     // enter/update
67798                     labels = labels.enter()
67799                         .append('text')
67800                         .attr('class', function(d) { return textClass + ' ' + featureClasses(d); })
67801                         .merge(labels)
67802                         .text(function(d) {
67803                             return d.properties.desc || d.properties.name;
67804                         })
67805                         .attr('x', function(d) {
67806                             var centroid = labelPath.centroid(d);
67807                             return centroid[0] + 11;
67808                         })
67809                         .attr('y', function(d) {
67810                             var centroid = labelPath.centroid(d);
67811                             return centroid[1];
67812                         });
67813                 }
67814             }
67815
67816
67817             function getExtension(fileName) {
67818                 if (!fileName) return;
67819
67820                 var re = /\.(gpx|kml|(geo)?json)$/i;
67821                 var match = fileName.toLowerCase().match(re);
67822                 return match && match.length && match[0];
67823             }
67824
67825
67826             function xmlToDom(textdata) {
67827                 return (new DOMParser()).parseFromString(textdata, 'text/xml');
67828             }
67829
67830
67831             drawData.setFile = function(extension, data) {
67832                 _template = null;
67833                 _fileList = null;
67834                 _geojson = null;
67835                 _src = null;
67836
67837                 var gj;
67838                 switch (extension) {
67839                     case '.gpx':
67840                         gj = togeojson.gpx(xmlToDom(data));
67841                         break;
67842                     case '.kml':
67843                         gj = togeojson.kml(xmlToDom(data));
67844                         break;
67845                     case '.geojson':
67846                     case '.json':
67847                         gj = JSON.parse(data);
67848                         break;
67849                 }
67850
67851                 gj = gj || {};
67852                 if (Object.keys(gj).length) {
67853                     _geojson = ensureIDs(gj);
67854                     _src = extension + ' data file';
67855                     this.fitZoom();
67856                 }
67857
67858                 dispatch.call('change');
67859                 return this;
67860             };
67861
67862
67863             drawData.showLabels = function(val) {
67864                 if (!arguments.length) return _showLabels;
67865
67866                 _showLabels = val;
67867                 return this;
67868             };
67869
67870
67871             drawData.enabled = function(val) {
67872                 if (!arguments.length) return _enabled;
67873
67874                 _enabled = val;
67875                 if (_enabled) {
67876                     showLayer();
67877                 } else {
67878                     hideLayer();
67879                 }
67880
67881                 dispatch.call('change');
67882                 return this;
67883             };
67884
67885
67886             drawData.hasData = function() {
67887                 var gj = _geojson || {};
67888                 return !!(_template || Object.keys(gj).length);
67889             };
67890
67891
67892             drawData.template = function(val, src) {
67893                 if (!arguments.length) return _template;
67894
67895                 // test source against OSM imagery blacklists..
67896                 var osm = context.connection();
67897                 if (osm) {
67898                     var blacklists = osm.imageryBlacklists();
67899                     var fail = false;
67900                     var tested = 0;
67901                     var regex;
67902
67903                     for (var i = 0; i < blacklists.length; i++) {
67904                         try {
67905                             regex = new RegExp(blacklists[i]);
67906                             fail = regex.test(val);
67907                             tested++;
67908                             if (fail) break;
67909                         } catch (e) {
67910                             /* noop */
67911                         }
67912                     }
67913
67914                     // ensure at least one test was run.
67915                     if (!tested) {
67916                         regex = new RegExp('.*\.google(apis)?\..*/(vt|kh)[\?/].*([xyz]=.*){3}.*');
67917                         fail = regex.test(val);
67918                     }
67919                 }
67920
67921                 _template = val;
67922                 _fileList = null;
67923                 _geojson = null;
67924
67925                 // strip off the querystring/hash from the template,
67926                 // it often includes the access token
67927                 _src = src || ('vectortile:' + val.split(/[?#]/)[0]);
67928
67929                 dispatch.call('change');
67930                 return this;
67931             };
67932
67933
67934             drawData.geojson = function(gj, src) {
67935                 if (!arguments.length) return _geojson;
67936
67937                 _template = null;
67938                 _fileList = null;
67939                 _geojson = null;
67940                 _src = null;
67941
67942                 gj = gj || {};
67943                 if (Object.keys(gj).length) {
67944                     _geojson = ensureIDs(gj);
67945                     _src = src || 'unknown.geojson';
67946                 }
67947
67948                 dispatch.call('change');
67949                 return this;
67950             };
67951
67952
67953             drawData.fileList = function(fileList) {
67954                 if (!arguments.length) return _fileList;
67955
67956                 _template = null;
67957                 _fileList = fileList;
67958                 _geojson = null;
67959                 _src = null;
67960
67961                 if (!fileList || !fileList.length) return this;
67962                 var f = fileList[0];
67963                 var extension = getExtension(f.name);
67964                 var reader = new FileReader();
67965                 reader.onload = (function() {
67966                     return function(e) {
67967                         drawData.setFile(extension, e.target.result);
67968                     };
67969                 })();
67970
67971                 reader.readAsText(f);
67972
67973                 return this;
67974             };
67975
67976
67977             drawData.url = function(url, defaultExtension) {
67978                 _template = null;
67979                 _fileList = null;
67980                 _geojson = null;
67981                 _src = null;
67982
67983                 // strip off any querystring/hash from the url before checking extension
67984                 var testUrl = url.split(/[?#]/)[0];
67985                 var extension = getExtension(testUrl) || defaultExtension;
67986                 if (extension) {
67987                     _template = null;
67988                     d3_text(url)
67989                         .then(function(data) {
67990                             drawData.setFile(extension, data);
67991                         })
67992                         .catch(function() {
67993                             /* ignore */
67994                         });
67995
67996                 } else {
67997                     drawData.template(url);
67998                 }
67999
68000                 return this;
68001             };
68002
68003
68004             drawData.getSrc = function() {
68005                 return _src || '';
68006             };
68007
68008
68009             drawData.fitZoom = function() {
68010                 var features = getFeatures(_geojson);
68011                 if (!features.length) return;
68012
68013                 var map = context.map();
68014                 var viewport = map.trimmedExtent().polygon();
68015                 var coords = features.reduce(function(coords, feature) {
68016                     var geom = feature.geometry;
68017                     if (!geom) return coords;
68018
68019                     var c = geom.coordinates;
68020
68021                     /* eslint-disable no-fallthrough */
68022                     switch (geom.type) {
68023                         case 'Point':
68024                             c = [c];
68025                         case 'MultiPoint':
68026                         case 'LineString':
68027                             break;
68028
68029                         case 'MultiPolygon':
68030                             c = utilArrayFlatten(c);
68031                         case 'Polygon':
68032                         case 'MultiLineString':
68033                             c = utilArrayFlatten(c);
68034                             break;
68035                     }
68036                     /* eslint-enable no-fallthrough */
68037
68038                     return utilArrayUnion(coords, c);
68039                 }, []);
68040
68041                 if (!geoPolygonIntersectsPolygon(viewport, coords, true)) {
68042                     var extent = geoExtent(d3_geoBounds({ type: 'LineString', coordinates: coords }));
68043                     map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
68044                 }
68045
68046                 return this;
68047             };
68048
68049
68050             init();
68051             return drawData;
68052         }
68053
68054         function svgDebug(projection, context) {
68055
68056           function drawDebug(selection) {
68057             const showTile = context.getDebug('tile');
68058             const showCollision = context.getDebug('collision');
68059             const showImagery = context.getDebug('imagery');
68060             const showTouchTargets = context.getDebug('target');
68061             const showDownloaded = context.getDebug('downloaded');
68062
68063             let debugData = [];
68064             if (showTile) {
68065               debugData.push({ class: 'red', label: 'tile' });
68066             }
68067             if (showCollision) {
68068               debugData.push({ class: 'yellow', label: 'collision' });
68069             }
68070             if (showImagery) {
68071               debugData.push({ class: 'orange', label: 'imagery' });
68072             }
68073             if (showTouchTargets) {
68074               debugData.push({ class: 'pink', label: 'touchTargets' });
68075             }
68076             if (showDownloaded) {
68077               debugData.push({ class: 'purple', label: 'downloaded' });
68078             }
68079
68080
68081             let legend = context.container().select('.main-content')
68082               .selectAll('.debug-legend')
68083               .data(debugData.length ? [0] : []);
68084
68085             legend.exit()
68086               .remove();
68087
68088             legend = legend.enter()
68089               .append('div')
68090               .attr('class', 'fillD debug-legend')
68091               .merge(legend);
68092
68093
68094             let legendItems = legend.selectAll('.debug-legend-item')
68095               .data(debugData, d => d.label);
68096
68097             legendItems.exit()
68098               .remove();
68099
68100             legendItems.enter()
68101               .append('span')
68102               .attr('class', d => `debug-legend-item ${d.class}`)
68103               .text(d => d.label);
68104
68105
68106             let layer = selection.selectAll('.layer-debug')
68107               .data(showImagery || showDownloaded ? [0] : []);
68108
68109             layer.exit()
68110               .remove();
68111
68112             layer = layer.enter()
68113               .append('g')
68114               .attr('class', 'layer-debug')
68115               .merge(layer);
68116
68117
68118             // imagery
68119             const extent = context.map().extent();
68120             _mainFileFetcher.get('imagery')
68121               .then(d => {
68122                 const hits = (showImagery && d.query.bbox(extent.rectangle(), true)) || [];
68123                 const features = hits.map(d => d.features[d.id]);
68124
68125                 let imagery = layer.selectAll('path.debug-imagery')
68126                   .data(features);
68127
68128                 imagery.exit()
68129                   .remove();
68130
68131                 imagery.enter()
68132                   .append('path')
68133                   .attr('class', 'debug-imagery debug orange');
68134               })
68135               .catch(() => { /* ignore */ });
68136
68137             // downloaded
68138             const osm = context.connection();
68139             let dataDownloaded = [];
68140             if (osm && showDownloaded) {
68141               const rtree = osm.caches('get').tile.rtree;
68142               dataDownloaded = rtree.all().map(bbox => {
68143                 return {
68144                   type: 'Feature',
68145                   properties: { id: bbox.id },
68146                   geometry: {
68147                     type: 'Polygon',
68148                     coordinates: [[
68149                       [ bbox.minX, bbox.minY ],
68150                       [ bbox.minX, bbox.maxY ],
68151                       [ bbox.maxX, bbox.maxY ],
68152                       [ bbox.maxX, bbox.minY ],
68153                       [ bbox.minX, bbox.minY ]
68154                     ]]
68155                   }
68156                 };
68157               });
68158             }
68159
68160             let downloaded = layer
68161               .selectAll('path.debug-downloaded')
68162               .data(showDownloaded ? dataDownloaded : []);
68163
68164             downloaded.exit()
68165               .remove();
68166
68167             downloaded.enter()
68168               .append('path')
68169               .attr('class', 'debug-downloaded debug purple');
68170
68171             // update
68172             layer.selectAll('path')
68173               .attr('d', svgPath(projection).geojson);
68174           }
68175
68176
68177           // This looks strange because `enabled` methods on other layers are
68178           // chainable getter/setters, and this one is just a getter.
68179           drawDebug.enabled = function() {
68180             if (!arguments.length) {
68181               return context.getDebug('tile') ||
68182                 context.getDebug('collision') ||
68183                 context.getDebug('imagery') ||
68184                 context.getDebug('target') ||
68185                 context.getDebug('downloaded');
68186             } else {
68187                 return this;
68188             }
68189           };
68190
68191
68192           return drawDebug;
68193         }
68194
68195         let _layerEnabled = false;
68196         let _qaService;
68197
68198         function svgKeepRight(projection, context, dispatch) {
68199           const throttledRedraw = throttle(() => dispatch.call('change'), 1000);
68200           const minZoom = 12;
68201
68202           let touchLayer = select(null);
68203           let drawLayer = select(null);
68204           let layerVisible = false;
68205
68206           function markerPath(selection, klass) {
68207             selection
68208               .attr('class', klass)
68209               .attr('transform', 'translate(-4, -24)')
68210               .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');
68211           }
68212
68213           // Loosely-coupled keepRight service for fetching issues.
68214           function getService() {
68215             if (services.keepRight && !_qaService) {
68216               _qaService = services.keepRight;
68217               _qaService.on('loaded', throttledRedraw);
68218             } else if (!services.keepRight && _qaService) {
68219               _qaService = null;
68220             }
68221
68222             return _qaService;
68223           }
68224
68225           // Show the markers
68226           function editOn() {
68227             if (!layerVisible) {
68228               layerVisible = true;
68229               drawLayer
68230                 .style('display', 'block');
68231             }
68232           }
68233
68234           // Immediately remove the markers and their touch targets
68235           function editOff() {
68236             if (layerVisible) {
68237               layerVisible = false;
68238               drawLayer
68239                 .style('display', 'none');
68240               drawLayer.selectAll('.qaItem.keepRight')
68241                 .remove();
68242               touchLayer.selectAll('.qaItem.keepRight')
68243                 .remove();
68244             }
68245           }
68246
68247           // Enable the layer.  This shows the markers and transitions them to visible.
68248           function layerOn() {
68249             editOn();
68250
68251             drawLayer
68252               .style('opacity', 0)
68253               .transition()
68254               .duration(250)
68255               .style('opacity', 1)
68256               .on('end interrupt', () => dispatch.call('change'));
68257           }
68258
68259           // Disable the layer.  This transitions the layer invisible and then hides the markers.
68260           function layerOff() {
68261             throttledRedraw.cancel();
68262             drawLayer.interrupt();
68263             touchLayer.selectAll('.qaItem.keepRight')
68264               .remove();
68265
68266             drawLayer
68267               .transition()
68268               .duration(250)
68269               .style('opacity', 0)
68270               .on('end interrupt', () => {
68271                 editOff();
68272                 dispatch.call('change');
68273               });
68274           }
68275
68276           // Update the issue markers
68277           function updateMarkers() {
68278             if (!layerVisible || !_layerEnabled) return;
68279
68280             const service = getService();
68281             const selectedID = context.selectedErrorID();
68282             const data = (service ? service.getItems(projection) : []);
68283             const getTransform = svgPointTransform(projection);
68284
68285             // Draw markers..
68286             const markers = drawLayer.selectAll('.qaItem.keepRight')
68287               .data(data, d => d.id);
68288
68289             // exit
68290             markers.exit()
68291               .remove();
68292
68293             // enter
68294             const markersEnter = markers.enter()
68295               .append('g')
68296                 .attr('class', d => `qaItem ${d.service} itemId-${d.id} itemType-${d.parentIssueType}`);
68297
68298             markersEnter
68299               .append('ellipse')
68300                 .attr('cx', 0.5)
68301                 .attr('cy', 1)
68302                 .attr('rx', 6.5)
68303                 .attr('ry', 3)
68304                 .attr('class', 'stroke');
68305
68306             markersEnter
68307               .append('path')
68308                 .call(markerPath, 'shadow');
68309
68310             markersEnter
68311               .append('use')
68312                 .attr('class', 'qaItem-fill')
68313                 .attr('width', '20px')
68314                 .attr('height', '20px')
68315                 .attr('x', '-8px')
68316                 .attr('y', '-22px')
68317                 .attr('xlink:href', '#iD-icon-bolt');
68318
68319             // update
68320             markers
68321               .merge(markersEnter)
68322               .sort(sortY)
68323                 .classed('selected', d => d.id === selectedID)
68324                 .attr('transform', getTransform);
68325
68326
68327             // Draw targets..
68328             if (touchLayer.empty()) return;
68329             const fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
68330
68331             const targets = touchLayer.selectAll('.qaItem.keepRight')
68332               .data(data, d => d.id);
68333
68334             // exit
68335             targets.exit()
68336               .remove();
68337
68338             // enter/update
68339             targets.enter()
68340               .append('rect')
68341                 .attr('width', '20px')
68342                 .attr('height', '20px')
68343                 .attr('x', '-8px')
68344                 .attr('y', '-22px')
68345               .merge(targets)
68346               .sort(sortY)
68347                 .attr('class', d => `qaItem ${d.service} target ${fillClass} itemId-${d.id}`)
68348                 .attr('transform', getTransform);
68349
68350
68351             function sortY(a, b) {
68352               return (a.id === selectedID) ? 1
68353                 : (b.id === selectedID) ? -1
68354                 : (a.severity === 'error' && b.severity !== 'error') ? 1
68355                 : (b.severity === 'error' && a.severity !== 'error') ? -1
68356                 : b.loc[1] - a.loc[1];
68357             }
68358           }
68359
68360           // Draw the keepRight layer and schedule loading issues and updating markers.
68361           function drawKeepRight(selection) {
68362             const service = getService();
68363
68364             const surface = context.surface();
68365             if (surface && !surface.empty()) {
68366               touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
68367             }
68368
68369             drawLayer = selection.selectAll('.layer-keepRight')
68370               .data(service ? [0] : []);
68371
68372             drawLayer.exit()
68373               .remove();
68374
68375             drawLayer = drawLayer.enter()
68376               .append('g')
68377                 .attr('class', 'layer-keepRight')
68378                 .style('display', _layerEnabled ? 'block' : 'none')
68379               .merge(drawLayer);
68380
68381             if (_layerEnabled) {
68382               if (service && ~~context.map().zoom() >= minZoom) {
68383                 editOn();
68384                 service.loadIssues(projection);
68385                 updateMarkers();
68386               } else {
68387                 editOff();
68388               }
68389             }
68390           }
68391
68392           // Toggles the layer on and off
68393           drawKeepRight.enabled = function(val) {
68394             if (!arguments.length) return _layerEnabled;
68395
68396             _layerEnabled = val;
68397             if (_layerEnabled) {
68398               layerOn();
68399             } else {
68400               layerOff();
68401               if (context.selectedErrorID()) {
68402                 context.enter(modeBrowse(context));
68403               }
68404             }
68405
68406             dispatch.call('change');
68407             return this;
68408           };
68409
68410           drawKeepRight.supported = () => !!getService();
68411
68412           return drawKeepRight;
68413         }
68414
68415         function svgGeolocate(projection) {
68416             var layer = select(null);
68417             var _position;
68418
68419
68420             function init() {
68421                 if (svgGeolocate.initialized) return;  // run once
68422                 svgGeolocate.enabled = false;
68423                 svgGeolocate.initialized = true;
68424             }
68425
68426             function showLayer() {
68427                 layer.style('display', 'block');
68428             }
68429
68430
68431             function hideLayer() {
68432                 layer
68433                     .transition()
68434                     .duration(250)
68435                     .style('opacity', 0);
68436             }
68437
68438             function layerOn() {
68439                 layer
68440                     .style('opacity', 0)
68441                     .transition()
68442                     .duration(250)
68443                     .style('opacity', 1);
68444
68445             }
68446
68447             function layerOff() {
68448                 layer.style('display', 'none');
68449             }
68450
68451             function transform(d) {
68452                 return svgPointTransform(projection)(d);
68453             }
68454
68455             function accuracy(accuracy, loc) { // converts accuracy to pixels...
68456                 var degreesRadius = geoMetersToLat(accuracy),
68457                     tangentLoc = [loc[0], loc[1] + degreesRadius],
68458                     projectedTangent = projection(tangentLoc),
68459                     projectedLoc = projection([loc[0], loc[1]]);
68460
68461                 // southern most point will have higher pixel value...
68462                return Math.round(projectedLoc[1] - projectedTangent[1]).toString();
68463             }
68464
68465             function update() {
68466                 var geolocation = { loc: [_position.coords.longitude, _position.coords.latitude] };
68467
68468                 var groups = layer.selectAll('.geolocations').selectAll('.geolocation')
68469                     .data([geolocation]);
68470
68471                 groups.exit()
68472                     .remove();
68473
68474                 var pointsEnter = groups.enter()
68475                     .append('g')
68476                     .attr('class', 'geolocation');
68477
68478                 pointsEnter
68479                     .append('circle')
68480                     .attr('class', 'geolocate-radius')
68481                     .attr('dx', '0')
68482                     .attr('dy', '0')
68483                     .attr('fill', 'rgb(15,128,225)')
68484                     .attr('fill-opacity', '0.3')
68485                     .attr('r', '0');
68486
68487                 pointsEnter
68488                     .append('circle')
68489                     .attr('dx', '0')
68490                     .attr('dy', '0')
68491                     .attr('fill', 'rgb(15,128,225)')
68492                     .attr('stroke', 'white')
68493                     .attr('stroke-width', '1.5')
68494                     .attr('r', '6');
68495
68496                 groups.merge(pointsEnter)
68497                     .attr('transform', transform);
68498
68499                 layer.select('.geolocate-radius').attr('r', accuracy(_position.coords.accuracy, geolocation.loc));
68500             }
68501
68502             function drawLocation(selection) {
68503                 var enabled = svgGeolocate.enabled;
68504
68505                 layer = selection.selectAll('.layer-geolocate')
68506                     .data([0]);
68507
68508                 layer.exit()
68509                     .remove();
68510
68511                 var layerEnter = layer.enter()
68512                     .append('g')
68513                     .attr('class', 'layer-geolocate')
68514                     .style('display', enabled ? 'block' : 'none');
68515
68516                 layerEnter
68517                     .append('g')
68518                     .attr('class', 'geolocations');
68519
68520                 layer = layerEnter
68521                     .merge(layer);
68522
68523                 if (enabled) {
68524                     update();
68525                 } else {
68526                     layerOff();
68527                 }
68528             }
68529
68530             drawLocation.enabled = function (position, enabled) {
68531                 if (!arguments.length) return svgGeolocate.enabled;
68532                 _position = position;
68533                 svgGeolocate.enabled = enabled;
68534                 if (svgGeolocate.enabled) {
68535                     showLayer();
68536                     layerOn();
68537                 } else {
68538                     hideLayer();
68539                 }
68540                 return this;
68541             };
68542
68543             init();
68544             return drawLocation;
68545         }
68546
68547         function svgLabels(projection, context) {
68548             var path = d3_geoPath(projection);
68549             var detected = utilDetect();
68550             var baselineHack = (detected.ie ||
68551                 detected.browser.toLowerCase() === 'edge' ||
68552                 (detected.browser.toLowerCase() === 'firefox' && detected.version >= 70));
68553             var _rdrawn = new RBush();
68554             var _rskipped = new RBush();
68555             var _textWidthCache = {};
68556             var _entitybboxes = {};
68557
68558             // Listed from highest to lowest priority
68559             var labelStack = [
68560                 ['line', 'aeroway', '*', 12],
68561                 ['line', 'highway', 'motorway', 12],
68562                 ['line', 'highway', 'trunk', 12],
68563                 ['line', 'highway', 'primary', 12],
68564                 ['line', 'highway', 'secondary', 12],
68565                 ['line', 'highway', 'tertiary', 12],
68566                 ['line', 'highway', '*', 12],
68567                 ['line', 'railway', '*', 12],
68568                 ['line', 'waterway', '*', 12],
68569                 ['area', 'aeroway', '*', 12],
68570                 ['area', 'amenity', '*', 12],
68571                 ['area', 'building', '*', 12],
68572                 ['area', 'historic', '*', 12],
68573                 ['area', 'leisure', '*', 12],
68574                 ['area', 'man_made', '*', 12],
68575                 ['area', 'natural', '*', 12],
68576                 ['area', 'shop', '*', 12],
68577                 ['area', 'tourism', '*', 12],
68578                 ['area', 'camp_site', '*', 12],
68579                 ['point', 'aeroway', '*', 10],
68580                 ['point', 'amenity', '*', 10],
68581                 ['point', 'building', '*', 10],
68582                 ['point', 'historic', '*', 10],
68583                 ['point', 'leisure', '*', 10],
68584                 ['point', 'man_made', '*', 10],
68585                 ['point', 'natural', '*', 10],
68586                 ['point', 'shop', '*', 10],
68587                 ['point', 'tourism', '*', 10],
68588                 ['point', 'camp_site', '*', 10],
68589                 ['line', 'name', '*', 12],
68590                 ['area', 'name', '*', 12],
68591                 ['point', 'name', '*', 10]
68592             ];
68593
68594
68595             function shouldSkipIcon(preset) {
68596                 var noIcons = ['building', 'landuse', 'natural'];
68597                 return noIcons.some(function(s) {
68598                     return preset.id.indexOf(s) >= 0;
68599                 });
68600             }
68601
68602
68603             function get(array, prop) {
68604                 return function(d, i) { return array[i][prop]; };
68605             }
68606
68607
68608             function textWidth(text, size, elem) {
68609                 var c = _textWidthCache[size];
68610                 if (!c) c = _textWidthCache[size] = {};
68611
68612                 if (c[text]) {
68613                     return c[text];
68614
68615                 } else if (elem) {
68616                     c[text] = elem.getComputedTextLength();
68617                     return c[text];
68618
68619                 } else {
68620                     var str = encodeURIComponent(text).match(/%[CDEFcdef]/g);
68621                     if (str === null) {
68622                         return size / 3 * 2 * text.length;
68623                     } else {
68624                         return size / 3 * (2 * text.length + str.length);
68625                     }
68626                 }
68627             }
68628
68629
68630             function drawLinePaths(selection, entities, filter, classes, labels) {
68631                 var paths = selection.selectAll('path')
68632                     .filter(filter)
68633                     .data(entities, osmEntity.key);
68634
68635                 // exit
68636                 paths.exit()
68637                     .remove();
68638
68639                 // enter/update
68640                 paths.enter()
68641                     .append('path')
68642                     .style('stroke-width', get(labels, 'font-size'))
68643                     .attr('id', function(d) { return 'ideditor-labelpath-' + d.id; })
68644                     .attr('class', classes)
68645                     .merge(paths)
68646                     .attr('d', get(labels, 'lineString'));
68647             }
68648
68649
68650             function drawLineLabels(selection, entities, filter, classes, labels) {
68651                 var texts = selection.selectAll('text.' + classes)
68652                     .filter(filter)
68653                     .data(entities, osmEntity.key);
68654
68655                 // exit
68656                 texts.exit()
68657                     .remove();
68658
68659                 // enter
68660                 texts.enter()
68661                     .append('text')
68662                     .attr('class', function(d, i) { return classes + ' ' + labels[i].classes + ' ' + d.id; })
68663                     .attr('dy', baselineHack ? '0.35em' : null)
68664                     .append('textPath')
68665                     .attr('class', 'textpath');
68666
68667                 // update
68668                 selection.selectAll('text.' + classes).selectAll('.textpath')
68669                     .filter(filter)
68670                     .data(entities, osmEntity.key)
68671                     .attr('startOffset', '50%')
68672                     .attr('xlink:href', function(d) { return '#ideditor-labelpath-' + d.id; })
68673                     .text(utilDisplayNameForPath);
68674             }
68675
68676
68677             function drawPointLabels(selection, entities, filter, classes, labels) {
68678                 var texts = selection.selectAll('text.' + classes)
68679                     .filter(filter)
68680                     .data(entities, osmEntity.key);
68681
68682                 // exit
68683                 texts.exit()
68684                     .remove();
68685
68686                 // enter/update
68687                 texts.enter()
68688                     .append('text')
68689                     .attr('class', function(d, i) {
68690                         return classes + ' ' + labels[i].classes + ' ' + d.id;
68691                     })
68692                     .merge(texts)
68693                     .attr('x', get(labels, 'x'))
68694                     .attr('y', get(labels, 'y'))
68695                     .style('text-anchor', get(labels, 'textAnchor'))
68696                     .text(utilDisplayName)
68697                     .each(function(d, i) {
68698                         textWidth(utilDisplayName(d), labels[i].height, this);
68699                     });
68700             }
68701
68702
68703             function drawAreaLabels(selection, entities, filter, classes, labels) {
68704                 entities = entities.filter(hasText);
68705                 labels = labels.filter(hasText);
68706                 drawPointLabels(selection, entities, filter, classes, labels);
68707
68708                 function hasText(d, i) {
68709                     return labels[i].hasOwnProperty('x') && labels[i].hasOwnProperty('y');
68710                 }
68711             }
68712
68713
68714             function drawAreaIcons(selection, entities, filter, classes, labels) {
68715                 var icons = selection.selectAll('use.' + classes)
68716                     .filter(filter)
68717                     .data(entities, osmEntity.key);
68718
68719                 // exit
68720                 icons.exit()
68721                     .remove();
68722
68723                 // enter/update
68724                 icons.enter()
68725                     .append('use')
68726                     .attr('class', 'icon ' + classes)
68727                     .attr('width', '17px')
68728                     .attr('height', '17px')
68729                     .merge(icons)
68730                     .attr('transform', get(labels, 'transform'))
68731                     .attr('xlink:href', function(d) {
68732                         var preset = _mainPresetIndex.match(d, context.graph());
68733                         var picon = preset && preset.icon;
68734
68735                         if (!picon) {
68736                             return '';
68737                         } else {
68738                             var isMaki = /^maki-/.test(picon);
68739                             return '#' + picon + (isMaki ? '-15' : '');
68740                         }
68741                     });
68742             }
68743
68744
68745             function drawCollisionBoxes(selection, rtree, which) {
68746                 var classes = 'debug ' + which + ' ' + (which === 'debug-skipped' ? 'orange' : 'yellow');
68747
68748                 var gj = [];
68749                 if (context.getDebug('collision')) {
68750                     gj = rtree.all().map(function(d) {
68751                         return { type: 'Polygon', coordinates: [[
68752                             [d.minX, d.minY],
68753                             [d.maxX, d.minY],
68754                             [d.maxX, d.maxY],
68755                             [d.minX, d.maxY],
68756                             [d.minX, d.minY]
68757                         ]]};
68758                     });
68759                 }
68760
68761                 var boxes = selection.selectAll('.' + which)
68762                     .data(gj);
68763
68764                 // exit
68765                 boxes.exit()
68766                     .remove();
68767
68768                 // enter/update
68769                 boxes.enter()
68770                     .append('path')
68771                     .attr('class', classes)
68772                     .merge(boxes)
68773                     .attr('d', d3_geoPath());
68774             }
68775
68776
68777             function drawLabels(selection, graph, entities, filter, dimensions, fullRedraw) {
68778                 var wireframe = context.surface().classed('fill-wireframe');
68779                 var zoom = geoScaleToZoom(projection.scale());
68780
68781                 var labelable = [];
68782                 var renderNodeAs = {};
68783                 var i, j, k, entity, geometry;
68784
68785                 for (i = 0; i < labelStack.length; i++) {
68786                     labelable.push([]);
68787                 }
68788
68789                 if (fullRedraw) {
68790                     _rdrawn.clear();
68791                     _rskipped.clear();
68792                     _entitybboxes = {};
68793
68794                 } else {
68795                     for (i = 0; i < entities.length; i++) {
68796                         entity = entities[i];
68797                         var toRemove = []
68798                             .concat(_entitybboxes[entity.id] || [])
68799                             .concat(_entitybboxes[entity.id + 'I'] || []);
68800
68801                         for (j = 0; j < toRemove.length; j++) {
68802                             _rdrawn.remove(toRemove[j]);
68803                             _rskipped.remove(toRemove[j]);
68804                         }
68805                     }
68806                 }
68807
68808                 // Loop through all the entities to do some preprocessing
68809                 for (i = 0; i < entities.length; i++) {
68810                     entity = entities[i];
68811                     geometry = entity.geometry(graph);
68812
68813                     // Insert collision boxes around interesting points/vertices
68814                     if (geometry === 'point' || (geometry === 'vertex' && isInterestingVertex(entity))) {
68815                         var hasDirections = entity.directions(graph, projection).length;
68816                         var markerPadding;
68817
68818                         if (!wireframe && geometry === 'point' && !(zoom >= 18 && hasDirections)) {
68819                             renderNodeAs[entity.id] = 'point';
68820                             markerPadding = 20;   // extra y for marker height
68821                         } else {
68822                             renderNodeAs[entity.id] = 'vertex';
68823                             markerPadding = 0;
68824                         }
68825
68826                         var coord = projection(entity.loc);
68827                         var nodePadding = 10;
68828                         var bbox = {
68829                             minX: coord[0] - nodePadding,
68830                             minY: coord[1] - nodePadding - markerPadding,
68831                             maxX: coord[0] + nodePadding,
68832                             maxY: coord[1] + nodePadding
68833                         };
68834
68835                         doInsert(bbox, entity.id + 'P');
68836                     }
68837
68838                     // From here on, treat vertices like points
68839                     if (geometry === 'vertex') {
68840                         geometry = 'point';
68841                     }
68842
68843                     // Determine which entities are label-able
68844                     var preset = geometry === 'area' && _mainPresetIndex.match(entity, graph);
68845                     var icon = preset && !shouldSkipIcon(preset) && preset.icon;
68846
68847                     if (!icon && !utilDisplayName(entity))
68848                         continue;
68849
68850                     for (k = 0; k < labelStack.length; k++) {
68851                         var matchGeom = labelStack[k][0];
68852                         var matchKey = labelStack[k][1];
68853                         var matchVal = labelStack[k][2];
68854                         var hasVal = entity.tags[matchKey];
68855
68856                         if (geometry === matchGeom && hasVal && (matchVal === '*' || matchVal === hasVal)) {
68857                             labelable[k].push(entity);
68858                             break;
68859                         }
68860                     }
68861                 }
68862
68863                 var positions = {
68864                     point: [],
68865                     line: [],
68866                     area: []
68867                 };
68868
68869                 var labelled = {
68870                     point: [],
68871                     line: [],
68872                     area: []
68873                 };
68874
68875                 // Try and find a valid label for labellable entities
68876                 for (k = 0; k < labelable.length; k++) {
68877                     var fontSize = labelStack[k][3];
68878
68879                     for (i = 0; i < labelable[k].length; i++) {
68880                         entity = labelable[k][i];
68881                         geometry = entity.geometry(graph);
68882
68883                         var getName = (geometry === 'line') ? utilDisplayNameForPath : utilDisplayName;
68884                         var name = getName(entity);
68885                         var width = name && textWidth(name, fontSize);
68886                         var p = null;
68887
68888                         if (geometry === 'point' || geometry === 'vertex') {
68889                             // no point or vertex labels in wireframe mode
68890                             // no vertex labels at low zooms (vertices have no icons)
68891                             if (wireframe) continue;
68892                             var renderAs = renderNodeAs[entity.id];
68893                             if (renderAs === 'vertex' && zoom < 17) continue;
68894
68895                             p = getPointLabel(entity, width, fontSize, renderAs);
68896
68897                         } else if (geometry === 'line') {
68898                             p = getLineLabel(entity, width, fontSize);
68899
68900                         } else if (geometry === 'area') {
68901                             p = getAreaLabel(entity, width, fontSize);
68902                         }
68903
68904                         if (p) {
68905                             if (geometry === 'vertex') { geometry = 'point'; }  // treat vertex like point
68906                             p.classes = geometry + ' tag-' + labelStack[k][1];
68907                             positions[geometry].push(p);
68908                             labelled[geometry].push(entity);
68909                         }
68910                     }
68911                 }
68912
68913
68914                 function isInterestingVertex(entity) {
68915                     var selectedIDs = context.selectedIDs();
68916
68917                     return entity.hasInterestingTags() ||
68918                         entity.isEndpoint(graph) ||
68919                         entity.isConnected(graph) ||
68920                         selectedIDs.indexOf(entity.id) !== -1 ||
68921                         graph.parentWays(entity).some(function(parent) {
68922                             return selectedIDs.indexOf(parent.id) !== -1;
68923                         });
68924                 }
68925
68926
68927                 function getPointLabel(entity, width, height, geometry) {
68928                     var y = (geometry === 'point' ? -12 : 0);
68929                     var pointOffsets = {
68930                         ltr: [15, y, 'start'],
68931                         rtl: [-15, y, 'end']
68932                     };
68933
68934                     var textDirection = _mainLocalizer.textDirection();
68935
68936                     var coord = projection(entity.loc);
68937                     var textPadding = 2;
68938                     var offset = pointOffsets[textDirection];
68939                     var p = {
68940                         height: height,
68941                         width: width,
68942                         x: coord[0] + offset[0],
68943                         y: coord[1] + offset[1],
68944                         textAnchor: offset[2]
68945                     };
68946
68947                     // insert a collision box for the text label..
68948                     var bbox;
68949                     if (textDirection === 'rtl') {
68950                         bbox = {
68951                             minX: p.x - width - textPadding,
68952                             minY: p.y - (height / 2) - textPadding,
68953                             maxX: p.x + textPadding,
68954                             maxY: p.y + (height / 2) + textPadding
68955                         };
68956                     } else {
68957                         bbox = {
68958                             minX: p.x - textPadding,
68959                             minY: p.y - (height / 2) - textPadding,
68960                             maxX: p.x + width + textPadding,
68961                             maxY: p.y + (height / 2) + textPadding
68962                         };
68963                     }
68964
68965                     if (tryInsert([bbox], entity.id, true)) {
68966                         return p;
68967                     }
68968                 }
68969
68970
68971                 function getLineLabel(entity, width, height) {
68972                     var viewport = geoExtent(context.projection.clipExtent()).polygon();
68973                     var points = graph.childNodes(entity)
68974                         .map(function(node) { return projection(node.loc); });
68975                     var length = geoPathLength(points);
68976
68977                     if (length < width + 20) return;
68978
68979                     // % along the line to attempt to place the label
68980                     var lineOffsets = [50, 45, 55, 40, 60, 35, 65, 30, 70,
68981                                        25, 75, 20, 80, 15, 95, 10, 90, 5, 95];
68982                     var padding = 3;
68983
68984                     for (var i = 0; i < lineOffsets.length; i++) {
68985                         var offset = lineOffsets[i];
68986                         var middle = offset / 100 * length;
68987                         var start = middle - width / 2;
68988
68989                         if (start < 0 || start + width > length) continue;
68990
68991                         // generate subpath and ignore paths that are invalid or don't cross viewport.
68992                         var sub = subpath(points, start, start + width);
68993                         if (!sub || !geoPolygonIntersectsPolygon(viewport, sub, true)) {
68994                             continue;
68995                         }
68996
68997                         var isReverse = reverse(sub);
68998                         if (isReverse) {
68999                             sub = sub.reverse();
69000                         }
69001
69002                         var bboxes = [];
69003                         var boxsize = (height + 2) / 2;
69004
69005                         for (var j = 0; j < sub.length - 1; j++) {
69006                             var a = sub[j];
69007                             var b = sub[j + 1];
69008
69009                             // split up the text into small collision boxes
69010                             var num = Math.max(1, Math.floor(geoVecLength(a, b) / boxsize / 2));
69011
69012                             for (var box = 0; box < num; box++) {
69013                                 var p = geoVecInterp(a, b, box / num);
69014                                 var x0 = p[0] - boxsize - padding;
69015                                 var y0 = p[1] - boxsize - padding;
69016                                 var x1 = p[0] + boxsize + padding;
69017                                 var y1 = p[1] + boxsize + padding;
69018
69019                                 bboxes.push({
69020                                     minX: Math.min(x0, x1),
69021                                     minY: Math.min(y0, y1),
69022                                     maxX: Math.max(x0, x1),
69023                                     maxY: Math.max(y0, y1)
69024                                 });
69025                             }
69026                         }
69027
69028                         if (tryInsert(bboxes, entity.id, false)) {   // accept this one
69029                             return {
69030                                 'font-size': height + 2,
69031                                 lineString: lineString(sub),
69032                                 startOffset: offset + '%'
69033                             };
69034                         }
69035                     }
69036
69037                     function reverse(p) {
69038                         var angle = Math.atan2(p[1][1] - p[0][1], p[1][0] - p[0][0]);
69039                         return !(p[0][0] < p[p.length - 1][0] && angle < Math.PI/2 && angle > -Math.PI/2);
69040                     }
69041
69042                     function lineString(points) {
69043                         return 'M' + points.join('L');
69044                     }
69045
69046                     function subpath(points, from, to) {
69047                         var sofar = 0;
69048                         var start, end, i0, i1;
69049
69050                         for (var i = 0; i < points.length - 1; i++) {
69051                             var a = points[i];
69052                             var b = points[i + 1];
69053                             var current = geoVecLength(a, b);
69054                             var portion;
69055                             if (!start && sofar + current >= from) {
69056                                 portion = (from - sofar) / current;
69057                                 start = [
69058                                     a[0] + portion * (b[0] - a[0]),
69059                                     a[1] + portion * (b[1] - a[1])
69060                                 ];
69061                                 i0 = i + 1;
69062                             }
69063                             if (!end && sofar + current >= to) {
69064                                 portion = (to - sofar) / current;
69065                                 end = [
69066                                     a[0] + portion * (b[0] - a[0]),
69067                                     a[1] + portion * (b[1] - a[1])
69068                                 ];
69069                                 i1 = i + 1;
69070                             }
69071                             sofar += current;
69072                         }
69073
69074                         var result = points.slice(i0, i1);
69075                         result.unshift(start);
69076                         result.push(end);
69077                         return result;
69078                     }
69079                 }
69080
69081
69082                 function getAreaLabel(entity, width, height) {
69083                     var centroid = path.centroid(entity.asGeoJSON(graph, true));
69084                     var extent = entity.extent(graph);
69085                     var areaWidth = projection(extent[1])[0] - projection(extent[0])[0];
69086
69087                     if (isNaN(centroid[0]) || areaWidth < 20) return;
69088
69089                     var preset = _mainPresetIndex.match(entity, context.graph());
69090                     var picon = preset && preset.icon;
69091                     var iconSize = 17;
69092                     var padding = 2;
69093                     var p = {};
69094
69095                     if (picon) {  // icon and label..
69096                         if (addIcon()) {
69097                             addLabel(iconSize + padding);
69098                             return p;
69099                         }
69100                     } else {   // label only..
69101                         if (addLabel(0)) {
69102                             return p;
69103                         }
69104                     }
69105
69106
69107                     function addIcon() {
69108                         var iconX = centroid[0] - (iconSize / 2);
69109                         var iconY = centroid[1] - (iconSize / 2);
69110                         var bbox = {
69111                             minX: iconX,
69112                             minY: iconY,
69113                             maxX: iconX + iconSize,
69114                             maxY: iconY + iconSize
69115                         };
69116
69117                         if (tryInsert([bbox], entity.id + 'I', true)) {
69118                             p.transform = 'translate(' + iconX + ',' + iconY + ')';
69119                             return true;
69120                         }
69121                         return false;
69122                     }
69123
69124                     function addLabel(yOffset) {
69125                         if (width && areaWidth >= width + 20) {
69126                             var labelX = centroid[0];
69127                             var labelY = centroid[1] + yOffset;
69128                             var bbox = {
69129                                 minX: labelX - (width / 2) - padding,
69130                                 minY: labelY - (height / 2) - padding,
69131                                 maxX: labelX + (width / 2) + padding,
69132                                 maxY: labelY + (height / 2) + padding
69133                             };
69134
69135                             if (tryInsert([bbox], entity.id, true)) {
69136                                 p.x = labelX;
69137                                 p.y = labelY;
69138                                 p.textAnchor = 'middle';
69139                                 p.height = height;
69140                                 return true;
69141                             }
69142                         }
69143                         return false;
69144                     }
69145                 }
69146
69147
69148                 // force insert a singular bounding box
69149                 // singular box only, no array, id better be unique
69150                 function doInsert(bbox, id) {
69151                     bbox.id = id;
69152
69153                     var oldbox = _entitybboxes[id];
69154                     if (oldbox) {
69155                         _rdrawn.remove(oldbox);
69156                     }
69157                     _entitybboxes[id] = bbox;
69158                     _rdrawn.insert(bbox);
69159                 }
69160
69161
69162                 function tryInsert(bboxes, id, saveSkipped) {
69163                     var skipped = false;
69164
69165                     for (var i = 0; i < bboxes.length; i++) {
69166                         var bbox = bboxes[i];
69167                         bbox.id = id;
69168
69169                         // Check that label is visible
69170                         if (bbox.minX < 0 || bbox.minY < 0 || bbox.maxX > dimensions[0] || bbox.maxY > dimensions[1]) {
69171                             skipped = true;
69172                             break;
69173                         }
69174                         if (_rdrawn.collides(bbox)) {
69175                             skipped = true;
69176                             break;
69177                         }
69178                     }
69179
69180                     _entitybboxes[id] = bboxes;
69181
69182                     if (skipped) {
69183                         if (saveSkipped) {
69184                             _rskipped.load(bboxes);
69185                         }
69186                     } else {
69187                         _rdrawn.load(bboxes);
69188                     }
69189
69190                     return !skipped;
69191                 }
69192
69193
69194                 var layer = selection.selectAll('.layer-osm.labels');
69195                 layer.selectAll('.labels-group')
69196                     .data(['halo', 'label', 'debug'])
69197                     .enter()
69198                     .append('g')
69199                     .attr('class', function(d) { return 'labels-group ' + d; });
69200
69201                 var halo = layer.selectAll('.labels-group.halo');
69202                 var label = layer.selectAll('.labels-group.label');
69203                 var debug = layer.selectAll('.labels-group.debug');
69204
69205                 // points
69206                 drawPointLabels(label, labelled.point, filter, 'pointlabel', positions.point);
69207                 drawPointLabels(halo, labelled.point, filter, 'pointlabel-halo', positions.point);
69208
69209                 // lines
69210                 drawLinePaths(layer, labelled.line, filter, '', positions.line);
69211                 drawLineLabels(label, labelled.line, filter, 'linelabel', positions.line);
69212                 drawLineLabels(halo, labelled.line, filter, 'linelabel-halo', positions.line);
69213
69214                 // areas
69215                 drawAreaLabels(label, labelled.area, filter, 'arealabel', positions.area);
69216                 drawAreaLabels(halo, labelled.area, filter, 'arealabel-halo', positions.area);
69217                 drawAreaIcons(label, labelled.area, filter, 'areaicon', positions.area);
69218                 drawAreaIcons(halo, labelled.area, filter, 'areaicon-halo', positions.area);
69219
69220                 // debug
69221                 drawCollisionBoxes(debug, _rskipped, 'debug-skipped');
69222                 drawCollisionBoxes(debug, _rdrawn, 'debug-drawn');
69223
69224                 layer.call(filterLabels);
69225             }
69226
69227
69228             function filterLabels(selection) {
69229                 var drawLayer = selection.selectAll('.layer-osm.labels');
69230                 var layers = drawLayer.selectAll('.labels-group.halo, .labels-group.label');
69231
69232                 layers.selectAll('.nolabel')
69233                     .classed('nolabel', false);
69234
69235                 var mouse = context.map().mouse();
69236                 var graph = context.graph();
69237                 var selectedIDs = context.selectedIDs();
69238                 var ids = [];
69239                 var pad, bbox;
69240
69241                 // hide labels near the mouse
69242                 if (mouse) {
69243                     pad = 20;
69244                     bbox = { minX: mouse[0] - pad, minY: mouse[1] - pad, maxX: mouse[0] + pad, maxY: mouse[1] + pad };
69245                     var nearMouse = _rdrawn.search(bbox).map(function(entity) { return entity.id; });
69246                     ids.push.apply(ids, nearMouse);
69247                 }
69248
69249                 // hide labels on selected nodes (they look weird when dragging / haloed)
69250                 for (var i = 0; i < selectedIDs.length; i++) {
69251                     var entity = graph.hasEntity(selectedIDs[i]);
69252                     if (entity && entity.type === 'node') {
69253                         ids.push(selectedIDs[i]);
69254                     }
69255                 }
69256
69257                 layers.selectAll(utilEntitySelector(ids))
69258                     .classed('nolabel', true);
69259
69260
69261                 // draw the mouse bbox if debugging is on..
69262                 var debug = selection.selectAll('.labels-group.debug');
69263                 var gj = [];
69264                 if (context.getDebug('collision')) {
69265                     gj = bbox ? [{
69266                         type: 'Polygon',
69267                         coordinates: [[
69268                             [bbox.minX, bbox.minY],
69269                             [bbox.maxX, bbox.minY],
69270                             [bbox.maxX, bbox.maxY],
69271                             [bbox.minX, bbox.maxY],
69272                             [bbox.minX, bbox.minY]
69273                         ]]
69274                     }] : [];
69275                 }
69276
69277                 var box = debug.selectAll('.debug-mouse')
69278                     .data(gj);
69279
69280                 // exit
69281                 box.exit()
69282                     .remove();
69283
69284                 // enter/update
69285                 box.enter()
69286                     .append('path')
69287                     .attr('class', 'debug debug-mouse yellow')
69288                     .merge(box)
69289                     .attr('d', d3_geoPath());
69290             }
69291
69292
69293             var throttleFilterLabels = throttle(filterLabels, 100);
69294
69295
69296             drawLabels.observe = function(selection) {
69297                 var listener = function() { throttleFilterLabels(selection); };
69298                 selection.on('mousemove.hidelabels', listener);
69299                 context.on('enter.hidelabels', listener);
69300             };
69301
69302
69303             drawLabels.off = function(selection) {
69304                 throttleFilterLabels.cancel();
69305                 selection.on('mousemove.hidelabels', null);
69306                 context.on('enter.hidelabels', null);
69307             };
69308
69309
69310             return drawLabels;
69311         }
69312
69313         let _layerEnabled$1 = false;
69314         let _qaService$1;
69315
69316         function svgImproveOSM(projection, context, dispatch) {
69317           const throttledRedraw = throttle(() => dispatch.call('change'), 1000);
69318           const minZoom = 12;
69319
69320           let touchLayer = select(null);
69321           let drawLayer = select(null);
69322           let layerVisible = false;
69323
69324           function markerPath(selection, klass) {
69325             selection
69326               .attr('class', klass)
69327               .attr('transform', 'translate(-10, -28)')
69328               .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');
69329           }
69330
69331           // Loosely-coupled improveOSM service for fetching issues
69332           function getService() {
69333             if (services.improveOSM && !_qaService$1) {
69334               _qaService$1 = services.improveOSM;
69335               _qaService$1.on('loaded', throttledRedraw);
69336             } else if (!services.improveOSM && _qaService$1) {
69337               _qaService$1 = null;
69338             }
69339
69340             return _qaService$1;
69341           }
69342
69343           // Show the markers
69344           function editOn() {
69345             if (!layerVisible) {
69346               layerVisible = true;
69347               drawLayer
69348                 .style('display', 'block');
69349             }
69350           }
69351
69352           // Immediately remove the markers and their touch targets
69353           function editOff() {
69354             if (layerVisible) {
69355               layerVisible = false;
69356               drawLayer
69357                 .style('display', 'none');
69358               drawLayer.selectAll('.qaItem.improveOSM')
69359                 .remove();
69360               touchLayer.selectAll('.qaItem.improveOSM')
69361                 .remove();
69362             }
69363           }
69364
69365           // Enable the layer.  This shows the markers and transitions them to visible.
69366           function layerOn() {
69367             editOn();
69368
69369             drawLayer
69370               .style('opacity', 0)
69371               .transition()
69372               .duration(250)
69373               .style('opacity', 1)
69374               .on('end interrupt', () => dispatch.call('change'));
69375           }
69376
69377           // Disable the layer.  This transitions the layer invisible and then hides the markers.
69378           function layerOff() {
69379             throttledRedraw.cancel();
69380             drawLayer.interrupt();
69381             touchLayer.selectAll('.qaItem.improveOSM')
69382               .remove();
69383
69384             drawLayer
69385               .transition()
69386               .duration(250)
69387               .style('opacity', 0)
69388               .on('end interrupt', () => {
69389                 editOff();
69390                 dispatch.call('change');
69391               });
69392           }
69393
69394           // Update the issue markers
69395           function updateMarkers() {
69396             if (!layerVisible || !_layerEnabled$1) return;
69397
69398             const service = getService();
69399             const selectedID = context.selectedErrorID();
69400             const data = (service ? service.getItems(projection) : []);
69401             const getTransform = svgPointTransform(projection);
69402
69403             // Draw markers..
69404             const markers = drawLayer.selectAll('.qaItem.improveOSM')
69405               .data(data, d => d.id);
69406
69407             // exit
69408             markers.exit()
69409               .remove();
69410
69411             // enter
69412             const markersEnter = markers.enter()
69413               .append('g')
69414                 .attr('class', d => `qaItem ${d.service} itemId-${d.id} itemType-${d.itemType}`);
69415
69416             markersEnter
69417               .append('polygon')
69418                 .call(markerPath, 'shadow');
69419
69420             markersEnter
69421               .append('ellipse')
69422                 .attr('cx', 0)
69423                 .attr('cy', 0)
69424                 .attr('rx', 4.5)
69425                 .attr('ry', 2)
69426                 .attr('class', 'stroke');
69427
69428             markersEnter
69429               .append('polygon')
69430                 .attr('fill', 'currentColor')
69431                 .call(markerPath, 'qaItem-fill');
69432
69433             markersEnter
69434               .append('use')
69435                 .attr('transform', 'translate(-6.5, -23)')
69436                 .attr('class', 'icon-annotation')
69437                 .attr('width', '13px')
69438                 .attr('height', '13px')
69439                 .attr('xlink:href', d => {
69440                   const picon = d.icon;
69441
69442                   if (!picon) {
69443                   return '';
69444                   } else {
69445                   const isMaki = /^maki-/.test(picon);
69446                   return `#${picon}${isMaki ? '-11' : ''}`;
69447                   }
69448                 });
69449
69450             // update
69451             markers
69452               .merge(markersEnter)
69453               .sort(sortY)
69454                 .classed('selected', d => d.id === selectedID)
69455                 .attr('transform', getTransform);
69456
69457
69458             // Draw targets..
69459             if (touchLayer.empty()) return;
69460             const fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
69461
69462             const targets = touchLayer.selectAll('.qaItem.improveOSM')
69463               .data(data, d => d.id);
69464
69465             // exit
69466             targets.exit()
69467               .remove();
69468
69469             // enter/update
69470             targets.enter()
69471               .append('rect')
69472                 .attr('width', '20px')
69473                 .attr('height', '30px')
69474                 .attr('x', '-10px')
69475                 .attr('y', '-28px')
69476               .merge(targets)
69477               .sort(sortY)
69478                 .attr('class', d => `qaItem ${d.service} target ${fillClass} itemId-${d.id}`)
69479                 .attr('transform', getTransform);
69480
69481             function sortY(a, b) {
69482               return (a.id === selectedID) ? 1
69483                 : (b.id === selectedID) ? -1
69484                 : b.loc[1] - a.loc[1];
69485             }
69486           }
69487
69488           // Draw the ImproveOSM layer and schedule loading issues and updating markers.
69489           function drawImproveOSM(selection) {
69490             const service = getService();
69491
69492             const surface = context.surface();
69493             if (surface && !surface.empty()) {
69494               touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
69495             }
69496
69497             drawLayer = selection.selectAll('.layer-improveOSM')
69498               .data(service ? [0] : []);
69499
69500             drawLayer.exit()
69501               .remove();
69502
69503             drawLayer = drawLayer.enter()
69504               .append('g')
69505                 .attr('class', 'layer-improveOSM')
69506                 .style('display', _layerEnabled$1 ? 'block' : 'none')
69507               .merge(drawLayer);
69508
69509             if (_layerEnabled$1) {
69510               if (service && ~~context.map().zoom() >= minZoom) {
69511                 editOn();
69512                 service.loadIssues(projection);
69513                 updateMarkers();
69514               } else {
69515                 editOff();
69516               }
69517             }
69518           }
69519
69520           // Toggles the layer on and off
69521           drawImproveOSM.enabled = function(val) {
69522             if (!arguments.length) return _layerEnabled$1;
69523
69524             _layerEnabled$1 = val;
69525             if (_layerEnabled$1) {
69526               layerOn();
69527             } else {
69528               layerOff();
69529               if (context.selectedErrorID()) {
69530                 context.enter(modeBrowse(context));
69531               }
69532             }
69533
69534             dispatch.call('change');
69535             return this;
69536           };
69537
69538           drawImproveOSM.supported = () => !!getService();
69539
69540           return drawImproveOSM;
69541         }
69542
69543         let _layerEnabled$2 = false;
69544         let _qaService$2;
69545
69546         function svgOsmose(projection, context, dispatch) {
69547           const throttledRedraw = throttle(() => dispatch.call('change'), 1000);
69548           const minZoom = 12;
69549
69550           let touchLayer = select(null);
69551           let drawLayer = select(null);
69552           let layerVisible = false;
69553
69554           function markerPath(selection, klass) {
69555             selection
69556               .attr('class', klass)
69557               .attr('transform', 'translate(-10, -28)')
69558               .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');
69559           }
69560
69561           // Loosely-coupled osmose service for fetching issues
69562           function getService() {
69563             if (services.osmose && !_qaService$2) {
69564               _qaService$2 = services.osmose;
69565               _qaService$2.on('loaded', throttledRedraw);
69566             } else if (!services.osmose && _qaService$2) {
69567               _qaService$2 = null;
69568             }
69569
69570             return _qaService$2;
69571           }
69572
69573           // Show the markers
69574           function editOn() {
69575             if (!layerVisible) {
69576               layerVisible = true;
69577               drawLayer
69578                 .style('display', 'block');
69579             }
69580           }
69581
69582           // Immediately remove the markers and their touch targets
69583           function editOff() {
69584             if (layerVisible) {
69585               layerVisible = false;
69586               drawLayer
69587                 .style('display', 'none');
69588               drawLayer.selectAll('.qaItem.osmose')
69589                 .remove();
69590               touchLayer.selectAll('.qaItem.osmose')
69591                 .remove();
69592             }
69593           }
69594
69595           // Enable the layer.  This shows the markers and transitions them to visible.
69596           function layerOn() {
69597             editOn();
69598
69599             drawLayer
69600               .style('opacity', 0)
69601               .transition()
69602               .duration(250)
69603               .style('opacity', 1)
69604               .on('end interrupt', () => dispatch.call('change'));
69605           }
69606
69607           // Disable the layer.  This transitions the layer invisible and then hides the markers.
69608           function layerOff() {
69609             throttledRedraw.cancel();
69610             drawLayer.interrupt();
69611             touchLayer.selectAll('.qaItem.osmose')
69612               .remove();
69613
69614             drawLayer
69615               .transition()
69616               .duration(250)
69617               .style('opacity', 0)
69618               .on('end interrupt', () => {
69619                 editOff();
69620                 dispatch.call('change');
69621               });
69622           }
69623
69624           // Update the issue markers
69625           function updateMarkers() {
69626             if (!layerVisible || !_layerEnabled$2) return;
69627
69628             const service = getService();
69629             const selectedID = context.selectedErrorID();
69630             const data = (service ? service.getItems(projection) : []);
69631             const getTransform = svgPointTransform(projection);
69632
69633             // Draw markers..
69634             const markers = drawLayer.selectAll('.qaItem.osmose')
69635               .data(data, d => d.id);
69636
69637             // exit
69638             markers.exit()
69639               .remove();
69640
69641             // enter
69642             const markersEnter = markers.enter()
69643               .append('g')
69644                 .attr('class', d => `qaItem ${d.service} itemId-${d.id} itemType-${d.itemType}`);
69645
69646             markersEnter
69647               .append('polygon')
69648                 .call(markerPath, 'shadow');
69649
69650             markersEnter
69651               .append('ellipse')
69652                 .attr('cx', 0)
69653                 .attr('cy', 0)
69654                 .attr('rx', 4.5)
69655                 .attr('ry', 2)
69656                 .attr('class', 'stroke');
69657
69658             markersEnter
69659               .append('polygon')
69660                 .attr('fill', d => service.getColor(d.item))
69661                 .call(markerPath, 'qaItem-fill');
69662
69663             markersEnter
69664               .append('use')
69665                 .attr('transform', 'translate(-6.5, -23)')
69666                 .attr('class', 'icon-annotation')
69667                 .attr('width', '13px')
69668                 .attr('height', '13px')
69669                 .attr('xlink:href', d => {
69670                   const picon = d.icon;
69671
69672                   if (!picon) {
69673                     return '';
69674                   } else {
69675                     const isMaki = /^maki-/.test(picon);
69676                     return `#${picon}${isMaki ? '-11' : ''}`;
69677                   }
69678                 });
69679
69680             // update
69681             markers
69682               .merge(markersEnter)
69683               .sort(sortY)
69684                 .classed('selected', d => d.id === selectedID)
69685                 .attr('transform', getTransform);
69686
69687             // Draw targets..
69688             if (touchLayer.empty()) return;
69689             const fillClass = context.getDebug('target') ? 'pink' : 'nocolor';
69690
69691             const targets = touchLayer.selectAll('.qaItem.osmose')
69692               .data(data, d => d.id);
69693
69694             // exit
69695             targets.exit()
69696               .remove();
69697
69698             // enter/update
69699             targets.enter()
69700               .append('rect')
69701                 .attr('width', '20px')
69702                 .attr('height', '30px')
69703                 .attr('x', '-10px')
69704                 .attr('y', '-28px')
69705               .merge(targets)
69706               .sort(sortY)
69707                 .attr('class', d => `qaItem ${d.service} target ${fillClass} itemId-${d.id}`)
69708                 .attr('transform', getTransform);
69709
69710             function sortY(a, b) {
69711               return (a.id === selectedID) ? 1
69712                 : (b.id === selectedID) ? -1
69713                 : b.loc[1] - a.loc[1];
69714             }
69715           }
69716
69717           // Draw the Osmose layer and schedule loading issues and updating markers.
69718           function drawOsmose(selection) {
69719             const service = getService();
69720
69721             const surface = context.surface();
69722             if (surface && !surface.empty()) {
69723               touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
69724             }
69725
69726             drawLayer = selection.selectAll('.layer-osmose')
69727               .data(service ? [0] : []);
69728
69729             drawLayer.exit()
69730               .remove();
69731
69732             drawLayer = drawLayer.enter()
69733               .append('g')
69734                 .attr('class', 'layer-osmose')
69735                 .style('display', _layerEnabled$2 ? 'block' : 'none')
69736               .merge(drawLayer);
69737
69738             if (_layerEnabled$2) {
69739               if (service && ~~context.map().zoom() >= minZoom) {
69740                 editOn();
69741                 service.loadIssues(projection);
69742                 updateMarkers();
69743               } else {
69744                 editOff();
69745               }
69746             }
69747           }
69748
69749           // Toggles the layer on and off
69750           drawOsmose.enabled = function(val) {
69751             if (!arguments.length) return _layerEnabled$2;
69752
69753             _layerEnabled$2 = val;
69754             if (_layerEnabled$2) {
69755               // Strings supplied by Osmose fetched before showing layer for first time
69756               // NOTE: Currently no way to change locale in iD at runtime, would need to re-call this method if that's ever implemented
69757               // Also, If layer is toggled quickly multiple requests are sent
69758               getService().loadStrings()
69759                 .then(layerOn)
69760                 .catch(err => {
69761                   console.log(err); // eslint-disable-line no-console
69762                 });
69763             } else {
69764               layerOff();
69765               if (context.selectedErrorID()) {
69766                 context.enter(modeBrowse(context));
69767               }
69768             }
69769
69770             dispatch.call('change');
69771             return this;
69772           };
69773
69774           drawOsmose.supported = () => !!getService();
69775
69776           return drawOsmose;
69777         }
69778
69779         function svgStreetside(projection, context, dispatch) {
69780             var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000);
69781             var minZoom = 14;
69782             var minMarkerZoom = 16;
69783             var minViewfieldZoom = 18;
69784             var layer = select(null);
69785             var _viewerYaw = 0;
69786             var _selectedSequence = null;
69787             var _streetside;
69788
69789             /**
69790              * init().
69791              */
69792             function init() {
69793                 if (svgStreetside.initialized) return;  // run once
69794                 svgStreetside.enabled = false;
69795                 svgStreetside.initialized = true;
69796             }
69797
69798             /**
69799              * getService().
69800              */
69801             function getService() {
69802                 if (services.streetside && !_streetside) {
69803                     _streetside = services.streetside;
69804                     _streetside.event
69805                         .on('viewerChanged.svgStreetside', viewerChanged)
69806                         .on('loadedBubbles.svgStreetside', throttledRedraw);
69807                 } else if (!services.streetside && _streetside) {
69808                     _streetside = null;
69809                 }
69810
69811                 return _streetside;
69812             }
69813
69814             /**
69815              * showLayer().
69816              */
69817             function showLayer() {
69818                 var service = getService();
69819                 if (!service) return;
69820
69821                 editOn();
69822
69823                 layer
69824                     .style('opacity', 0)
69825                     .transition()
69826                     .duration(250)
69827                     .style('opacity', 1)
69828                     .on('end', function () { dispatch.call('change'); });
69829             }
69830
69831             /**
69832              * hideLayer().
69833              */
69834             function hideLayer() {
69835                 throttledRedraw.cancel();
69836
69837                 layer
69838                     .transition()
69839                     .duration(250)
69840                     .style('opacity', 0)
69841                     .on('end', editOff);
69842             }
69843
69844             /**
69845              * editOn().
69846              */
69847             function editOn() {
69848                 layer.style('display', 'block');
69849             }
69850
69851             /**
69852              * editOff().
69853              */
69854             function editOff() {
69855                 layer.selectAll('.viewfield-group').remove();
69856                 layer.style('display', 'none');
69857             }
69858
69859             /**
69860              * click() Handles 'bubble' point click event.
69861              */
69862             function click(d) {
69863                 var service = getService();
69864                 if (!service) return;
69865
69866                 // try to preserve the viewer rotation when staying on the same sequence
69867                 if (d.sequenceKey !== _selectedSequence) {
69868                     _viewerYaw = 0;  // reset
69869                 }
69870                 _selectedSequence = d.sequenceKey;
69871
69872                 service
69873                     .selectImage(context, d)
69874                     .then(response => {
69875                         if (response.status === 'ok'){
69876                             service.showViewer(context, _viewerYaw);
69877                         }
69878                     });
69879
69880
69881                 context.map().centerEase(d.loc);
69882             }
69883
69884             /**
69885              * mouseover().
69886              */
69887             function mouseover(d) {
69888                 var service = getService();
69889                 if (service) service.setStyles(context, d);
69890             }
69891
69892             /**
69893              * mouseout().
69894              */
69895             function mouseout() {
69896                 var service = getService();
69897                 if (service) service.setStyles(context, null);
69898             }
69899
69900             /**
69901              * transform().
69902              */
69903             function transform(d) {
69904                 var t = svgPointTransform(projection)(d);
69905                 var rot = d.ca + _viewerYaw;
69906                 if (rot) {
69907                     t += ' rotate(' + Math.floor(rot) + ',0,0)';
69908                 }
69909                 return t;
69910             }
69911
69912
69913             function viewerChanged() {
69914                 var service = getService();
69915                 if (!service) return;
69916
69917                 var viewer = service.viewer();
69918                 if (!viewer) return;
69919
69920                 // update viewfield rotation
69921                 _viewerYaw = viewer.getYaw();
69922
69923                 // avoid updating if the map is currently transformed
69924                 // e.g. during drags or easing.
69925                 if (context.map().isTransformed()) return;
69926
69927                 layer.selectAll('.viewfield-group.currentView')
69928                     .attr('transform', transform);
69929             }
69930
69931
69932             context.photos().on('change.streetside', update);
69933
69934             /**
69935              * update().
69936              */
69937             function update() {
69938                 var viewer = context.container().select('.photoviewer');
69939                 var selected = viewer.empty() ? undefined : viewer.datum();
69940                 var z = ~~context.map().zoom();
69941                 var showMarkers = (z >= minMarkerZoom);
69942                 var showViewfields = (z >= minViewfieldZoom);
69943                 var service = getService();
69944
69945                 var sequences = [];
69946                 var bubbles = [];
69947
69948                 if (context.photos().showsPanoramic()) {
69949                     sequences = (service ? service.sequences(projection) : []);
69950                     bubbles = (service && showMarkers ? service.bubbles(projection) : []);
69951                 }
69952
69953                 var traces = layer.selectAll('.sequences').selectAll('.sequence')
69954                     .data(sequences, function(d) { return d.properties.key; });
69955
69956                 // exit
69957                 traces.exit()
69958                     .remove();
69959
69960                 // enter/update
69961                 traces = traces.enter()
69962                     .append('path')
69963                     .attr('class', 'sequence')
69964                     .merge(traces)
69965                     .attr('d', svgPath(projection).geojson);
69966
69967
69968                 var groups = layer.selectAll('.markers').selectAll('.viewfield-group')
69969                     .data(bubbles, function(d) {
69970                         // force reenter once bubbles are attached to a sequence
69971                         return d.key + (d.sequenceKey ? 'v1' : 'v0');
69972                     });
69973
69974                 // exit
69975                 groups.exit()
69976                     .remove();
69977
69978                 // enter
69979                 var groupsEnter = groups.enter()
69980                     .append('g')
69981                     .attr('class', 'viewfield-group')
69982                     .on('mouseenter', mouseover)
69983                     .on('mouseleave', mouseout)
69984                     .on('click', click);
69985
69986                 groupsEnter
69987                     .append('g')
69988                     .attr('class', 'viewfield-scale');
69989
69990                 // update
69991                 var markers = groups
69992                     .merge(groupsEnter)
69993                     .sort(function(a, b) {
69994                         return (a === selected) ? 1
69995                             : (b === selected) ? -1
69996                             : b.loc[1] - a.loc[1];
69997                     })
69998                     .attr('transform', transform)
69999                     .select('.viewfield-scale');
70000
70001
70002                 markers.selectAll('circle')
70003                     .data([0])
70004                     .enter()
70005                     .append('circle')
70006                     .attr('dx', '0')
70007                     .attr('dy', '0')
70008                     .attr('r', '6');
70009
70010                 var viewfields = markers.selectAll('.viewfield')
70011                     .data(showViewfields ? [0] : []);
70012
70013                 viewfields.exit()
70014                     .remove();
70015
70016                 // viewfields may or may not be drawn...
70017                 // but if they are, draw below the circles
70018                 viewfields.enter()
70019                     .insert('path', 'circle')
70020                     .attr('class', 'viewfield')
70021                     .attr('transform', 'scale(1.5,1.5),translate(-8, -13)')
70022                     .attr('d', viewfieldPath);
70023
70024                 function viewfieldPath() {
70025                     var d = this.parentNode.__data__;
70026                     if (d.pano) {
70027                         return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
70028                     } else {
70029                         return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
70030                     }
70031                 }
70032
70033             }
70034
70035             /**
70036              * drawImages()
70037              * drawImages is the method that is returned (and that runs) everytime 'svgStreetside()' is called.
70038              * 'svgStreetside()' is called from index.js
70039              */
70040             function drawImages(selection) {
70041                 var enabled = svgStreetside.enabled;
70042                 var service = getService();
70043
70044                 layer = selection.selectAll('.layer-streetside-images')
70045                     .data(service ? [0] : []);
70046
70047                 layer.exit()
70048                     .remove();
70049
70050                 var layerEnter = layer.enter()
70051                     .append('g')
70052                     .attr('class', 'layer-streetside-images')
70053                     .style('display', enabled ? 'block' : 'none');
70054
70055                 layerEnter
70056                     .append('g')
70057                     .attr('class', 'sequences');
70058
70059                 layerEnter
70060                     .append('g')
70061                     .attr('class', 'markers');
70062
70063                 layer = layerEnter
70064                     .merge(layer);
70065
70066                 if (enabled) {
70067                     if (service && ~~context.map().zoom() >= minZoom) {
70068                         editOn();
70069                         update();
70070                         service.loadBubbles(projection);
70071                     } else {
70072                         editOff();
70073                     }
70074                 }
70075             }
70076
70077
70078             /**
70079              * drawImages.enabled().
70080              */
70081             drawImages.enabled = function(_) {
70082                 if (!arguments.length) return svgStreetside.enabled;
70083                 svgStreetside.enabled = _;
70084                 if (svgStreetside.enabled) {
70085                     showLayer();
70086                 } else {
70087                     hideLayer();
70088                 }
70089                 dispatch.call('change');
70090                 return this;
70091             };
70092
70093             /**
70094              * drawImages.supported().
70095              */
70096             drawImages.supported = function() {
70097                 return !!getService();
70098             };
70099
70100             init();
70101
70102             return drawImages;
70103         }
70104
70105         function svgMapillaryImages(projection, context, dispatch) {
70106             var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000);
70107             var minZoom = 12;
70108             var minMarkerZoom = 16;
70109             var minViewfieldZoom = 18;
70110             var layer = select(null);
70111             var _mapillary;
70112             var viewerCompassAngle;
70113
70114
70115             function init() {
70116                 if (svgMapillaryImages.initialized) return;  // run once
70117                 svgMapillaryImages.enabled = false;
70118                 svgMapillaryImages.initialized = true;
70119             }
70120
70121
70122             function getService() {
70123                 if (services.mapillary && !_mapillary) {
70124                     _mapillary = services.mapillary;
70125                     _mapillary.event.on('loadedImages', throttledRedraw);
70126                     _mapillary.event.on('bearingChanged', function(e) {
70127                         viewerCompassAngle = e;
70128
70129                         // avoid updating if the map is currently transformed
70130                         // e.g. during drags or easing.
70131                         if (context.map().isTransformed()) return;
70132
70133                         layer.selectAll('.viewfield-group.currentView')
70134                             .filter(function(d) {
70135                                 return d.pano;
70136                             })
70137                             .attr('transform', transform);
70138                     });
70139                 } else if (!services.mapillary && _mapillary) {
70140                     _mapillary = null;
70141                 }
70142
70143                 return _mapillary;
70144             }
70145
70146
70147             function showLayer() {
70148                 var service = getService();
70149                 if (!service) return;
70150
70151                 editOn();
70152
70153                 layer
70154                     .style('opacity', 0)
70155                     .transition()
70156                     .duration(250)
70157                     .style('opacity', 1)
70158                     .on('end', function () { dispatch.call('change'); });
70159             }
70160
70161
70162             function hideLayer() {
70163                 throttledRedraw.cancel();
70164
70165                 layer
70166                     .transition()
70167                     .duration(250)
70168                     .style('opacity', 0)
70169                     .on('end', editOff);
70170             }
70171
70172
70173             function editOn() {
70174                 layer.style('display', 'block');
70175             }
70176
70177
70178             function editOff() {
70179                 layer.selectAll('.viewfield-group').remove();
70180                 layer.style('display', 'none');
70181             }
70182
70183
70184             function click(d) {
70185                 var service = getService();
70186                 if (!service) return;
70187
70188                 service
70189                     .selectImage(context, d.key)
70190                     .updateViewer(context, d.key)
70191                     .showViewer(context);
70192
70193                 context.map().centerEase(d.loc);
70194             }
70195
70196
70197             function mouseover(d) {
70198                 var service = getService();
70199                 if (service) service.setStyles(context, d);
70200             }
70201
70202
70203             function mouseout() {
70204                 var service = getService();
70205                 if (service) service.setStyles(context, null);
70206             }
70207
70208
70209             function transform(d) {
70210                 var t = svgPointTransform(projection)(d);
70211                 if (d.pano && viewerCompassAngle !== null && isFinite(viewerCompassAngle)) {
70212                     t += ' rotate(' + Math.floor(viewerCompassAngle) + ',0,0)';
70213                 } else if (d.ca) {
70214                     t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
70215                 }
70216                 return t;
70217             }
70218
70219             context.photos().on('change.mapillary_images', update);
70220
70221             function filterImages(images) {
70222                 var showsPano = context.photos().showsPanoramic();
70223                 var showsFlat = context.photos().showsFlat();
70224                 if (!showsPano || !showsFlat) {
70225                     images = images.filter(function(image) {
70226                         if (image.pano) return showsPano;
70227                         return showsFlat;
70228                     });
70229                 }
70230                 return images;
70231             }
70232
70233             function filterSequences(sequences, service) {
70234                 var showsPano = context.photos().showsPanoramic();
70235                 var showsFlat = context.photos().showsFlat();
70236                 if (!showsPano || !showsFlat) {
70237                     sequences = sequences.filter(function(sequence) {
70238                         if (sequence.properties.hasOwnProperty('pano')) {
70239                             if (sequence.properties.pano) return showsPano;
70240                             return showsFlat;
70241                         } else {
70242                             // if the sequence doesn't specify pano or not, search its images
70243                             var cProps = sequence.properties.coordinateProperties;
70244                             if (cProps && cProps.image_keys && cProps.image_keys.length > 0) {
70245                                 for (var index in cProps.image_keys) {
70246                                     var imageKey = cProps.image_keys[index];
70247                                     var image = service.cachedImage(imageKey);
70248                                     if (image && image.hasOwnProperty('pano')) {
70249                                         if (image.pano) return showsPano;
70250                                         return showsFlat;
70251                                     }
70252                                 }
70253                             }
70254                         }
70255                     });
70256                 }
70257                 return sequences;
70258             }
70259
70260             function update() {
70261
70262                 var z = ~~context.map().zoom();
70263                 var showMarkers = (z >= minMarkerZoom);
70264                 var showViewfields = (z >= minViewfieldZoom);
70265
70266                 var service = getService();
70267                 var selectedKey = service && service.getSelectedImageKey();
70268                 var sequences = (service ? service.sequences(projection) : []);
70269                 var images = (service && showMarkers ? service.images(projection) : []);
70270
70271                 images = filterImages(images);
70272                 sequences = filterSequences(sequences, service);
70273
70274                 var traces = layer.selectAll('.sequences').selectAll('.sequence')
70275                     .data(sequences, function(d) { return d.properties.key; });
70276
70277                 // exit
70278                 traces.exit()
70279                     .remove();
70280
70281                 // enter/update
70282                 traces = traces.enter()
70283                     .append('path')
70284                     .attr('class', 'sequence')
70285                     .merge(traces)
70286                     .attr('d', svgPath(projection).geojson);
70287
70288
70289                 var groups = layer.selectAll('.markers').selectAll('.viewfield-group')
70290                     .data(images, function(d) { return d.key; });
70291
70292                 // exit
70293                 groups.exit()
70294                     .remove();
70295
70296                 // enter
70297                 var groupsEnter = groups.enter()
70298                     .append('g')
70299                     .attr('class', 'viewfield-group')
70300                     .on('mouseenter', mouseover)
70301                     .on('mouseleave', mouseout)
70302                     .on('click', click);
70303
70304                 groupsEnter
70305                     .append('g')
70306                     .attr('class', 'viewfield-scale');
70307
70308                 // update
70309                 var markers = groups
70310                     .merge(groupsEnter)
70311                     .sort(function(a, b) {
70312                         return (a.key === selectedKey) ? 1
70313                             : (b.key === selectedKey) ? -1
70314                             : b.loc[1] - a.loc[1];  // sort Y
70315                     })
70316                     .attr('transform', transform)
70317                     .select('.viewfield-scale');
70318
70319
70320                 markers.selectAll('circle')
70321                     .data([0])
70322                     .enter()
70323                     .append('circle')
70324                     .attr('dx', '0')
70325                     .attr('dy', '0')
70326                     .attr('r', '6');
70327
70328                 var viewfields = markers.selectAll('.viewfield')
70329                     .data(showViewfields ? [0] : []);
70330
70331                 viewfields.exit()
70332                     .remove();
70333
70334                 viewfields.enter()               // viewfields may or may not be drawn...
70335                     .insert('path', 'circle')    // but if they are, draw below the circles
70336                     .attr('class', 'viewfield')
70337                     .classed('pano', function() { return this.parentNode.__data__.pano; })
70338                     .attr('transform', 'scale(1.5,1.5),translate(-8, -13)')
70339                     .attr('d', viewfieldPath);
70340
70341                 function viewfieldPath() {
70342                     var d = this.parentNode.__data__;
70343                     if (d.pano) {
70344                         return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
70345                     } else {
70346                         return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
70347                     }
70348                 }
70349             }
70350
70351
70352             function drawImages(selection) {
70353                 var enabled = svgMapillaryImages.enabled;
70354                 var service = getService();
70355
70356                 layer = selection.selectAll('.layer-mapillary')
70357                     .data(service ? [0] : []);
70358
70359                 layer.exit()
70360                     .remove();
70361
70362                 var layerEnter = layer.enter()
70363                     .append('g')
70364                     .attr('class', 'layer-mapillary')
70365                     .style('display', enabled ? 'block' : 'none');
70366
70367                 layerEnter
70368                     .append('g')
70369                     .attr('class', 'sequences');
70370
70371                 layerEnter
70372                     .append('g')
70373                     .attr('class', 'markers');
70374
70375                 layer = layerEnter
70376                     .merge(layer);
70377
70378                 if (enabled) {
70379                     if (service && ~~context.map().zoom() >= minZoom) {
70380                         editOn();
70381                         update();
70382                         service.loadImages(projection);
70383                     } else {
70384                         editOff();
70385                     }
70386                 }
70387             }
70388
70389
70390             drawImages.enabled = function(_) {
70391                 if (!arguments.length) return svgMapillaryImages.enabled;
70392                 svgMapillaryImages.enabled = _;
70393                 if (svgMapillaryImages.enabled) {
70394                     showLayer();
70395                 } else {
70396                     hideLayer();
70397                 }
70398                 dispatch.call('change');
70399                 return this;
70400             };
70401
70402
70403             drawImages.supported = function() {
70404                 return !!getService();
70405             };
70406
70407
70408             init();
70409             return drawImages;
70410         }
70411
70412         function svgMapillarySigns(projection, context, dispatch) {
70413             var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000);
70414             var minZoom = 12;
70415             var layer = select(null);
70416             var _mapillary;
70417
70418
70419             function init() {
70420                 if (svgMapillarySigns.initialized) return;  // run once
70421                 svgMapillarySigns.enabled = false;
70422                 svgMapillarySigns.initialized = true;
70423             }
70424
70425
70426             function getService() {
70427                 if (services.mapillary && !_mapillary) {
70428                     _mapillary = services.mapillary;
70429                     _mapillary.event.on('loadedSigns', throttledRedraw);
70430                 } else if (!services.mapillary && _mapillary) {
70431                     _mapillary = null;
70432                 }
70433                 return _mapillary;
70434             }
70435
70436
70437             function showLayer() {
70438                 var service = getService();
70439                 if (!service) return;
70440
70441                 editOn();
70442             }
70443
70444
70445             function hideLayer() {
70446                 throttledRedraw.cancel();
70447                 editOff();
70448             }
70449
70450
70451             function editOn() {
70452                 layer.style('display', 'block');
70453             }
70454
70455
70456             function editOff() {
70457                 layer.selectAll('.icon-sign').remove();
70458                 layer.style('display', 'none');
70459             }
70460
70461
70462             function click(d) {
70463                 var service = getService();
70464                 if (!service) return;
70465
70466                 context.map().centerEase(d.loc);
70467
70468                 var selectedImageKey = service.getSelectedImageKey();
70469                 var imageKey;
70470
70471                 // Pick one of the images the sign was detected in,
70472                 // preference given to an image already selected.
70473                 d.detections.forEach(function(detection) {
70474                     if (!imageKey || selectedImageKey === detection.image_key) {
70475                         imageKey = detection.image_key;
70476                     }
70477                 });
70478
70479                 service
70480                     .selectImage(context, imageKey)
70481                     .updateViewer(context, imageKey)
70482                     .showViewer(context);
70483             }
70484
70485
70486             function update() {
70487                 var service = getService();
70488                 var data = (service ? service.signs(projection) : []);
70489                 var selectedImageKey = service.getSelectedImageKey();
70490                 var transform = svgPointTransform(projection);
70491
70492                 var signs = layer.selectAll('.icon-sign')
70493                     .data(data, function(d) { return d.key; });
70494
70495                 // exit
70496                 signs.exit()
70497                     .remove();
70498
70499                 // enter
70500                 var enter = signs.enter()
70501                     .append('g')
70502                     .attr('class', 'icon-sign icon-detected')
70503                     .on('click', click);
70504
70505                 enter
70506                     .append('use')
70507                     .attr('width', '24px')
70508                     .attr('height', '24px')
70509                     .attr('x', '-12px')
70510                     .attr('y', '-12px')
70511                     .attr('xlink:href', function(d) { return '#' + d.value; });
70512
70513                 enter
70514                     .append('rect')
70515                     .attr('width', '24px')
70516                     .attr('height', '24px')
70517                     .attr('x', '-12px')
70518                     .attr('y', '-12px');
70519
70520                 // update
70521                 signs
70522                     .merge(enter)
70523                     .attr('transform', transform)
70524                     .classed('currentView', function(d) {
70525                         return d.detections.some(function(detection) {
70526                             return detection.image_key === selectedImageKey;
70527                         });
70528                     })
70529                     .sort(function(a, b) {
70530                         var aSelected = a.detections.some(function(detection) {
70531                             return detection.image_key === selectedImageKey;
70532                         });
70533                         var bSelected = b.detections.some(function(detection) {
70534                             return detection.image_key === selectedImageKey;
70535                         });
70536                         if (aSelected === bSelected) {
70537                             return b.loc[1] - a.loc[1]; // sort Y
70538                         } else if (aSelected) {
70539                             return 1;
70540                         }
70541                         return -1;
70542                     });
70543             }
70544
70545
70546             function drawSigns(selection) {
70547                 var enabled = svgMapillarySigns.enabled;
70548                 var service = getService();
70549
70550                 layer = selection.selectAll('.layer-mapillary-signs')
70551                     .data(service ? [0] : []);
70552
70553                 layer.exit()
70554                     .remove();
70555
70556                 layer = layer.enter()
70557                     .append('g')
70558                     .attr('class', 'layer-mapillary-signs layer-mapillary-detections')
70559                     .style('display', enabled ? 'block' : 'none')
70560                     .merge(layer);
70561
70562                 if (enabled) {
70563                     if (service && ~~context.map().zoom() >= minZoom) {
70564                         editOn();
70565                         update();
70566                         service.loadSigns(projection);
70567                     } else {
70568                         editOff();
70569                     }
70570                 }
70571             }
70572
70573
70574             drawSigns.enabled = function(_) {
70575                 if (!arguments.length) return svgMapillarySigns.enabled;
70576                 svgMapillarySigns.enabled = _;
70577                 if (svgMapillarySigns.enabled) {
70578                     showLayer();
70579                 } else {
70580                     hideLayer();
70581                 }
70582                 dispatch.call('change');
70583                 return this;
70584             };
70585
70586
70587             drawSigns.supported = function() {
70588                 return !!getService();
70589             };
70590
70591
70592             init();
70593             return drawSigns;
70594         }
70595
70596         function svgMapillaryMapFeatures(projection, context, dispatch) {
70597             var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000);
70598             var minZoom = 12;
70599             var layer = select(null);
70600             var _mapillary;
70601
70602
70603             function init() {
70604                 if (svgMapillaryMapFeatures.initialized) return;  // run once
70605                 svgMapillaryMapFeatures.enabled = false;
70606                 svgMapillaryMapFeatures.initialized = true;
70607             }
70608
70609
70610             function getService() {
70611                 if (services.mapillary && !_mapillary) {
70612                     _mapillary = services.mapillary;
70613                     _mapillary.event.on('loadedMapFeatures', throttledRedraw);
70614                 } else if (!services.mapillary && _mapillary) {
70615                     _mapillary = null;
70616                 }
70617                 return _mapillary;
70618             }
70619
70620
70621             function showLayer() {
70622                 var service = getService();
70623                 if (!service) return;
70624
70625                 editOn();
70626             }
70627
70628
70629             function hideLayer() {
70630                 throttledRedraw.cancel();
70631                 editOff();
70632             }
70633
70634
70635             function editOn() {
70636                 layer.style('display', 'block');
70637             }
70638
70639
70640             function editOff() {
70641                 layer.selectAll('.icon-map-feature').remove();
70642                 layer.style('display', 'none');
70643             }
70644
70645
70646             function click(d) {
70647                 var service = getService();
70648                 if (!service) return;
70649
70650                 context.map().centerEase(d.loc);
70651
70652                 var selectedImageKey = service.getSelectedImageKey();
70653                 var imageKey;
70654
70655                 // Pick one of the images the map feature was detected in,
70656                 // preference given to an image already selected.
70657                 d.detections.forEach(function(detection) {
70658                     if (!imageKey || selectedImageKey === detection.image_key) {
70659                         imageKey = detection.image_key;
70660                     }
70661                 });
70662
70663                 service
70664                     .selectImage(context, imageKey)
70665                     .updateViewer(context, imageKey)
70666                     .showViewer(context);
70667             }
70668
70669
70670             function update() {
70671                 var service = getService();
70672                 var data = (service ? service.mapFeatures(projection) : []);
70673                 var selectedImageKey = service && service.getSelectedImageKey();
70674                 var transform = svgPointTransform(projection);
70675
70676                 var mapFeatures = layer.selectAll('.icon-map-feature')
70677                     .data(data, function(d) { return d.key; });
70678
70679                 // exit
70680                 mapFeatures.exit()
70681                     .remove();
70682
70683                 // enter
70684                 var enter = mapFeatures.enter()
70685                     .append('g')
70686                     .attr('class', 'icon-map-feature icon-detected')
70687                     .on('click', click);
70688
70689                 enter
70690                     .append('title')
70691                     .text(function(d) {
70692                         var id = d.value.replace(/--/g, '.').replace(/-/g, '_');
70693                         return _t('mapillary_map_features.' + id);
70694                     });
70695
70696                 enter
70697                     .append('use')
70698                     .attr('width', '24px')
70699                     .attr('height', '24px')
70700                     .attr('x', '-12px')
70701                     .attr('y', '-12px')
70702                     .attr('xlink:href', function(d) {
70703                         if (d.value === 'object--billboard') {
70704                             // no billboard icon right now, so use the advertisement icon
70705                             return '#object--sign--advertisement';
70706                         }
70707                         return '#' + d.value;
70708                     });
70709
70710                 enter
70711                     .append('rect')
70712                     .attr('width', '24px')
70713                     .attr('height', '24px')
70714                     .attr('x', '-12px')
70715                     .attr('y', '-12px');
70716
70717                 // update
70718                 mapFeatures
70719                     .merge(enter)
70720                     .attr('transform', transform)
70721                     .classed('currentView', function(d) {
70722                         return d.detections.some(function(detection) {
70723                             return detection.image_key === selectedImageKey;
70724                         });
70725                     })
70726                     .sort(function(a, b) {
70727                         var aSelected = a.detections.some(function(detection) {
70728                             return detection.image_key === selectedImageKey;
70729                         });
70730                         var bSelected = b.detections.some(function(detection) {
70731                             return detection.image_key === selectedImageKey;
70732                         });
70733                         if (aSelected === bSelected) {
70734                             return b.loc[1] - a.loc[1]; // sort Y
70735                         } else if (aSelected) {
70736                             return 1;
70737                         }
70738                         return -1;
70739                     });
70740             }
70741
70742
70743             function drawMapFeatures(selection) {
70744                 var enabled = svgMapillaryMapFeatures.enabled;
70745                 var service = getService();
70746
70747                 layer = selection.selectAll('.layer-mapillary-map-features')
70748                     .data(service ? [0] : []);
70749
70750                 layer.exit()
70751                     .remove();
70752
70753                 layer = layer.enter()
70754                     .append('g')
70755                     .attr('class', 'layer-mapillary-map-features layer-mapillary-detections')
70756                     .style('display', enabled ? 'block' : 'none')
70757                     .merge(layer);
70758
70759                 if (enabled) {
70760                     if (service && ~~context.map().zoom() >= minZoom) {
70761                         editOn();
70762                         update();
70763                         service.loadMapFeatures(projection);
70764                     } else {
70765                         editOff();
70766                     }
70767                 }
70768             }
70769
70770
70771             drawMapFeatures.enabled = function(_) {
70772                 if (!arguments.length) return svgMapillaryMapFeatures.enabled;
70773                 svgMapillaryMapFeatures.enabled = _;
70774                 if (svgMapillaryMapFeatures.enabled) {
70775                     showLayer();
70776                 } else {
70777                     hideLayer();
70778                 }
70779                 dispatch.call('change');
70780                 return this;
70781             };
70782
70783
70784             drawMapFeatures.supported = function() {
70785                 return !!getService();
70786             };
70787
70788
70789             init();
70790             return drawMapFeatures;
70791         }
70792
70793         function svgOpenstreetcamImages(projection, context, dispatch) {
70794             var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000);
70795             var minZoom = 12;
70796             var minMarkerZoom = 16;
70797             var minViewfieldZoom = 18;
70798             var layer = select(null);
70799             var _openstreetcam;
70800
70801
70802             function init() {
70803                 if (svgOpenstreetcamImages.initialized) return;  // run once
70804                 svgOpenstreetcamImages.enabled = false;
70805                 svgOpenstreetcamImages.initialized = true;
70806             }
70807
70808
70809             function getService() {
70810                 if (services.openstreetcam && !_openstreetcam) {
70811                     _openstreetcam = services.openstreetcam;
70812                     _openstreetcam.event.on('loadedImages', throttledRedraw);
70813                 } else if (!services.openstreetcam && _openstreetcam) {
70814                     _openstreetcam = null;
70815                 }
70816
70817                 return _openstreetcam;
70818             }
70819
70820
70821             function showLayer() {
70822                 var service = getService();
70823                 if (!service) return;
70824
70825                 editOn();
70826
70827                 layer
70828                     .style('opacity', 0)
70829                     .transition()
70830                     .duration(250)
70831                     .style('opacity', 1)
70832                     .on('end', function () { dispatch.call('change'); });
70833             }
70834
70835
70836             function hideLayer() {
70837                 throttledRedraw.cancel();
70838
70839                 layer
70840                     .transition()
70841                     .duration(250)
70842                     .style('opacity', 0)
70843                     .on('end', editOff);
70844             }
70845
70846
70847             function editOn() {
70848                 layer.style('display', 'block');
70849             }
70850
70851
70852             function editOff() {
70853                 layer.selectAll('.viewfield-group').remove();
70854                 layer.style('display', 'none');
70855             }
70856
70857
70858             function click(d) {
70859                 var service = getService();
70860                 if (!service) return;
70861
70862                 service
70863                     .selectImage(context, d)
70864                     .updateViewer(context, d)
70865                     .showViewer(context);
70866
70867                 context.map().centerEase(d.loc);
70868             }
70869
70870
70871             function mouseover(d) {
70872                 var service = getService();
70873                 if (service) service.setStyles(context, d);
70874             }
70875
70876
70877             function mouseout() {
70878                 var service = getService();
70879                 if (service) service.setStyles(context, null);
70880             }
70881
70882
70883             function transform(d) {
70884                 var t = svgPointTransform(projection)(d);
70885                 if (d.ca) {
70886                     t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
70887                 }
70888                 return t;
70889             }
70890
70891
70892             context.photos().on('change.openstreetcam_images', update);
70893
70894             function update() {
70895                 var viewer = context.container().select('.photoviewer');
70896                 var selected = viewer.empty() ? undefined : viewer.datum();
70897
70898                 var z = ~~context.map().zoom();
70899                 var showMarkers = (z >= minMarkerZoom);
70900                 var showViewfields = (z >= minViewfieldZoom);
70901
70902                 var service = getService();
70903                 var sequences = [];
70904                 var images = [];
70905
70906                 if (context.photos().showsFlat()) {
70907                     sequences = (service ? service.sequences(projection) : []);
70908                     images = (service && showMarkers ? service.images(projection) : []);
70909                 }
70910
70911                 var traces = layer.selectAll('.sequences').selectAll('.sequence')
70912                     .data(sequences, function(d) { return d.properties.key; });
70913
70914                 // exit
70915                 traces.exit()
70916                     .remove();
70917
70918                 // enter/update
70919                 traces = traces.enter()
70920                     .append('path')
70921                     .attr('class', 'sequence')
70922                     .merge(traces)
70923                     .attr('d', svgPath(projection).geojson);
70924
70925
70926                 var groups = layer.selectAll('.markers').selectAll('.viewfield-group')
70927                     .data(images, function(d) { return d.key; });
70928
70929                 // exit
70930                 groups.exit()
70931                     .remove();
70932
70933                 // enter
70934                 var groupsEnter = groups.enter()
70935                     .append('g')
70936                     .attr('class', 'viewfield-group')
70937                     .on('mouseenter', mouseover)
70938                     .on('mouseleave', mouseout)
70939                     .on('click', click);
70940
70941                 groupsEnter
70942                     .append('g')
70943                     .attr('class', 'viewfield-scale');
70944
70945                 // update
70946                 var markers = groups
70947                     .merge(groupsEnter)
70948                     .sort(function(a, b) {
70949                         return (a === selected) ? 1
70950                             : (b === selected) ? -1
70951                             : b.loc[1] - a.loc[1];  // sort Y
70952                     })
70953                     .attr('transform', transform)
70954                     .select('.viewfield-scale');
70955
70956
70957                 markers.selectAll('circle')
70958                     .data([0])
70959                     .enter()
70960                     .append('circle')
70961                     .attr('dx', '0')
70962                     .attr('dy', '0')
70963                     .attr('r', '6');
70964
70965                 var viewfields = markers.selectAll('.viewfield')
70966                     .data(showViewfields ? [0] : []);
70967
70968                 viewfields.exit()
70969                     .remove();
70970
70971                 viewfields.enter()               // viewfields may or may not be drawn...
70972                     .insert('path', 'circle')    // but if they are, draw below the circles
70973                     .attr('class', 'viewfield')
70974                     .attr('transform', 'scale(1.5,1.5),translate(-8, -13)')
70975                     .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');
70976             }
70977
70978
70979             function drawImages(selection) {
70980                 var enabled = svgOpenstreetcamImages.enabled,
70981                     service = getService();
70982
70983                 layer = selection.selectAll('.layer-openstreetcam')
70984                     .data(service ? [0] : []);
70985
70986                 layer.exit()
70987                     .remove();
70988
70989                 var layerEnter = layer.enter()
70990                     .append('g')
70991                     .attr('class', 'layer-openstreetcam')
70992                     .style('display', enabled ? 'block' : 'none');
70993
70994                 layerEnter
70995                     .append('g')
70996                     .attr('class', 'sequences');
70997
70998                 layerEnter
70999                     .append('g')
71000                     .attr('class', 'markers');
71001
71002                 layer = layerEnter
71003                     .merge(layer);
71004
71005                 if (enabled) {
71006                     if (service && ~~context.map().zoom() >= minZoom) {
71007                         editOn();
71008                         update();
71009                         service.loadImages(projection);
71010                     } else {
71011                         editOff();
71012                     }
71013                 }
71014             }
71015
71016
71017             drawImages.enabled = function(_) {
71018                 if (!arguments.length) return svgOpenstreetcamImages.enabled;
71019                 svgOpenstreetcamImages.enabled = _;
71020                 if (svgOpenstreetcamImages.enabled) {
71021                     showLayer();
71022                 } else {
71023                     hideLayer();
71024                 }
71025                 dispatch.call('change');
71026                 return this;
71027             };
71028
71029
71030             drawImages.supported = function() {
71031                 return !!getService();
71032             };
71033
71034
71035             init();
71036             return drawImages;
71037         }
71038
71039         function svgOsm(projection, context, dispatch) {
71040             var enabled = true;
71041
71042
71043             function drawOsm(selection) {
71044                 selection.selectAll('.layer-osm')
71045                     .data(['covered', 'areas', 'lines', 'points', 'labels'])
71046                     .enter()
71047                     .append('g')
71048                     .attr('class', function(d) { return 'layer-osm ' + d; });
71049
71050                 selection.selectAll('.layer-osm.points').selectAll('.points-group')
71051                     .data(['points', 'midpoints', 'vertices', 'turns'])
71052                     .enter()
71053                     .append('g')
71054                     .attr('class', function(d) { return 'points-group ' + d; });
71055             }
71056
71057
71058             function showLayer() {
71059                 var layer = context.surface().selectAll('.data-layer.osm');
71060                 layer.interrupt();
71061
71062                 layer
71063                     .classed('disabled', false)
71064                     .style('opacity', 0)
71065                     .transition()
71066                     .duration(250)
71067                     .style('opacity', 1)
71068                     .on('end interrupt', function () {
71069                         dispatch.call('change');
71070                     });
71071             }
71072
71073
71074             function hideLayer() {
71075                 var layer = context.surface().selectAll('.data-layer.osm');
71076                 layer.interrupt();
71077
71078                 layer
71079                     .transition()
71080                     .duration(250)
71081                     .style('opacity', 0)
71082                     .on('end interrupt', function () {
71083                         layer.classed('disabled', true);
71084                         dispatch.call('change');
71085                     });
71086             }
71087
71088
71089             drawOsm.enabled = function(val) {
71090                 if (!arguments.length) return enabled;
71091                 enabled = val;
71092
71093                 if (enabled) {
71094                     showLayer();
71095                 } else {
71096                     hideLayer();
71097                 }
71098
71099                 dispatch.call('change');
71100                 return this;
71101             };
71102
71103
71104             return drawOsm;
71105         }
71106
71107         var _notesEnabled = false;
71108         var _osmService;
71109
71110
71111         function svgNotes(projection, context, dispatch$1) {
71112             if (!dispatch$1) { dispatch$1 = dispatch('change'); }
71113             var throttledRedraw = throttle(function () { dispatch$1.call('change'); }, 1000);
71114             var minZoom = 12;
71115             var touchLayer = select(null);
71116             var drawLayer = select(null);
71117             var _notesVisible = false;
71118
71119
71120             function markerPath(selection, klass) {
71121                 selection
71122                     .attr('class', klass)
71123                     .attr('transform', 'translate(-8, -22)')
71124                     .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');
71125             }
71126
71127
71128             // Loosely-coupled osm service for fetching notes.
71129             function getService() {
71130                 if (services.osm && !_osmService) {
71131                     _osmService = services.osm;
71132                     _osmService.on('loadedNotes', throttledRedraw);
71133                 } else if (!services.osm && _osmService) {
71134                     _osmService = null;
71135                 }
71136
71137                 return _osmService;
71138             }
71139
71140
71141             // Show the notes
71142             function editOn() {
71143                 if (!_notesVisible) {
71144                     _notesVisible = true;
71145                     drawLayer
71146                         .style('display', 'block');
71147                 }
71148             }
71149
71150
71151             // Immediately remove the notes and their touch targets
71152             function editOff() {
71153                 if (_notesVisible) {
71154                     _notesVisible = false;
71155                     drawLayer
71156                         .style('display', 'none');
71157                     drawLayer.selectAll('.note')
71158                         .remove();
71159                     touchLayer.selectAll('.note')
71160                         .remove();
71161                 }
71162             }
71163
71164
71165             // Enable the layer.  This shows the notes and transitions them to visible.
71166             function layerOn() {
71167                 editOn();
71168
71169                 drawLayer
71170                     .style('opacity', 0)
71171                     .transition()
71172                     .duration(250)
71173                     .style('opacity', 1)
71174                     .on('end interrupt', function () {
71175                         dispatch$1.call('change');
71176                     });
71177             }
71178
71179
71180             // Disable the layer.  This transitions the layer invisible and then hides the notes.
71181             function layerOff() {
71182                 throttledRedraw.cancel();
71183                 drawLayer.interrupt();
71184                 touchLayer.selectAll('.note')
71185                     .remove();
71186
71187                 drawLayer
71188                     .transition()
71189                     .duration(250)
71190                     .style('opacity', 0)
71191                     .on('end interrupt', function () {
71192                         editOff();
71193                         dispatch$1.call('change');
71194                     });
71195             }
71196
71197
71198             // Update the note markers
71199             function updateMarkers() {
71200                 if (!_notesVisible || !_notesEnabled) return;
71201
71202                 var service = getService();
71203                 var selectedID = context.selectedNoteID();
71204                 var data = (service ? service.notes(projection) : []);
71205                 var getTransform = svgPointTransform(projection);
71206
71207                 // Draw markers..
71208                 var notes = drawLayer.selectAll('.note')
71209                     .data(data, function(d) { return d.status + d.id; });
71210
71211                 // exit
71212                 notes.exit()
71213                     .remove();
71214
71215                 // enter
71216                 var notesEnter = notes.enter()
71217                     .append('g')
71218                     .attr('class', function(d) { return 'note note-' + d.id + ' ' + d.status; })
71219                     .classed('new', function(d) { return d.id < 0; });
71220
71221                 notesEnter
71222                     .append('ellipse')
71223                     .attr('cx', 0.5)
71224                     .attr('cy', 1)
71225                     .attr('rx', 6.5)
71226                     .attr('ry', 3)
71227                     .attr('class', 'stroke');
71228
71229                 notesEnter
71230                     .append('path')
71231                     .call(markerPath, 'shadow');
71232
71233                 notesEnter
71234                     .append('use')
71235                     .attr('class', 'note-fill')
71236                     .attr('width', '20px')
71237                     .attr('height', '20px')
71238                     .attr('x', '-8px')
71239                     .attr('y', '-22px')
71240                     .attr('xlink:href', '#iD-icon-note');
71241
71242                 notesEnter.selectAll('.icon-annotation')
71243                     .data(function(d) { return [d]; })
71244                     .enter()
71245                     .append('use')
71246                     .attr('class', 'icon-annotation')
71247                     .attr('width', '10px')
71248                     .attr('height', '10px')
71249                     .attr('x', '-3px')
71250                     .attr('y', '-19px')
71251                     .attr('xlink:href', function(d) {
71252                         return '#iD-icon-' + (d.id < 0 ? 'plus' : (d.status === 'open' ? 'close' : 'apply'));
71253                     });
71254
71255                 // update
71256                 notes
71257                     .merge(notesEnter)
71258                     .sort(sortY)
71259                     .classed('selected', function(d) {
71260                         var mode = context.mode();
71261                         var isMoving = mode && mode.id === 'drag-note';  // no shadows when dragging
71262                         return !isMoving && d.id === selectedID;
71263                     })
71264                     .attr('transform', getTransform);
71265
71266
71267                 // Draw targets..
71268                 if (touchLayer.empty()) return;
71269                 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
71270
71271                 var targets = touchLayer.selectAll('.note')
71272                     .data(data, function(d) { return d.id; });
71273
71274                 // exit
71275                 targets.exit()
71276                     .remove();
71277
71278                 // enter/update
71279                 targets.enter()
71280                     .append('rect')
71281                     .attr('width', '20px')
71282                     .attr('height', '20px')
71283                     .attr('x', '-8px')
71284                     .attr('y', '-22px')
71285                     .merge(targets)
71286                     .sort(sortY)
71287                     .attr('class', function(d) {
71288                         var newClass = (d.id < 0 ? 'new' : '');
71289                         return 'note target note-' + d.id + ' ' + fillClass + newClass;
71290                     })
71291                     .attr('transform', getTransform);
71292
71293
71294                 function sortY(a, b) {
71295                     return (a.id === selectedID) ? 1 : (b.id === selectedID) ? -1 : b.loc[1] - a.loc[1];
71296                 }
71297             }
71298
71299
71300             // Draw the notes layer and schedule loading notes and updating markers.
71301             function drawNotes(selection) {
71302                 var service = getService();
71303
71304                 var surface = context.surface();
71305                 if (surface && !surface.empty()) {
71306                     touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
71307                 }
71308
71309                 drawLayer = selection.selectAll('.layer-notes')
71310                     .data(service ? [0] : []);
71311
71312                 drawLayer.exit()
71313                     .remove();
71314
71315                 drawLayer = drawLayer.enter()
71316                     .append('g')
71317                     .attr('class', 'layer-notes')
71318                     .style('display', _notesEnabled ? 'block' : 'none')
71319                     .merge(drawLayer);
71320
71321                 if (_notesEnabled) {
71322                     if (service && ~~context.map().zoom() >= minZoom) {
71323                         editOn();
71324                         service.loadNotes(projection);
71325                         updateMarkers();
71326                     } else {
71327                         editOff();
71328                     }
71329                 }
71330             }
71331
71332
71333             // Toggles the layer on and off
71334             drawNotes.enabled = function(val) {
71335                 if (!arguments.length) return _notesEnabled;
71336
71337                 _notesEnabled = val;
71338                 if (_notesEnabled) {
71339                     layerOn();
71340                 } else {
71341                     layerOff();
71342                     if (context.selectedNoteID()) {
71343                         context.enter(modeBrowse(context));
71344                     }
71345                 }
71346
71347                 dispatch$1.call('change');
71348                 return this;
71349             };
71350
71351
71352             return drawNotes;
71353         }
71354
71355         function svgTouch() {
71356
71357             function drawTouch(selection) {
71358                 selection.selectAll('.layer-touch')
71359                     .data(['areas', 'lines', 'points', 'turns', 'markers'])
71360                     .enter()
71361                     .append('g')
71362                     .attr('class', function(d) { return 'layer-touch ' + d; });
71363             }
71364
71365             return drawTouch;
71366         }
71367
71368         function refresh(selection, node) {
71369             var cr = node.getBoundingClientRect();
71370             var prop = [cr.width, cr.height];
71371             selection.property('__dimensions__', prop);
71372             return prop;
71373         }
71374
71375         function utilGetDimensions(selection, force) {
71376             if (!selection || selection.empty()) {
71377                 return [0, 0];
71378             }
71379             var node = selection.node(),
71380                 cached = selection.property('__dimensions__');
71381             return (!cached || force) ? refresh(selection, node) : cached;
71382         }
71383
71384
71385         function utilSetDimensions(selection, dimensions) {
71386             if (!selection || selection.empty()) {
71387                 return selection;
71388             }
71389             var node = selection.node();
71390             if (dimensions === null) {
71391                 refresh(selection, node);
71392                 return selection;
71393             }
71394             return selection
71395                 .property('__dimensions__', [dimensions[0], dimensions[1]])
71396                 .attr('width', dimensions[0])
71397                 .attr('height', dimensions[1]);
71398         }
71399
71400         function svgLayers(projection, context) {
71401             var dispatch$1 = dispatch('change');
71402             var svg = select(null);
71403             var _layers = [
71404                 { id: 'osm', layer: svgOsm(projection, context, dispatch$1) },
71405                 { id: 'notes', layer: svgNotes(projection, context, dispatch$1) },
71406                 { id: 'data', layer: svgData(projection, context, dispatch$1) },
71407                 { id: 'keepRight', layer: svgKeepRight(projection, context, dispatch$1) },
71408                 { id: 'improveOSM', layer: svgImproveOSM(projection, context, dispatch$1) },
71409                 { id: 'osmose', layer: svgOsmose(projection, context, dispatch$1) },
71410                 { id: 'streetside', layer: svgStreetside(projection, context, dispatch$1)},
71411                 { id: 'mapillary', layer: svgMapillaryImages(projection, context, dispatch$1) },
71412                 { id: 'mapillary-map-features',  layer: svgMapillaryMapFeatures(projection, context, dispatch$1) },
71413                 { id: 'mapillary-signs',  layer: svgMapillarySigns(projection, context, dispatch$1) },
71414                 { id: 'openstreetcam', layer: svgOpenstreetcamImages(projection, context, dispatch$1) },
71415                 { id: 'debug', layer: svgDebug(projection, context) },
71416                 { id: 'geolocate', layer: svgGeolocate(projection) },
71417                 { id: 'touch', layer: svgTouch() }
71418             ];
71419
71420
71421             function drawLayers(selection) {
71422                 svg = selection.selectAll('.surface')
71423                     .data([0]);
71424
71425                 svg = svg.enter()
71426                     .append('svg')
71427                     .attr('class', 'surface')
71428                     .merge(svg);
71429
71430                 var defs = svg.selectAll('.surface-defs')
71431                     .data([0]);
71432
71433                 defs.enter()
71434                     .append('defs')
71435                     .attr('class', 'surface-defs');
71436
71437                 var groups = svg.selectAll('.data-layer')
71438                     .data(_layers);
71439
71440                 groups.exit()
71441                     .remove();
71442
71443                 groups.enter()
71444                     .append('g')
71445                     .attr('class', function(d) { return 'data-layer ' + d.id; })
71446                     .merge(groups)
71447                     .each(function(d) { select(this).call(d.layer); });
71448             }
71449
71450
71451             drawLayers.all = function() {
71452                 return _layers;
71453             };
71454
71455
71456             drawLayers.layer = function(id) {
71457                 var obj = _layers.find(function(o) { return o.id === id; });
71458                 return obj && obj.layer;
71459             };
71460
71461
71462             drawLayers.only = function(what) {
71463                 var arr = [].concat(what);
71464                 var all = _layers.map(function(layer) { return layer.id; });
71465                 return drawLayers.remove(utilArrayDifference(all, arr));
71466             };
71467
71468
71469             drawLayers.remove = function(what) {
71470                 var arr = [].concat(what);
71471                 arr.forEach(function(id) {
71472                     _layers = _layers.filter(function(o) { return o.id !== id; });
71473                 });
71474                 dispatch$1.call('change');
71475                 return this;
71476             };
71477
71478
71479             drawLayers.add = function(what) {
71480                 var arr = [].concat(what);
71481                 arr.forEach(function(obj) {
71482                     if ('id' in obj && 'layer' in obj) {
71483                         _layers.push(obj);
71484                     }
71485                 });
71486                 dispatch$1.call('change');
71487                 return this;
71488             };
71489
71490
71491             drawLayers.dimensions = function(val) {
71492                 if (!arguments.length) return utilGetDimensions(svg);
71493                 utilSetDimensions(svg, val);
71494                 return this;
71495             };
71496
71497
71498             return utilRebind(drawLayers, dispatch$1, 'on');
71499         }
71500
71501         function svgLines(projection, context) {
71502             var detected = utilDetect();
71503
71504             var highway_stack = {
71505                 motorway: 0,
71506                 motorway_link: 1,
71507                 trunk: 2,
71508                 trunk_link: 3,
71509                 primary: 4,
71510                 primary_link: 5,
71511                 secondary: 6,
71512                 tertiary: 7,
71513                 unclassified: 8,
71514                 residential: 9,
71515                 service: 10,
71516                 footway: 11
71517             };
71518
71519
71520             function drawTargets(selection, graph, entities, filter) {
71521                 var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
71522                 var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
71523                 var getPath = svgPath(projection).geojson;
71524                 var activeID = context.activeID();
71525                 var base = context.history().base();
71526
71527                 // The targets and nopes will be MultiLineString sub-segments of the ways
71528                 var data = { targets: [], nopes: [] };
71529
71530                 entities.forEach(function(way) {
71531                     var features = svgSegmentWay(way, graph, activeID);
71532                     data.targets.push.apply(data.targets, features.passive);
71533                     data.nopes.push.apply(data.nopes, features.active);
71534                 });
71535
71536
71537                 // Targets allow hover and vertex snapping
71538                 var targetData = data.targets.filter(getPath);
71539                 var targets = selection.selectAll('.line.target-allowed')
71540                     .filter(function(d) { return filter(d.properties.entity); })
71541                     .data(targetData, function key(d) { return d.id; });
71542
71543                 // exit
71544                 targets.exit()
71545                     .remove();
71546
71547                 var segmentWasEdited = function(d) {
71548                     var wayID = d.properties.entity.id;
71549                     // if the whole line was edited, don't draw segment changes
71550                     if (!base.entities[wayID] ||
71551                         !fastDeepEqual(graph.entities[wayID].nodes, base.entities[wayID].nodes)) {
71552                         return false;
71553                     }
71554                     return d.properties.nodes.some(function(n) {
71555                         return !base.entities[n.id] ||
71556                                !fastDeepEqual(graph.entities[n.id].loc, base.entities[n.id].loc);
71557                     });
71558                 };
71559
71560                 // enter/update
71561                 targets.enter()
71562                     .append('path')
71563                     .merge(targets)
71564                     .attr('d', getPath)
71565                     .attr('class', function(d) {
71566                         return 'way line target target-allowed ' + targetClass + d.id;
71567                     })
71568                     .classed('segment-edited', segmentWasEdited);
71569
71570                 // NOPE
71571                 var nopeData = data.nopes.filter(getPath);
71572                 var nopes = selection.selectAll('.line.target-nope')
71573                     .filter(function(d) { return filter(d.properties.entity); })
71574                     .data(nopeData, function key(d) { return d.id; });
71575
71576                 // exit
71577                 nopes.exit()
71578                     .remove();
71579
71580                 // enter/update
71581                 nopes.enter()
71582                     .append('path')
71583                     .merge(nopes)
71584                     .attr('d', getPath)
71585                     .attr('class', function(d) {
71586                         return 'way line target target-nope ' + nopeClass + d.id;
71587                     })
71588                     .classed('segment-edited', segmentWasEdited);
71589             }
71590
71591
71592             function drawLines(selection, graph, entities, filter) {
71593                 var base = context.history().base();
71594
71595                 function waystack(a, b) {
71596                     var selected = context.selectedIDs();
71597                     var scoreA = selected.indexOf(a.id) !== -1 ? 20 : 0;
71598                     var scoreB = selected.indexOf(b.id) !== -1 ? 20 : 0;
71599
71600                     if (a.tags.highway) { scoreA -= highway_stack[a.tags.highway]; }
71601                     if (b.tags.highway) { scoreB -= highway_stack[b.tags.highway]; }
71602                     return scoreA - scoreB;
71603                 }
71604
71605
71606                 function drawLineGroup(selection, klass, isSelected) {
71607                     // Note: Don't add `.selected` class in draw modes
71608                     var mode = context.mode();
71609                     var isDrawing = mode && /^draw/.test(mode.id);
71610                     var selectedClass = (!isDrawing && isSelected) ? 'selected ' : '';
71611
71612                     var lines = selection
71613                         .selectAll('path')
71614                         .filter(filter)
71615                         .data(getPathData(isSelected), osmEntity.key);
71616
71617                     lines.exit()
71618                         .remove();
71619
71620                     // Optimization: Call expensive TagClasses only on enter selection. This
71621                     // works because osmEntity.key is defined to include the entity v attribute.
71622                     lines.enter()
71623                         .append('path')
71624                         .attr('class', function(d) {
71625
71626                             var prefix = 'way line';
71627
71628                             // if this line isn't styled by its own tags
71629                             if (!d.hasInterestingTags()) {
71630
71631                                 var parentRelations = graph.parentRelations(d);
71632                                 var parentMultipolygons = parentRelations.filter(function(relation) {
71633                                     return relation.isMultipolygon();
71634                                 });
71635
71636                                 // and if it's a member of at least one multipolygon relation
71637                                 if (parentMultipolygons.length > 0 &&
71638                                     // and only multipolygon relations
71639                                     parentRelations.length === parentMultipolygons.length) {
71640                                     // then fudge the classes to style this as an area edge
71641                                     prefix = 'relation area';
71642                                 }
71643                             }
71644
71645                             var oldMPClass = oldMultiPolygonOuters[d.id] ? 'old-multipolygon ' : '';
71646                             return prefix + ' ' + klass + ' ' + selectedClass + oldMPClass + d.id;
71647                         })
71648                         .classed('added', function(d) {
71649                             return !base.entities[d.id];
71650                         })
71651                         .classed('geometry-edited', function(d) {
71652                             return graph.entities[d.id] &&
71653                                 base.entities[d.id] &&
71654                                 !fastDeepEqual(graph.entities[d.id].nodes, base.entities[d.id].nodes);
71655                         })
71656                         .classed('retagged', function(d) {
71657                             return graph.entities[d.id] &&
71658                                 base.entities[d.id] &&
71659                                 !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
71660                         })
71661                         .call(svgTagClasses())
71662                         .merge(lines)
71663                         .sort(waystack)
71664                         .attr('d', getPath)
71665                         .call(svgTagClasses().tags(svgRelationMemberTags(graph)));
71666
71667                     return selection;
71668                 }
71669
71670
71671                 function getPathData(isSelected) {
71672                     return function() {
71673                         var layer = this.parentNode.__data__;
71674                         var data = pathdata[layer] || [];
71675                         return data.filter(function(d) {
71676                             if (isSelected)
71677                                 return context.selectedIDs().indexOf(d.id) !== -1;
71678                             else
71679                                 return context.selectedIDs().indexOf(d.id) === -1;
71680                         });
71681                     };
71682                 }
71683
71684                 function addMarkers(layergroup, pathclass, groupclass, groupdata, marker) {
71685                     var markergroup = layergroup
71686                         .selectAll('g.' + groupclass)
71687                         .data([pathclass]);
71688
71689                     markergroup = markergroup.enter()
71690                         .append('g')
71691                         .attr('class', groupclass)
71692                         .merge(markergroup);
71693
71694                     var markers = markergroup
71695                         .selectAll('path')
71696                         .filter(filter)
71697                         .data(
71698                             function data() { return groupdata[this.parentNode.__data__] || []; },
71699                             function key(d) { return [d.id, d.index]; }
71700                         );
71701
71702                     markers.exit()
71703                         .remove();
71704
71705                     markers = markers.enter()
71706                         .append('path')
71707                         .attr('class', pathclass)
71708                         .merge(markers)
71709                         .attr('marker-mid', marker)
71710                         .attr('d', function(d) { return d.d; });
71711
71712                     if (detected.ie) {
71713                         markers.each(function() { this.parentNode.insertBefore(this, this); });
71714                     }
71715                 }
71716
71717
71718                 var getPath = svgPath(projection, graph);
71719                 var ways = [];
71720                 var onewaydata = {};
71721                 var sideddata = {};
71722                 var oldMultiPolygonOuters = {};
71723
71724                 for (var i = 0; i < entities.length; i++) {
71725                     var entity = entities[i];
71726                     var outer = osmOldMultipolygonOuterMember(entity, graph);
71727                     if (outer) {
71728                         ways.push(entity.mergeTags(outer.tags));
71729                         oldMultiPolygonOuters[outer.id] = true;
71730                     } else if (entity.geometry(graph) === 'line') {
71731                         ways.push(entity);
71732                     }
71733                 }
71734
71735                 ways = ways.filter(getPath);
71736                 var pathdata = utilArrayGroupBy(ways, function(way) { return way.layer(); });
71737
71738                 Object.keys(pathdata).forEach(function(k) {
71739                     var v = pathdata[k];
71740                     var onewayArr = v.filter(function(d) { return d.isOneWay(); });
71741                     var onewaySegments = svgMarkerSegments(
71742                         projection, graph, 35,
71743                         function shouldReverse(entity) { return entity.tags.oneway === '-1'; },
71744                         function bothDirections(entity) {
71745                             return entity.tags.oneway === 'reversible' || entity.tags.oneway === 'alternating';
71746                         }
71747                     );
71748                     onewaydata[k] = utilArrayFlatten(onewayArr.map(onewaySegments));
71749
71750                     var sidedArr = v.filter(function(d) { return d.isSided(); });
71751                     var sidedSegments = svgMarkerSegments(
71752                         projection, graph, 30,
71753                         function shouldReverse() { return false; },
71754                         function bothDirections() { return false; }
71755                     );
71756                     sideddata[k] = utilArrayFlatten(sidedArr.map(sidedSegments));
71757                 });
71758
71759
71760                 var covered = selection.selectAll('.layer-osm.covered');     // under areas
71761                 var uncovered = selection.selectAll('.layer-osm.lines');     // over areas
71762                 var touchLayer = selection.selectAll('.layer-touch.lines');
71763
71764                 // Draw lines..
71765                 [covered, uncovered].forEach(function(selection) {
71766                     var range = (selection === covered ? range$1(-10,0) : range$1(0,11));
71767                     var layergroup = selection
71768                         .selectAll('g.layergroup')
71769                         .data(range);
71770
71771                     layergroup = layergroup.enter()
71772                         .append('g')
71773                         .attr('class', function(d) { return 'layergroup layer' + String(d); })
71774                         .merge(layergroup);
71775
71776                     layergroup
71777                         .selectAll('g.linegroup')
71778                         .data(['shadow', 'casing', 'stroke', 'shadow-highlighted', 'casing-highlighted', 'stroke-highlighted'])
71779                         .enter()
71780                         .append('g')
71781                         .attr('class', function(d) { return 'linegroup line-' + d; });
71782
71783                     layergroup.selectAll('g.line-shadow')
71784                         .call(drawLineGroup, 'shadow', false);
71785                     layergroup.selectAll('g.line-casing')
71786                         .call(drawLineGroup, 'casing', false);
71787                     layergroup.selectAll('g.line-stroke')
71788                         .call(drawLineGroup, 'stroke', false);
71789
71790                     layergroup.selectAll('g.line-shadow-highlighted')
71791                         .call(drawLineGroup, 'shadow', true);
71792                     layergroup.selectAll('g.line-casing-highlighted')
71793                         .call(drawLineGroup, 'casing', true);
71794                     layergroup.selectAll('g.line-stroke-highlighted')
71795                         .call(drawLineGroup, 'stroke', true);
71796
71797                     addMarkers(layergroup, 'oneway', 'onewaygroup', onewaydata, 'url(#ideditor-oneway-marker)');
71798                     addMarkers(layergroup, 'sided', 'sidedgroup', sideddata,
71799                         function marker(d) {
71800                             var category = graph.entity(d.id).sidednessIdentifier();
71801                             return 'url(#ideditor-sided-marker-' + category + ')';
71802                         }
71803                     );
71804                 });
71805
71806                 // Draw touch targets..
71807                 touchLayer
71808                     .call(drawTargets, graph, ways, filter);
71809             }
71810
71811
71812             return drawLines;
71813         }
71814
71815         function svgMidpoints(projection, context) {
71816             var targetRadius = 8;
71817
71818             function drawTargets(selection, graph, entities, filter) {
71819                 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
71820                 var getTransform = svgPointTransform(projection).geojson;
71821
71822                 var data = entities.map(function(midpoint) {
71823                     return {
71824                         type: 'Feature',
71825                         id: midpoint.id,
71826                         properties: {
71827                             target: true,
71828                             entity: midpoint
71829                         },
71830                         geometry: {
71831                             type: 'Point',
71832                             coordinates: midpoint.loc
71833                         }
71834                     };
71835                 });
71836
71837                 var targets = selection.selectAll('.midpoint.target')
71838                     .filter(function(d) { return filter(d.properties.entity); })
71839                     .data(data, function key(d) { return d.id; });
71840
71841                 // exit
71842                 targets.exit()
71843                     .remove();
71844
71845                 // enter/update
71846                 targets.enter()
71847                     .append('circle')
71848                     .attr('r', targetRadius)
71849                     .merge(targets)
71850                     .attr('class', function(d) { return 'node midpoint target ' + fillClass + d.id; })
71851                     .attr('transform', getTransform);
71852             }
71853
71854
71855             function drawMidpoints(selection, graph, entities, filter, extent) {
71856                 var drawLayer = selection.selectAll('.layer-osm.points .points-group.midpoints');
71857                 var touchLayer = selection.selectAll('.layer-touch.points');
71858
71859                 var mode = context.mode();
71860                 if ((mode && mode.id !== 'select') || !context.map().withinEditableZoom()) {
71861                     drawLayer.selectAll('.midpoint').remove();
71862                     touchLayer.selectAll('.midpoint.target').remove();
71863                     return;
71864                 }
71865
71866                 var poly = extent.polygon();
71867                 var midpoints = {};
71868
71869                 for (var i = 0; i < entities.length; i++) {
71870                     var entity = entities[i];
71871
71872                     if (entity.type !== 'way') continue;
71873                     if (!filter(entity)) continue;
71874                     if (context.selectedIDs().indexOf(entity.id) < 0) continue;
71875
71876                     var nodes = graph.childNodes(entity);
71877                     for (var j = 0; j < nodes.length - 1; j++) {
71878                         var a = nodes[j];
71879                         var b = nodes[j + 1];
71880                         var id = [a.id, b.id].sort().join('-');
71881
71882                         if (midpoints[id]) {
71883                             midpoints[id].parents.push(entity);
71884                         } else if (geoVecLength(projection(a.loc), projection(b.loc)) > 40) {
71885                             var point = geoVecInterp(a.loc, b.loc, 0.5);
71886                             var loc = null;
71887
71888                             if (extent.intersects(point)) {
71889                                 loc = point;
71890                             } else {
71891                                 for (var k = 0; k < 4; k++) {
71892                                     point = geoLineIntersection([a.loc, b.loc], [poly[k], poly[k + 1]]);
71893                                     if (point &&
71894                                         geoVecLength(projection(a.loc), projection(point)) > 20 &&
71895                                         geoVecLength(projection(b.loc), projection(point)) > 20)
71896                                     {
71897                                         loc = point;
71898                                         break;
71899                                     }
71900                                 }
71901                             }
71902
71903                             if (loc) {
71904                                 midpoints[id] = {
71905                                     type: 'midpoint',
71906                                     id: id,
71907                                     loc: loc,
71908                                     edge: [a.id, b.id],
71909                                     parents: [entity]
71910                                 };
71911                             }
71912                         }
71913                     }
71914                 }
71915
71916
71917                 function midpointFilter(d) {
71918                     if (midpoints[d.id])
71919                         return true;
71920
71921                     for (var i = 0; i < d.parents.length; i++) {
71922                         if (filter(d.parents[i])) {
71923                             return true;
71924                         }
71925                     }
71926
71927                     return false;
71928                 }
71929
71930
71931                 var groups = drawLayer.selectAll('.midpoint')
71932                     .filter(midpointFilter)
71933                     .data(Object.values(midpoints), function(d) { return d.id; });
71934
71935                 groups.exit()
71936                     .remove();
71937
71938                 var enter = groups.enter()
71939                     .insert('g', ':first-child')
71940                     .attr('class', 'midpoint');
71941
71942                 enter
71943                     .append('polygon')
71944                     .attr('points', '-6,8 10,0 -6,-8')
71945                     .attr('class', 'shadow');
71946
71947                 enter
71948                     .append('polygon')
71949                     .attr('points', '-3,4 5,0 -3,-4')
71950                     .attr('class', 'fill');
71951
71952                 groups = groups
71953                     .merge(enter)
71954                     .attr('transform', function(d) {
71955                         var translate = svgPointTransform(projection);
71956                         var a = graph.entity(d.edge[0]);
71957                         var b = graph.entity(d.edge[1]);
71958                         var angle = geoAngle(a, b, projection) * (180 / Math.PI);
71959                         return translate(d) + ' rotate(' + angle + ')';
71960                     })
71961                     .call(svgTagClasses().tags(
71962                         function(d) { return d.parents[0].tags; }
71963                     ));
71964
71965                 // Propagate data bindings.
71966                 groups.select('polygon.shadow');
71967                 groups.select('polygon.fill');
71968
71969
71970                 // Draw touch targets..
71971                 touchLayer
71972                     .call(drawTargets, graph, Object.values(midpoints), midpointFilter);
71973             }
71974
71975             return drawMidpoints;
71976         }
71977
71978         function svgPoints(projection, context) {
71979
71980             function markerPath(selection, klass) {
71981                 selection
71982                     .attr('class', klass)
71983                     .attr('transform', 'translate(-8, -23)')
71984                     .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');
71985             }
71986
71987             function sortY(a, b) {
71988                 return b.loc[1] - a.loc[1];
71989             }
71990
71991
71992             // Avoid exit/enter if we're just moving stuff around.
71993             // The node will get a new version but we only need to run the update selection.
71994             function fastEntityKey(d) {
71995                 var mode = context.mode();
71996                 var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id);
71997                 return isMoving ? d.id : osmEntity.key(d);
71998             }
71999
72000
72001             function drawTargets(selection, graph, entities, filter) {
72002                 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
72003                 var getTransform = svgPointTransform(projection).geojson;
72004                 var activeID = context.activeID();
72005                 var data = [];
72006
72007                 entities.forEach(function(node) {
72008                     if (activeID === node.id) return;   // draw no target on the activeID
72009
72010                     data.push({
72011                         type: 'Feature',
72012                         id: node.id,
72013                         properties: {
72014                             target: true,
72015                             entity: node
72016                         },
72017                         geometry: node.asGeoJSON()
72018                     });
72019                 });
72020
72021                 var targets = selection.selectAll('.point.target')
72022                     .filter(function(d) { return filter(d.properties.entity); })
72023                     .data(data, function key(d) { return d.id; });
72024
72025                 // exit
72026                 targets.exit()
72027                     .remove();
72028
72029                 // enter/update
72030                 targets.enter()
72031                     .append('rect')
72032                     .attr('x', -10)
72033                     .attr('y', -26)
72034                     .attr('width', 20)
72035                     .attr('height', 30)
72036                     .merge(targets)
72037                     .attr('class', function(d) { return 'node point target ' + fillClass + d.id; })
72038                     .attr('transform', getTransform);
72039             }
72040
72041
72042             function drawPoints(selection, graph, entities, filter) {
72043                 var wireframe = context.surface().classed('fill-wireframe');
72044                 var zoom = geoScaleToZoom(projection.scale());
72045                 var base = context.history().base();
72046
72047                 // Points with a direction will render as vertices at higher zooms..
72048                 function renderAsPoint(entity) {
72049                     return entity.geometry(graph) === 'point' &&
72050                         !(zoom >= 18 && entity.directions(graph, projection).length);
72051                 }
72052
72053                 // All points will render as vertices in wireframe mode too..
72054                 var points = wireframe ? [] : entities.filter(renderAsPoint);
72055                 points.sort(sortY);
72056
72057
72058                 var drawLayer = selection.selectAll('.layer-osm.points .points-group.points');
72059                 var touchLayer = selection.selectAll('.layer-touch.points');
72060
72061                 // Draw points..
72062                 var groups = drawLayer.selectAll('g.point')
72063                     .filter(filter)
72064                     .data(points, fastEntityKey);
72065
72066                 groups.exit()
72067                     .remove();
72068
72069                 var enter = groups.enter()
72070                     .append('g')
72071                     .attr('class', function(d) { return 'node point ' + d.id; })
72072                     .order();
72073
72074                 enter
72075                     .append('path')
72076                     .call(markerPath, 'shadow');
72077
72078                 enter
72079                     .append('ellipse')
72080                     .attr('cx', 0.5)
72081                     .attr('cy', 1)
72082                     .attr('rx', 6.5)
72083                     .attr('ry', 3)
72084                     .attr('class', 'stroke');
72085
72086                 enter
72087                     .append('path')
72088                     .call(markerPath, 'stroke');
72089
72090                 enter
72091                     .append('use')
72092                     .attr('transform', 'translate(-5, -19)')
72093                     .attr('class', 'icon')
72094                     .attr('width', '11px')
72095                     .attr('height', '11px');
72096
72097                 groups = groups
72098                     .merge(enter)
72099                     .attr('transform', svgPointTransform(projection))
72100                     .classed('added', function(d) {
72101                         return !base.entities[d.id]; // if it doesn't exist in the base graph, it's new
72102                     })
72103                     .classed('moved', function(d) {
72104                         return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].loc, base.entities[d.id].loc);
72105                     })
72106                     .classed('retagged', function(d) {
72107                         return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
72108                     })
72109                     .call(svgTagClasses());
72110
72111                 groups.select('.shadow');   // propagate bound data
72112                 groups.select('.stroke');   // propagate bound data
72113                 groups.select('.icon')      // propagate bound data
72114                     .attr('xlink:href', function(entity) {
72115                         var preset = _mainPresetIndex.match(entity, graph);
72116                         var picon = preset && preset.icon;
72117
72118                         if (!picon) {
72119                             return '';
72120                         } else {
72121                             var isMaki = /^maki-/.test(picon);
72122                             return '#' + picon + (isMaki ? '-11' : '');
72123                         }
72124                     });
72125
72126
72127                 // Draw touch targets..
72128                 touchLayer
72129                     .call(drawTargets, graph, points, filter);
72130             }
72131
72132
72133             return drawPoints;
72134         }
72135
72136         function svgTurns(projection, context) {
72137
72138             function icon(turn) {
72139                 var u = turn.u ? '-u' : '';
72140                 if (turn.no) return '#iD-turn-no' + u;
72141                 if (turn.only) return '#iD-turn-only' + u;
72142                 return '#iD-turn-yes' + u;
72143             }
72144
72145             function drawTurns(selection, graph, turns) {
72146
72147                 function turnTransform(d) {
72148                     var pxRadius = 50;
72149                     var toWay = graph.entity(d.to.way);
72150                     var toPoints = graph.childNodes(toWay)
72151                         .map(function (n) { return n.loc; })
72152                         .map(projection);
72153                     var toLength = geoPathLength(toPoints);
72154                     var mid = toLength / 2;    // midpoint of destination way
72155
72156                     var toNode = graph.entity(d.to.node);
72157                     var toVertex = graph.entity(d.to.vertex);
72158                     var a = geoAngle(toVertex, toNode, projection);
72159                     var o = projection(toVertex.loc);
72160                     var r = d.u ? 0                  // u-turn: no radius
72161                         : !toWay.__via ? pxRadius    // leaf way: put marker at pxRadius
72162                         : Math.min(mid, pxRadius);   // via way: prefer pxRadius, fallback to mid for very short ways
72163
72164                     return 'translate(' + (r * Math.cos(a) + o[0]) + ',' + (r * Math.sin(a) + o[1]) + ') ' +
72165                         'rotate(' + a * 180 / Math.PI + ')';
72166                 }
72167
72168
72169                 var drawLayer = selection.selectAll('.layer-osm.points .points-group.turns');
72170                 var touchLayer = selection.selectAll('.layer-touch.turns');
72171
72172                 // Draw turns..
72173                 var groups = drawLayer.selectAll('g.turn')
72174                     .data(turns, function(d) { return d.key; });
72175
72176                 // exit
72177                 groups.exit()
72178                     .remove();
72179
72180                 // enter
72181                 var groupsEnter = groups.enter()
72182                     .append('g')
72183                     .attr('class', function(d) { return 'turn ' + d.key; });
72184
72185                 var turnsEnter = groupsEnter
72186                     .filter(function(d) { return !d.u; });
72187
72188                 turnsEnter.append('rect')
72189                     .attr('transform', 'translate(-22, -12)')
72190                     .attr('width', '44')
72191                     .attr('height', '24');
72192
72193                 turnsEnter.append('use')
72194                     .attr('transform', 'translate(-22, -12)')
72195                     .attr('width', '44')
72196                     .attr('height', '24');
72197
72198                 var uEnter = groupsEnter
72199                     .filter(function(d) { return d.u; });
72200
72201                 uEnter.append('circle')
72202                     .attr('r', '16');
72203
72204                 uEnter.append('use')
72205                     .attr('transform', 'translate(-16, -16)')
72206                     .attr('width', '32')
72207                     .attr('height', '32');
72208
72209                 // update
72210                 groups = groups
72211                     .merge(groupsEnter)
72212                     .attr('opacity', function(d) { return d.direct === false ? '0.7' : null; })
72213                     .attr('transform', turnTransform);
72214
72215                 groups.select('use')
72216                     .attr('xlink:href', icon);
72217
72218                 groups.select('rect');      // propagate bound data
72219                 groups.select('circle');    // propagate bound data
72220
72221
72222                 // Draw touch targets..
72223                 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
72224                 groups = touchLayer.selectAll('g.turn')
72225                     .data(turns, function(d) { return d.key; });
72226
72227                 // exit
72228                 groups.exit()
72229                     .remove();
72230
72231                 // enter
72232                 groupsEnter = groups.enter()
72233                     .append('g')
72234                     .attr('class', function(d) { return 'turn ' + d.key; });
72235
72236                 turnsEnter = groupsEnter
72237                     .filter(function(d) { return !d.u; });
72238
72239                 turnsEnter.append('rect')
72240                     .attr('class', 'target ' + fillClass)
72241                     .attr('transform', 'translate(-22, -12)')
72242                     .attr('width', '44')
72243                     .attr('height', '24');
72244
72245                 uEnter = groupsEnter
72246                     .filter(function(d) { return d.u; });
72247
72248                 uEnter.append('circle')
72249                     .attr('class', 'target ' + fillClass)
72250                     .attr('r', '16');
72251
72252                 // update
72253                 groups = groups
72254                     .merge(groupsEnter)
72255                     .attr('transform', turnTransform);
72256
72257                 groups.select('rect');      // propagate bound data
72258                 groups.select('circle');    // propagate bound data
72259
72260
72261                 return this;
72262             }
72263
72264             return drawTurns;
72265         }
72266
72267         function svgVertices(projection, context) {
72268             var radiuses = {
72269                 //       z16-, z17,   z18+,  w/icon
72270                 shadow: [6,    7.5,   7.5,   12],
72271                 stroke: [2.5,  3.5,   3.5,   8],
72272                 fill:   [1,    1.5,   1.5,   1.5]
72273             };
72274
72275             var _currHoverTarget;
72276             var _currPersistent = {};
72277             var _currHover = {};
72278             var _prevHover = {};
72279             var _currSelected = {};
72280             var _prevSelected = {};
72281             var _radii = {};
72282
72283
72284             function sortY(a, b) {
72285                 return b.loc[1] - a.loc[1];
72286             }
72287
72288             // Avoid exit/enter if we're just moving stuff around.
72289             // The node will get a new version but we only need to run the update selection.
72290             function fastEntityKey(d) {
72291                 var mode = context.mode();
72292                 var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id);
72293                 return isMoving ? d.id : osmEntity.key(d);
72294             }
72295
72296
72297             function draw(selection, graph, vertices, sets, filter) {
72298                 sets = sets || { selected: {}, important: {}, hovered: {} };
72299
72300                 var icons = {};
72301                 var directions = {};
72302                 var wireframe = context.surface().classed('fill-wireframe');
72303                 var zoom = geoScaleToZoom(projection.scale());
72304                 var z = (zoom < 17 ? 0 : zoom < 18 ? 1 : 2);
72305                 var activeID = context.activeID();
72306                 var base = context.history().base();
72307
72308
72309                 function getIcon(d) {
72310                     // always check latest entity, as fastEntityKey avoids enter/exit now
72311                     var entity = graph.entity(d.id);
72312                     if (entity.id in icons) return icons[entity.id];
72313
72314                     icons[entity.id] =
72315                         entity.hasInterestingTags() &&
72316                         _mainPresetIndex.match(entity, graph).icon;
72317
72318                     return icons[entity.id];
72319                 }
72320
72321
72322                 // memoize directions results, return false for empty arrays (for use in filter)
72323                 function getDirections(entity) {
72324                     if (entity.id in directions) return directions[entity.id];
72325
72326                     var angles = entity.directions(graph, projection);
72327                     directions[entity.id] = angles.length ? angles : false;
72328                     return angles;
72329                 }
72330
72331
72332                 function updateAttributes(selection) {
72333                     ['shadow', 'stroke', 'fill'].forEach(function(klass) {
72334                         var rads = radiuses[klass];
72335                         selection.selectAll('.' + klass)
72336                             .each(function(entity) {
72337                                 var i = z && getIcon(entity);
72338                                 var r = rads[i ? 3 : z];
72339
72340                                 // slightly increase the size of unconnected endpoints #3775
72341                                 if (entity.id !== activeID && entity.isEndpoint(graph) && !entity.isConnected(graph)) {
72342                                     r += 1.5;
72343                                 }
72344
72345                                 if (klass === 'shadow') {   // remember this value, so we don't need to
72346                                     _radii[entity.id] = r;  // recompute it when we draw the touch targets
72347                                 }
72348
72349                                 select(this)
72350                                     .attr('r', r)
72351                                     .attr('visibility', (i && klass === 'fill') ? 'hidden' : null);
72352                             });
72353                     });
72354                 }
72355
72356                 vertices.sort(sortY);
72357
72358                 var groups = selection.selectAll('g.vertex')
72359                     .filter(filter)
72360                     .data(vertices, fastEntityKey);
72361
72362                 // exit
72363                 groups.exit()
72364                     .remove();
72365
72366                 // enter
72367                 var enter = groups.enter()
72368                     .append('g')
72369                     .attr('class', function(d) { return 'node vertex ' + d.id; })
72370                     .order();
72371
72372                 enter
72373                     .append('circle')
72374                     .attr('class', 'shadow');
72375
72376                 enter
72377                     .append('circle')
72378                     .attr('class', 'stroke');
72379
72380                 // Vertices with tags get a fill.
72381                 enter.filter(function(d) { return d.hasInterestingTags(); })
72382                     .append('circle')
72383                     .attr('class', 'fill');
72384
72385                 // update
72386                 groups = groups
72387                     .merge(enter)
72388                     .attr('transform', svgPointTransform(projection))
72389                     .classed('sibling', function(d) { return d.id in sets.selected; })
72390                     .classed('shared', function(d) { return graph.isShared(d); })
72391                     .classed('endpoint', function(d) { return d.isEndpoint(graph); })
72392                     .classed('added', function(d) {
72393                         return !base.entities[d.id]; // if it doesn't exist in the base graph, it's new
72394                     })
72395                     .classed('moved', function(d) {
72396                         return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].loc, base.entities[d.id].loc);
72397                     })
72398                     .classed('retagged', function(d) {
72399                         return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
72400                     })
72401                     .call(updateAttributes);
72402
72403                 // Vertices with icons get a `use`.
72404                 var iconUse = groups
72405                     .selectAll('.icon')
72406                     .data(function data(d) { return zoom >= 17 && getIcon(d) ? [d] : []; }, fastEntityKey);
72407
72408                 // exit
72409                 iconUse.exit()
72410                     .remove();
72411
72412                 // enter
72413                 iconUse.enter()
72414                     .append('use')
72415                     .attr('class', 'icon')
72416                     .attr('width', '11px')
72417                     .attr('height', '11px')
72418                     .attr('transform', 'translate(-5.5, -5.5)')
72419                     .attr('xlink:href', function(d) {
72420                         var picon = getIcon(d);
72421                         var isMaki = /^maki-/.test(picon);
72422                         return '#' + picon + (isMaki ? '-11' : '');
72423                     });
72424
72425
72426                 // Vertices with directions get viewfields
72427                 var dgroups = groups
72428                     .selectAll('.viewfieldgroup')
72429                     .data(function data(d) { return zoom >= 18 && getDirections(d) ? [d] : []; }, fastEntityKey);
72430
72431                 // exit
72432                 dgroups.exit()
72433                     .remove();
72434
72435                 // enter/update
72436                 dgroups = dgroups.enter()
72437                     .insert('g', '.shadow')
72438                     .attr('class', 'viewfieldgroup')
72439                     .merge(dgroups);
72440
72441                 var viewfields = dgroups.selectAll('.viewfield')
72442                     .data(getDirections, function key(d) { return osmEntity.key(d); });
72443
72444                 // exit
72445                 viewfields.exit()
72446                     .remove();
72447
72448                 // enter/update
72449                 viewfields.enter()
72450                     .append('path')
72451                     .attr('class', 'viewfield')
72452                     .attr('d', 'M0,0H0')
72453                     .merge(viewfields)
72454                     .attr('marker-start', 'url(#ideditor-viewfield-marker' + (wireframe ? '-wireframe' : '') + ')')
72455                     .attr('transform', function(d) { return 'rotate(' + d + ')'; });
72456             }
72457
72458
72459             function drawTargets(selection, graph, entities, filter) {
72460                 var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
72461                 var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
72462                 var getTransform = svgPointTransform(projection).geojson;
72463                 var activeID = context.activeID();
72464                 var data = { targets: [], nopes: [] };
72465
72466                 entities.forEach(function(node) {
72467                     if (activeID === node.id) return;   // draw no target on the activeID
72468
72469                     var vertexType = svgPassiveVertex(node, graph, activeID);
72470                     if (vertexType !== 0) {     // passive or adjacent - allow to connect
72471                         data.targets.push({
72472                             type: 'Feature',
72473                             id: node.id,
72474                             properties: {
72475                                 target: true,
72476                                 entity: node
72477                             },
72478                             geometry: node.asGeoJSON()
72479                         });
72480                     } else {
72481                         data.nopes.push({
72482                             type: 'Feature',
72483                             id: node.id + '-nope',
72484                             properties: {
72485                                 nope: true,
72486                                 target: true,
72487                                 entity: node
72488                             },
72489                             geometry: node.asGeoJSON()
72490                         });
72491                     }
72492                 });
72493
72494                 // Targets allow hover and vertex snapping
72495                 var targets = selection.selectAll('.vertex.target-allowed')
72496                     .filter(function(d) { return filter(d.properties.entity); })
72497                     .data(data.targets, function key(d) { return d.id; });
72498
72499                 // exit
72500                 targets.exit()
72501                     .remove();
72502
72503                 // enter/update
72504                 targets.enter()
72505                     .append('circle')
72506                     .attr('r', function(d) {
72507                         return _radii[d.id]
72508                           || radiuses.shadow[3];
72509                     })
72510                     .merge(targets)
72511                     .attr('class', function(d) {
72512                         return 'node vertex target target-allowed '
72513                         + targetClass + d.id;
72514                     })
72515                     .attr('transform', getTransform);
72516
72517
72518                 // NOPE
72519                 var nopes = selection.selectAll('.vertex.target-nope')
72520                     .filter(function(d) { return filter(d.properties.entity); })
72521                     .data(data.nopes, function key(d) { return d.id; });
72522
72523                 // exit
72524                 nopes.exit()
72525                     .remove();
72526
72527                 // enter/update
72528                 nopes.enter()
72529                     .append('circle')
72530                     .attr('r', function(d) { return (_radii[d.properties.entity.id] || radiuses.shadow[3]); })
72531                     .merge(nopes)
72532                     .attr('class', function(d) { return 'node vertex target target-nope ' + nopeClass + d.id; })
72533                     .attr('transform', getTransform);
72534             }
72535
72536
72537             // Points can also render as vertices:
72538             // 1. in wireframe mode or
72539             // 2. at higher zooms if they have a direction
72540             function renderAsVertex(entity, graph, wireframe, zoom) {
72541                 var geometry = entity.geometry(graph);
72542                 return geometry === 'vertex' || (geometry === 'point' && (
72543                     wireframe || (zoom >= 18 && entity.directions(graph, projection).length)
72544                 ));
72545             }
72546
72547
72548             function isEditedNode(node, base, head) {
72549                 var baseNode = base.entities[node.id];
72550                 var headNode = head.entities[node.id];
72551                 return !headNode ||
72552                     !baseNode ||
72553                     !fastDeepEqual(headNode.tags, baseNode.tags) ||
72554                     !fastDeepEqual(headNode.loc, baseNode.loc);
72555             }
72556
72557
72558             function getSiblingAndChildVertices(ids, graph, wireframe, zoom) {
72559                 var results = {};
72560
72561                 var seenIds = {};
72562
72563                 function addChildVertices(entity) {
72564
72565                     // avoid redunant work and infinite recursion of circular relations
72566                     if (seenIds[entity.id]) return;
72567                     seenIds[entity.id] = true;
72568
72569                     var geometry = entity.geometry(graph);
72570                     if (!context.features().isHiddenFeature(entity, graph, geometry)) {
72571                         var i;
72572                         if (entity.type === 'way') {
72573                             for (i = 0; i < entity.nodes.length; i++) {
72574                                 var child = graph.hasEntity(entity.nodes[i]);
72575                                 if (child) {
72576                                     addChildVertices(child);
72577                                 }
72578                             }
72579                         } else if (entity.type === 'relation') {
72580                             for (i = 0; i < entity.members.length; i++) {
72581                                 var member = graph.hasEntity(entity.members[i].id);
72582                                 if (member) {
72583                                     addChildVertices(member);
72584                                 }
72585                             }
72586                         } else if (renderAsVertex(entity, graph, wireframe, zoom)) {
72587                             results[entity.id] = entity;
72588                         }
72589                     }
72590                 }
72591
72592                 ids.forEach(function(id) {
72593                     var entity = graph.hasEntity(id);
72594                     if (!entity) return;
72595
72596                     if (entity.type === 'node') {
72597                         if (renderAsVertex(entity, graph, wireframe, zoom)) {
72598                             results[entity.id] = entity;
72599                             graph.parentWays(entity).forEach(function(entity) {
72600                                 addChildVertices(entity);
72601                             });
72602                         }
72603                     } else {  // way, relation
72604                         addChildVertices(entity);
72605                     }
72606                 });
72607
72608                 return results;
72609             }
72610
72611
72612             function drawVertices(selection, graph, entities, filter, extent, fullRedraw) {
72613                 var wireframe = context.surface().classed('fill-wireframe');
72614                 var visualDiff = context.surface().classed('highlight-edited');
72615                 var zoom = geoScaleToZoom(projection.scale());
72616                 var mode = context.mode();
72617                 var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id);
72618                 var base = context.history().base();
72619
72620                 var drawLayer = selection.selectAll('.layer-osm.points .points-group.vertices');
72621                 var touchLayer = selection.selectAll('.layer-touch.points');
72622
72623                 if (fullRedraw) {
72624                     _currPersistent = {};
72625                     _radii = {};
72626                 }
72627
72628                 // Collect important vertices from the `entities` list..
72629                 // (during a paritial redraw, it will not contain everything)
72630                 for (var i = 0; i < entities.length; i++) {
72631                     var entity = entities[i];
72632                     var geometry = entity.geometry(graph);
72633                     var keep = false;
72634
72635                     // a point that looks like a vertex..
72636                     if ((geometry === 'point') && renderAsVertex(entity, graph, wireframe, zoom)) {
72637                         _currPersistent[entity.id] = entity;
72638                         keep = true;
72639
72640                     // a vertex of some importance..
72641                     } else if (geometry === 'vertex' &&
72642                         (entity.hasInterestingTags() || entity.isEndpoint(graph) || entity.isConnected(graph)
72643                         || (visualDiff && isEditedNode(entity, base, graph)))) {
72644                         _currPersistent[entity.id] = entity;
72645                         keep = true;
72646                     }
72647
72648                     // whatever this is, it's not a persistent vertex..
72649                     if (!keep && !fullRedraw) {
72650                         delete _currPersistent[entity.id];
72651                     }
72652                 }
72653
72654                 // 3 sets of vertices to consider:
72655                 var sets = {
72656                     persistent: _currPersistent,  // persistent = important vertices (render always)
72657                     selected: _currSelected,      // selected + siblings of selected (render always)
72658                     hovered: _currHover           // hovered + siblings of hovered (render only in draw modes)
72659                 };
72660
72661                 var all = Object.assign({}, (isMoving ? _currHover : {}), _currSelected, _currPersistent);
72662
72663                 // Draw the vertices..
72664                 // The filter function controls the scope of what objects d3 will touch (exit/enter/update)
72665                 // Adjust the filter function to expand the scope beyond whatever entities were passed in.
72666                 var filterRendered = function(d) {
72667                     return d.id in _currPersistent || d.id in _currSelected || d.id in _currHover || filter(d);
72668                 };
72669                 drawLayer
72670                     .call(draw, graph, currentVisible(all), sets, filterRendered);
72671
72672                 // Draw touch targets..
72673                 // When drawing, render all targets (not just those affected by a partial redraw)
72674                 var filterTouch = function(d) {
72675                     return isMoving ? true : filterRendered(d);
72676                 };
72677                 touchLayer
72678                     .call(drawTargets, graph, currentVisible(all), filterTouch);
72679
72680
72681                 function currentVisible(which) {
72682                     return Object.keys(which)
72683                         .map(graph.hasEntity, graph)     // the current version of this entity
72684                         .filter(function (entity) { return entity && entity.intersects(extent, graph); });
72685                 }
72686             }
72687
72688
72689             // partial redraw - only update the selected items..
72690             drawVertices.drawSelected = function(selection, graph, extent) {
72691                 var wireframe = context.surface().classed('fill-wireframe');
72692                 var zoom = geoScaleToZoom(projection.scale());
72693
72694                 _prevSelected = _currSelected || {};
72695                 if (context.map().isInWideSelection()) {
72696                     _currSelected = {};
72697                     context.selectedIDs().forEach(function(id) {
72698                         var entity = graph.hasEntity(id);
72699                         if (!entity) return;
72700
72701                         if (entity.type === 'node') {
72702                             if (renderAsVertex(entity, graph, wireframe, zoom)) {
72703                                 _currSelected[entity.id] = entity;
72704                             }
72705                         }
72706                     });
72707
72708                 } else {
72709                     _currSelected = getSiblingAndChildVertices(context.selectedIDs(), graph, wireframe, zoom);
72710                 }
72711
72712                 // note that drawVertices will add `_currSelected` automatically if needed..
72713                 var filter = function(d) { return d.id in _prevSelected; };
72714                 drawVertices(selection, graph, Object.values(_prevSelected), filter, extent, false);
72715             };
72716
72717
72718             // partial redraw - only update the hovered items..
72719             drawVertices.drawHover = function(selection, graph, target, extent) {
72720                 if (target === _currHoverTarget) return;  // continue only if something changed
72721
72722                 var wireframe = context.surface().classed('fill-wireframe');
72723                 var zoom = geoScaleToZoom(projection.scale());
72724
72725                 _prevHover = _currHover || {};
72726                 _currHoverTarget = target;
72727                 var entity = target && target.properties && target.properties.entity;
72728
72729                 if (entity) {
72730                     _currHover = getSiblingAndChildVertices([entity.id], graph, wireframe, zoom);
72731                 } else {
72732                     _currHover = {};
72733                 }
72734
72735                 // note that drawVertices will add `_currHover` automatically if needed..
72736                 var filter = function(d) { return d.id in _prevHover; };
72737                 drawVertices(selection, graph, Object.values(_prevHover), filter, extent, false);
72738             };
72739
72740             return drawVertices;
72741         }
72742
72743         function utilBindOnce(target, type, listener, capture) {
72744             var typeOnce = type + '.once';
72745             function one() {
72746                 target.on(typeOnce, null);
72747                 listener.apply(this, arguments);
72748             }
72749             target.on(typeOnce, one, capture);
72750             return this;
72751         }
72752
72753         // Adapted from d3-zoom to handle pointer events.
72754
72755         // Ignore right-click, since that should open the context menu.
72756         function defaultFilter$2() {
72757           return !event.ctrlKey && !event.button;
72758         }
72759
72760         function defaultExtent$1() {
72761           var e = this;
72762           if (e instanceof SVGElement) {
72763             e = e.ownerSVGElement || e;
72764             if (e.hasAttribute('viewBox')) {
72765               e = e.viewBox.baseVal;
72766               return [[e.x, e.y], [e.x + e.width, e.y + e.height]];
72767             }
72768             return [[0, 0], [e.width.baseVal.value, e.height.baseVal.value]];
72769           }
72770           return [[0, 0], [e.clientWidth, e.clientHeight]];
72771         }
72772
72773         function defaultWheelDelta$1() {
72774           return -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002);
72775         }
72776
72777         function defaultConstrain$1(transform, extent, translateExtent) {
72778           var dx0 = transform.invertX(extent[0][0]) - translateExtent[0][0],
72779               dx1 = transform.invertX(extent[1][0]) - translateExtent[1][0],
72780               dy0 = transform.invertY(extent[0][1]) - translateExtent[0][1],
72781               dy1 = transform.invertY(extent[1][1]) - translateExtent[1][1];
72782           return transform.translate(
72783             dx1 > dx0 ? (dx0 + dx1) / 2 : Math.min(0, dx0) || Math.max(0, dx1),
72784             dy1 > dy0 ? (dy0 + dy1) / 2 : Math.min(0, dy0) || Math.max(0, dy1)
72785           );
72786         }
72787
72788         function utilZoomPan() {
72789           var filter = defaultFilter$2,
72790               extent = defaultExtent$1,
72791               constrain = defaultConstrain$1,
72792               wheelDelta = defaultWheelDelta$1,
72793               scaleExtent = [0, Infinity],
72794               translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]],
72795               interpolate = interpolateZoom,
72796               listeners = dispatch('start', 'zoom', 'end'),
72797               _wheelDelay = 150,
72798               _transform = identity$2,
72799               _activeGesture;
72800
72801           function zoom(selection) {
72802             selection
72803                 .on('pointerdown.zoom', pointerdown)
72804                 .on('wheel.zoom', wheeled)
72805                 .style('touch-action', 'none')
72806                 .style('-webkit-tap-highlight-color', 'rgba(0,0,0,0)');
72807
72808             select(window)
72809                 .on('pointermove.zoompan', pointermove)
72810                 .on('pointerup.zoompan pointercancel.zoompan', pointerup);
72811           }
72812
72813           zoom.transform = function(collection, transform, point) {
72814             var selection = collection.selection ? collection.selection() : collection;
72815             if (collection !== selection) {
72816               schedule(collection, transform, point);
72817             } else {
72818               selection.interrupt().each(function() {
72819                 gesture(this, arguments)
72820                     .start()
72821                     .zoom(null, typeof transform === 'function' ? transform.apply(this, arguments) : transform)
72822                     .end();
72823               });
72824             }
72825           };
72826
72827           zoom.scaleBy = function(selection, k, p) {
72828             zoom.scaleTo(selection, function() {
72829               var k0 = _transform.k,
72830                   k1 = typeof k === 'function' ? k.apply(this, arguments) : k;
72831               return k0 * k1;
72832             }, p);
72833           };
72834
72835           zoom.scaleTo = function(selection, k, p) {
72836             zoom.transform(selection, function() {
72837               var e = extent.apply(this, arguments),
72838                   t0 = _transform,
72839                   p0 = p == null ? centroid(e) : typeof p === 'function' ? p.apply(this, arguments) : p,
72840                   p1 = t0.invert(p0),
72841                   k1 = typeof k === 'function' ? k.apply(this, arguments) : k;
72842               return constrain(translate(scale(t0, k1), p0, p1), e, translateExtent);
72843             }, p);
72844           };
72845
72846           zoom.translateBy = function(selection, x, y) {
72847             zoom.transform(selection, function() {
72848               return constrain(_transform.translate(
72849                 typeof x === 'function' ? x.apply(this, arguments) : x,
72850                 typeof y === 'function' ? y.apply(this, arguments) : y
72851               ), extent.apply(this, arguments), translateExtent);
72852             });
72853           };
72854
72855           zoom.translateTo = function(selection, x, y, p) {
72856             zoom.transform(selection, function() {
72857               var e = extent.apply(this, arguments),
72858                   t = _transform,
72859                   p0 = p == null ? centroid(e) : typeof p === 'function' ? p.apply(this, arguments) : p;
72860               return constrain(identity$2.translate(p0[0], p0[1]).scale(t.k).translate(
72861                 typeof x === 'function' ? -x.apply(this, arguments) : -x,
72862                 typeof y === 'function' ? -y.apply(this, arguments) : -y
72863               ), e, translateExtent);
72864             }, p);
72865           };
72866
72867           function scale(transform, k) {
72868             k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], k));
72869             return k === transform.k ? transform : new Transform(k, transform.x, transform.y);
72870           }
72871
72872           function translate(transform, p0, p1) {
72873             var x = p0[0] - p1[0] * transform.k, y = p0[1] - p1[1] * transform.k;
72874             return x === transform.x && y === transform.y ? transform : new Transform(transform.k, x, y);
72875           }
72876
72877           function centroid(extent) {
72878             return [(+extent[0][0] + +extent[1][0]) / 2, (+extent[0][1] + +extent[1][1]) / 2];
72879           }
72880
72881           function schedule(transition, transform, point) {
72882             transition
72883                 .on('start.zoom', function() { gesture(this, arguments).start(); })
72884                 .on('interrupt.zoom end.zoom', function() { gesture(this, arguments).end(); })
72885                 .tween('zoom', function() {
72886                   var that = this,
72887                       args = arguments,
72888                       g = gesture(that, args),
72889                       e = extent.apply(that, args),
72890                       p = point == null ? centroid(e) : typeof point === 'function' ? point.apply(that, args) : point,
72891                       w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]),
72892                       a = _transform,
72893                       b = typeof transform === 'function' ? transform.apply(that, args) : transform,
72894                       i = interpolate(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k));
72895                   return function(t) {
72896                     if (t === 1) t = b; // Avoid rounding error on end.
72897                     else { var l = i(t), k = w / l[2]; t = new Transform(k, p[0] - l[0] * k, p[1] - l[1] * k); }
72898                     g.zoom(null, t);
72899                   };
72900                 });
72901           }
72902
72903           function gesture(that, args, clean) {
72904             return (!clean && _activeGesture) || new Gesture(that, args);
72905           }
72906
72907           function Gesture(that, args) {
72908             this.that = that;
72909             this.args = args;
72910             this.active = 0;
72911             this.extent = extent.apply(that, args);
72912           }
72913
72914           Gesture.prototype = {
72915             start: function() {
72916               if (++this.active === 1) {
72917                 _activeGesture = this;
72918                 this.emit('start');
72919               }
72920               return this;
72921             },
72922             zoom: function(key, transform) {
72923               if (this.mouse && key !== 'mouse') this.mouse[1] = transform.invert(this.mouse[0]);
72924               if (this.pointer0 && key !== 'touch') this.pointer0[1] = transform.invert(this.pointer0[0]);
72925               if (this.pointer1 && key !== 'touch') this.pointer1[1] = transform.invert(this.pointer1[0]);
72926               _transform = transform;
72927               this.emit('zoom');
72928               return this;
72929             },
72930             end: function() {
72931               if (--this.active === 0) {
72932                 _activeGesture = null;
72933                 this.emit('end');
72934               }
72935               return this;
72936             },
72937             emit: function(type) {
72938               customEvent(new ZoomEvent(zoom, type, _transform), listeners.apply, listeners, [type, this.that, this.args]);
72939             }
72940           };
72941
72942           function wheeled() {
72943             if (!filter.apply(this, arguments)) return;
72944             var g = gesture(this, arguments),
72945                 t = _transform,
72946                 k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], t.k * Math.pow(2, wheelDelta.apply(this, arguments)))),
72947                 p = utilFastMouse(this)(event);
72948
72949             // If the mouse is in the same location as before, reuse it.
72950             // If there were recent wheel events, reset the wheel idle timeout.
72951             if (g.wheel) {
72952               if (g.mouse[0][0] !== p[0] || g.mouse[0][1] !== p[1]) {
72953                 g.mouse[1] = t.invert(g.mouse[0] = p);
72954               }
72955               clearTimeout(g.wheel);
72956
72957             // Otherwise, capture the mouse point and location at the start.
72958             } else {
72959               g.mouse = [p, t.invert(p)];
72960               interrupt(this);
72961               g.start();
72962             }
72963
72964             event.preventDefault();
72965             event.stopImmediatePropagation();
72966             g.wheel = setTimeout(wheelidled, _wheelDelay);
72967             g.zoom('mouse', constrain(translate(scale(t, k), g.mouse[0], g.mouse[1]), g.extent, translateExtent));
72968
72969             function wheelidled() {
72970               g.wheel = null;
72971               g.end();
72972             }
72973           }
72974
72975           var _downPointerIDs = new Set();
72976           var _pointerLocGetter;
72977
72978           function pointerdown() {
72979             _downPointerIDs.add(event.pointerId);
72980
72981             if (!filter.apply(this, arguments)) return;
72982
72983             var g = gesture(this, arguments, _downPointerIDs.size === 1);
72984             var started;
72985
72986             event.stopImmediatePropagation();
72987             _pointerLocGetter = utilFastMouse(this);
72988             var loc = _pointerLocGetter(event);
72989             var p = [loc, _transform.invert(loc), event.pointerId];
72990             if (!g.pointer0) {
72991                g.pointer0 = p;
72992                started = true;
72993
72994             } else if (!g.pointer1 && g.pointer0[2] !== p[2]) {
72995                g.pointer1 = p;
72996             }
72997
72998             if (started) {
72999               interrupt(this);
73000               g.start();
73001             }
73002           }
73003
73004           function pointermove() {
73005             if (!_downPointerIDs.has(event.pointerId)) return;
73006
73007             if (!_activeGesture || !_pointerLocGetter) return;
73008
73009             var g = gesture(this, arguments);
73010
73011             var isPointer0 = g.pointer0 && g.pointer0[2] === event.pointerId;
73012             var isPointer1 = !isPointer0 && g.pointer1 && g.pointer1[2] === event.pointerId;
73013
73014             if ((isPointer0 || isPointer1) && 'buttons' in event && !event.buttons) {
73015               // The pointer went up without ending the gesture somehow, e.g.
73016               // a down mouse was moved off the map and released. End it here.
73017               if (g.pointer0) _downPointerIDs.delete(g.pointer0[2]);
73018               if (g.pointer1) _downPointerIDs.delete(g.pointer1[2]);
73019               g.end();
73020               return;
73021             }
73022
73023             event.preventDefault();
73024             event.stopImmediatePropagation();
73025
73026             var loc = _pointerLocGetter(event);
73027             var t, p, l;
73028
73029             if (isPointer0) g.pointer0[0] = loc;
73030             else if (isPointer1) g.pointer1[0] = loc;
73031
73032             t = _transform;
73033             if (g.pointer1) {
73034               var p0 = g.pointer0[0], l0 = g.pointer0[1],
73035                   p1 = g.pointer1[0], l1 = g.pointer1[1],
73036                   dp = (dp = p1[0] - p0[0]) * dp + (dp = p1[1] - p0[1]) * dp,
73037                   dl = (dl = l1[0] - l0[0]) * dl + (dl = l1[1] - l0[1]) * dl;
73038               t = scale(t, Math.sqrt(dp / dl));
73039               p = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2];
73040               l = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2];
73041             } else if (g.pointer0) {
73042               p = g.pointer0[0];
73043               l = g.pointer0[1];
73044             }
73045             else return;
73046             g.zoom('touch', constrain(translate(t, p, l), g.extent, translateExtent));
73047           }
73048
73049           function pointerup() {
73050             if (!_downPointerIDs.has(event.pointerId)) return;
73051
73052             _downPointerIDs.delete(event.pointerId);
73053
73054             if (!_activeGesture) return;
73055
73056             var g = gesture(this, arguments);
73057
73058             event.stopImmediatePropagation();
73059
73060             if (g.pointer0 && g.pointer0[2] === event.pointerId) delete g.pointer0;
73061             else if (g.pointer1 && g.pointer1[2] === event.pointerId) delete g.pointer1;
73062
73063             if (g.pointer1 && !g.pointer0) {
73064               g.pointer0 = g.pointer1;
73065               delete g.pointer1;
73066             }
73067             if (g.pointer0) g.pointer0[1] = _transform.invert(g.pointer0[0]);
73068             else {
73069               g.end();
73070             }
73071           }
73072
73073           zoom.wheelDelta = function(_) {
73074             return arguments.length ? (wheelDelta = utilFunctor(+_), zoom) : wheelDelta;
73075           };
73076
73077           zoom.filter = function(_) {
73078             return arguments.length ? (filter = utilFunctor(!!_), zoom) : filter;
73079           };
73080
73081           zoom.extent = function(_) {
73082             return arguments.length ? (extent = utilFunctor([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom) : extent;
73083           };
73084
73085           zoom.scaleExtent = function(_) {
73086             return arguments.length ? (scaleExtent[0] = +_[0], scaleExtent[1] = +_[1], zoom) : [scaleExtent[0], scaleExtent[1]];
73087           };
73088
73089           zoom.translateExtent = function(_) {
73090             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]]];
73091           };
73092
73093           zoom.constrain = function(_) {
73094             return arguments.length ? (constrain = _, zoom) : constrain;
73095           };
73096
73097           zoom.interpolate = function(_) {
73098             return arguments.length ? (interpolate = _, zoom) : interpolate;
73099           };
73100
73101           zoom._transform = function(_) {
73102             return arguments.length ? (_transform = _, zoom) : _transform;
73103           };
73104
73105           zoom.on = function() {
73106             var value = listeners.on.apply(listeners, arguments);
73107             return value === listeners ? zoom : value;
73108           };
73109
73110           return zoom;
73111         }
73112
73113         // A custom double-click / double-tap event detector that works on touch devices
73114         // if pointer events are supported. Falls back to default `dblclick` event.
73115         function utilDoubleUp() {
73116
73117             var dispatch$1 = dispatch('doubleUp');
73118
73119             var _maxTimespan = 500; // milliseconds
73120             var _maxDistance = 20; // web pixels; be somewhat generous to account for touch devices
73121             var _pointer; // object representing the pointer that could trigger double up
73122
73123             function pointerIsValidFor(loc) {
73124                 // second pointerup must occur within a small timeframe after the first pointerdown
73125                 return new Date().getTime() - _pointer.startTime <= _maxTimespan &&
73126                     // all pointer events must occur within a small distance of the first pointerdown
73127                     geoVecLength(_pointer.startLoc, loc) <= _maxDistance;
73128             }
73129
73130             function pointerdown() {
73131
73132                 // ignore right-click
73133                 if (event.ctrlKey || event.button === 2) return;
73134
73135                 var loc = [event.clientX, event.clientY];
73136
73137                 // Don't rely on pointerId here since it can change between pointerdown
73138                 // events on touch devices
73139                 if (_pointer && !pointerIsValidFor(loc)) {
73140                     // if this pointer is no longer valid, clear it so another can be started
73141                     _pointer = undefined;
73142                 }
73143
73144                 if (!_pointer) {
73145                     _pointer = {
73146                         startLoc: loc,
73147                         startTime: new Date().getTime(),
73148                         upCount: 0,
73149                         pointerId: event.pointerId
73150                     };
73151                 } else { // double down
73152                     _pointer.pointerId = event.pointerId;
73153                 }
73154             }
73155
73156             function pointerup() {
73157
73158                 // ignore right-click
73159                 if (event.ctrlKey || event.button === 2) return;
73160
73161                 if (!_pointer || _pointer.pointerId !== event.pointerId) return;
73162
73163                 _pointer.upCount += 1;
73164
73165                 if (_pointer.upCount === 2) { // double up!
73166                     var loc = [event.clientX, event.clientY];
73167                     if (pointerIsValidFor(loc)) {
73168                         var locInThis = utilFastMouse(this)(event);
73169                         dispatch$1.call('doubleUp', this, locInThis);
73170                     }
73171                     // clear the pointer info in any case
73172                     _pointer = undefined;
73173                 }
73174             }
73175
73176             function doubleUp(selection) {
73177                 if ('PointerEvent' in window) {
73178                     // dblclick isn't well supported on touch devices so manually use
73179                     // pointer events if they're available
73180                     selection
73181                         .on('pointerdown.doubleUp', pointerdown)
73182                         .on('pointerup.doubleUp', pointerup);
73183                 } else {
73184                     // fallback to dblclick
73185                     selection
73186                         .on('dblclick.doubleUp', function() {
73187                             dispatch$1.call('doubleUp', this, utilFastMouse(this)(event));
73188                         });
73189                 }
73190             }
73191
73192             doubleUp.off = function(selection) {
73193                 selection
73194                     .on('pointerdown.doubleUp', null)
73195                     .on('pointerup.doubleUp', null)
73196                     .on('dblclick.doubleUp', null);
73197             };
73198
73199             return utilRebind(doubleUp, dispatch$1, 'on');
73200         }
73201
73202         // constants
73203         var TILESIZE = 256;
73204         var minZoom = 2;
73205         var maxZoom = 24;
73206         var kMin = geoZoomToScale(minZoom, TILESIZE);
73207         var kMax = geoZoomToScale(maxZoom, TILESIZE);
73208
73209         function clamp(num, min, max) {
73210             return Math.max(min, Math.min(num, max));
73211         }
73212
73213
73214         function rendererMap(context) {
73215             var dispatch$1 = dispatch(
73216                 'move', 'drawn',
73217                 'crossEditableZoom', 'hitMinZoom',
73218                 'changeHighlighting', 'changeAreaFill'
73219             );
73220             var projection = context.projection;
73221             var curtainProjection = context.curtainProjection;
73222             var drawLayers;
73223             var drawPoints;
73224             var drawVertices;
73225             var drawLines;
73226             var drawAreas;
73227             var drawMidpoints;
73228             var drawLabels;
73229
73230             var _selection = select(null);
73231             var supersurface = select(null);
73232             var wrapper = select(null);
73233             var surface = select(null);
73234
73235             var _dimensions = [1, 1];
73236             var _dblClickZoomEnabled = true;
73237             var _redrawEnabled = true;
73238             var _gestureTransformStart;
73239             var _transformStart = projection.transform();
73240             var _transformLast;
73241             var _isTransformed = false;
73242             var _minzoom = 0;
73243             var _getMouseCoords;
73244             var _lastPointerEvent;
73245             var _lastWithinEditableZoom;
73246
73247             // whether a pointerdown event started the zoom
73248             var _pointerDown = false;
73249
73250             // use pointer events on supported platforms; fallback to mouse events
73251             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
73252
73253             // use pointer event interaction if supported; fallback to touch/mouse events in d3-zoom
73254             var _zoomerPannerFunction = 'PointerEvent' in window ? utilZoomPan : d3_zoom;
73255
73256             var _zoomerPanner = _zoomerPannerFunction()
73257                 .scaleExtent([kMin, kMax])
73258                 .interpolate(interpolate)
73259                 .filter(zoomEventFilter)
73260                 .on('zoom.map', zoomPan)
73261                 .on('start.map', function() {
73262                     _pointerDown = event.sourceEvent && event.sourceEvent.type === 'pointerdown';
73263                 })
73264                 .on('end.map', function() {
73265                     _pointerDown = false;
73266                 });
73267             var _doubleUpHandler = utilDoubleUp();
73268
73269             var scheduleRedraw = throttle(redraw, 750);
73270             // var isRedrawScheduled = false;
73271             // var pendingRedrawCall;
73272             // function scheduleRedraw() {
73273             //     // Only schedule the redraw if one has not already been set.
73274             //     if (isRedrawScheduled) return;
73275             //     isRedrawScheduled = true;
73276             //     var that = this;
73277             //     var args = arguments;
73278             //     pendingRedrawCall = window.requestIdleCallback(function () {
73279             //         // Reset the boolean so future redraws can be set.
73280             //         isRedrawScheduled = false;
73281             //         redraw.apply(that, args);
73282             //     }, { timeout: 1400 });
73283             // }
73284
73285             function cancelPendingRedraw() {
73286                 scheduleRedraw.cancel();
73287                 // isRedrawScheduled = false;
73288                 // window.cancelIdleCallback(pendingRedrawCall);
73289             }
73290
73291
73292             function map(selection) {
73293                 _selection = selection;
73294
73295                 context
73296                     .on('change.map', immediateRedraw);
73297
73298                 var osm = context.connection();
73299                 if (osm) {
73300                     osm.on('change.map', immediateRedraw);
73301                 }
73302
73303                 function didUndoOrRedo(targetTransform) {
73304                     var mode = context.mode().id;
73305                     if (mode !== 'browse' && mode !== 'select') return;
73306                     if (targetTransform) {
73307                         map.transformEase(targetTransform);
73308                     }
73309                 }
73310
73311                 context.history()
73312                     .on('merge.map', function() { scheduleRedraw(); })
73313                     .on('change.map', immediateRedraw)
73314                     .on('undone.map', function(stack, fromStack) {
73315                         didUndoOrRedo(fromStack.transform);
73316                     })
73317                     .on('redone.map', function(stack) {
73318                         didUndoOrRedo(stack.transform);
73319                     });
73320
73321                 context.background()
73322                     .on('change.map', immediateRedraw);
73323
73324                 context.features()
73325                     .on('redraw.map', immediateRedraw);
73326
73327                 drawLayers
73328                     .on('change.map', function() {
73329                         context.background().updateImagery();
73330                         immediateRedraw();
73331                     });
73332
73333                 selection
73334                     .on('wheel.map mousewheel.map', function() {
73335                         // disable swipe-to-navigate browser pages on trackpad/magic mouse – #5552
73336                         event.preventDefault();
73337                     })
73338                     .call(_zoomerPanner)
73339                     .call(_zoomerPanner.transform, projection.transform())
73340                     .on('dblclick.zoom', null); // override d3-zoom dblclick handling
73341
73342                 map.supersurface = supersurface = selection.append('div')
73343                     .attr('class', 'supersurface')
73344                     .call(utilSetTransform, 0, 0);
73345
73346                 // Need a wrapper div because Opera can't cope with an absolutely positioned
73347                 // SVG element: http://bl.ocks.org/jfirebaugh/6fbfbd922552bf776c16
73348                 wrapper = supersurface
73349                     .append('div')
73350                     .attr('class', 'layer layer-data');
73351
73352                 map.surface = surface = wrapper
73353                     .call(drawLayers)
73354                     .selectAll('.surface');
73355
73356                 surface
73357                     .call(drawLabels.observe)
73358                     .call(_doubleUpHandler)
73359                     .on(_pointerPrefix + 'down.zoom', function() {
73360                         _lastPointerEvent = event;
73361                         if (event.button === 2) {
73362                             event.stopPropagation();
73363                         }
73364                     }, true)
73365                     .on(_pointerPrefix + 'up.zoom', function() {
73366                         _lastPointerEvent = event;
73367                         if (resetTransform()) {
73368                             immediateRedraw();
73369                         }
73370                     })
73371                     .on(_pointerPrefix + 'move.map', function() {
73372                         _lastPointerEvent = event;
73373                     })
73374                     .on(_pointerPrefix + 'over.vertices', function() {
73375                         if (map.editableDataEnabled() && !_isTransformed) {
73376                             var hover = event.target.__data__;
73377                             surface.call(drawVertices.drawHover, context.graph(), hover, map.extent());
73378                             dispatch$1.call('drawn', this, { full: false });
73379                         }
73380                     })
73381                     .on(_pointerPrefix + 'out.vertices', function() {
73382                         if (map.editableDataEnabled() && !_isTransformed) {
73383                             var hover = event.relatedTarget && event.relatedTarget.__data__;
73384                             surface.call(drawVertices.drawHover, context.graph(), hover, map.extent());
73385                             dispatch$1.call('drawn', this, { full: false });
73386                         }
73387                     });
73388
73389                 var detected = utilDetect();
73390
73391                 // only WebKit supports gesture events
73392                 if ('GestureEvent' in window &&
73393                     // Listening for gesture events on iOS 13.4+ breaks double-tapping,
73394                     // but we only need to do this on desktop Safari anyway. – #7694
73395                     !detected.isMobileWebKit) {
73396
73397                     // Desktop Safari sends gesture events for multitouch trackpad pinches.
73398                     // We can listen for these and translate them into map zooms.
73399                     surface
73400                         .on('gesturestart.surface', function() {
73401                             event.preventDefault();
73402                             _gestureTransformStart = projection.transform();
73403                         })
73404                         .on('gesturechange.surface', gestureChange);
73405                 }
73406
73407                 // must call after surface init
73408                 updateAreaFill();
73409
73410                 _doubleUpHandler.on('doubleUp.map', function(p0) {
73411                     if (!_dblClickZoomEnabled) return;
73412
73413                     // don't zoom if targeting something other than the map itself
73414                     if (typeof event.target.__data__ === 'object' &&
73415                         // or area fills
73416                         !select(event.target).classed('fill')) return;
73417
73418                     var zoomOut = event.shiftKey;
73419
73420                     var t = projection.transform();
73421
73422                     var p1 = t.invert(p0);
73423
73424                     t = t.scale(zoomOut ? 0.5 : 2);
73425
73426                     t.x = p0[0] - p1[0] * t.k;
73427                     t.y = p0[1] - p1[1] * t.k;
73428
73429                     map.transformEase(t);
73430                 });
73431
73432                 context.on('enter.map',  function() {
73433                     if (!map.editableDataEnabled(true /* skip zoom check */)) return;
73434
73435                     // redraw immediately any objects affected by a change in selectedIDs.
73436                     var graph = context.graph();
73437                     var selectedAndParents = {};
73438                     context.selectedIDs().forEach(function(id) {
73439                         var entity = graph.hasEntity(id);
73440                         if (entity) {
73441                             selectedAndParents[entity.id] = entity;
73442                             if (entity.type === 'node') {
73443                                 graph.parentWays(entity).forEach(function(parent) {
73444                                     selectedAndParents[parent.id] = parent;
73445                                 });
73446                             }
73447                         }
73448                     });
73449                     var data = Object.values(selectedAndParents);
73450                     var filter = function(d) { return d.id in selectedAndParents; };
73451
73452                     data = context.features().filter(data, graph);
73453
73454                     surface
73455                         .call(drawVertices.drawSelected, graph, map.extent())
73456                         .call(drawLines, graph, data, filter)
73457                         .call(drawAreas, graph, data, filter)
73458                         .call(drawMidpoints, graph, data, filter, map.trimmedExtent());
73459
73460                     dispatch$1.call('drawn', this, { full: false });
73461
73462                     // redraw everything else later
73463                     scheduleRedraw();
73464                 });
73465
73466                 map.dimensions(utilGetDimensions(selection));
73467             }
73468
73469
73470             function zoomEventFilter() {
73471                 // Fix for #2151, (see also d3/d3-zoom#60, d3/d3-brush#18)
73472                 // Intercept `mousedown` and check if there is an orphaned zoom gesture.
73473                 // This can happen if a previous `mousedown` occurred without a `mouseup`.
73474                 // If we detect this, dispatch `mouseup` to complete the orphaned gesture,
73475                 // so that d3-zoom won't stop propagation of new `mousedown` events.
73476                 if (event.type === 'mousedown') {
73477                     var hasOrphan = false;
73478                     var listeners = window.__on;
73479                     for (var i = 0; i < listeners.length; i++) {
73480                         var listener = listeners[i];
73481                         if (listener.name === 'zoom' && listener.type === 'mouseup') {
73482                             hasOrphan = true;
73483                             break;
73484                         }
73485                     }
73486                     if (hasOrphan) {
73487                         var event$1 = window.CustomEvent;
73488                         if (event$1) {
73489                             event$1 = new event$1('mouseup');
73490                         } else {
73491                             event$1 = window.document.createEvent('Event');
73492                             event$1.initEvent('mouseup', false, false);
73493                         }
73494                         // Event needs to be dispatched with an event.view property.
73495                         event$1.view = window;
73496                         window.dispatchEvent(event$1);
73497                     }
73498                 }
73499
73500                 return event.button !== 2;   // ignore right clicks
73501             }
73502
73503
73504             function pxCenter() {
73505                 return [_dimensions[0] / 2, _dimensions[1] / 2];
73506             }
73507
73508
73509             function drawEditable(difference, extent) {
73510                 var mode = context.mode();
73511                 var graph = context.graph();
73512                 var features = context.features();
73513                 var all = context.history().intersects(map.extent());
73514                 var fullRedraw = false;
73515                 var data;
73516                 var set;
73517                 var filter;
73518                 var applyFeatureLayerFilters = true;
73519
73520                 if (map.isInWideSelection()) {
73521                     data = [];
73522                     utilEntityAndDeepMemberIDs(mode.selectedIDs(), context.graph()).forEach(function(id) {
73523                         var entity = context.hasEntity(id);
73524                         if (entity) data.push(entity);
73525                     });
73526                     fullRedraw = true;
73527                     filter = utilFunctor(true);
73528                     // selected features should always be visible, so we can skip filtering
73529                     applyFeatureLayerFilters = false;
73530
73531                 } else if (difference) {
73532                     var complete = difference.complete(map.extent());
73533                     data = Object.values(complete).filter(Boolean);
73534                     set = new Set(Object.keys(complete));
73535                     filter = function(d) { return set.has(d.id); };
73536                     features.clear(data);
73537
73538                 } else {
73539                     // force a full redraw if gatherStats detects that a feature
73540                     // should be auto-hidden (e.g. points or buildings)..
73541                     if (features.gatherStats(all, graph, _dimensions)) {
73542                         extent = undefined;
73543                     }
73544
73545                     if (extent) {
73546                         data = context.history().intersects(map.extent().intersection(extent));
73547                         set = new Set(data.map(function(entity) { return entity.id; }));
73548                         filter = function(d) { return set.has(d.id); };
73549
73550                     } else {
73551                         data = all;
73552                         fullRedraw = true;
73553                         filter = utilFunctor(true);
73554                     }
73555                 }
73556
73557                 if (applyFeatureLayerFilters) {
73558                     data = features.filter(data, graph);
73559                 } else {
73560                     context.features().resetStats();
73561                 }
73562
73563                 if (mode && mode.id === 'select') {
73564                     // update selected vertices - the user might have just double-clicked a way,
73565                     // creating a new vertex, triggering a partial redraw without a mode change
73566                     surface.call(drawVertices.drawSelected, graph, map.extent());
73567                 }
73568
73569                 surface
73570                     .call(drawVertices, graph, data, filter, map.extent(), fullRedraw)
73571                     .call(drawLines, graph, data, filter)
73572                     .call(drawAreas, graph, data, filter)
73573                     .call(drawMidpoints, graph, data, filter, map.trimmedExtent())
73574                     .call(drawLabels, graph, data, filter, _dimensions, fullRedraw)
73575                     .call(drawPoints, graph, data, filter);
73576
73577                 dispatch$1.call('drawn', this, {full: true});
73578             }
73579
73580             map.init = function() {
73581                 drawLayers = svgLayers(projection, context);
73582                 drawPoints = svgPoints(projection, context);
73583                 drawVertices = svgVertices(projection, context);
73584                 drawLines = svgLines(projection, context);
73585                 drawAreas = svgAreas(projection, context);
73586                 drawMidpoints = svgMidpoints(projection, context);
73587                 drawLabels = svgLabels(projection, context);
73588             };
73589
73590             function editOff() {
73591                 context.features().resetStats();
73592                 surface.selectAll('.layer-osm *').remove();
73593                 surface.selectAll('.layer-touch:not(.markers) *').remove();
73594
73595                 var allowed = {
73596                     'browse': true,
73597                     'save': true,
73598                     'select-note': true,
73599                     'select-data': true,
73600                     'select-error': true
73601                 };
73602
73603                 var mode = context.mode();
73604                 if (mode && !allowed[mode.id]) {
73605                     context.enter(modeBrowse(context));
73606                 }
73607
73608                 dispatch$1.call('drawn', this, {full: true});
73609             }
73610
73611
73612
73613
73614
73615             function gestureChange() {
73616                 // Remap Safari gesture events to wheel events - #5492
73617                 // We want these disabled most places, but enabled for zoom/unzoom on map surface
73618                 // https://developer.mozilla.org/en-US/docs/Web/API/GestureEvent
73619                 var e = event;
73620                 e.preventDefault();
73621
73622                 var props = {
73623                     deltaMode: 0,    // dummy values to ignore in zoomPan
73624                     deltaY: 1,       // dummy values to ignore in zoomPan
73625                     clientX: e.clientX,
73626                     clientY: e.clientY,
73627                     screenX: e.screenX,
73628                     screenY: e.screenY,
73629                     x: e.x,
73630                     y: e.y
73631                 };
73632
73633                 var e2 = new WheelEvent('wheel', props);
73634                 e2._scale = e.scale;         // preserve the original scale
73635                 e2._rotation = e.rotation;   // preserve the original rotation
73636
73637                 _selection.node().dispatchEvent(e2);
73638             }
73639
73640
73641             function zoomPan(manualEvent) {
73642                 var event$1 = (manualEvent || event);
73643                 var source = event$1.sourceEvent;
73644                 var eventTransform = event$1.transform;
73645                 var x = eventTransform.x;
73646                 var y = eventTransform.y;
73647                 var k = eventTransform.k;
73648
73649                 // Special handling of 'wheel' events:
73650                 // They might be triggered by the user scrolling the mouse wheel,
73651                 // or 2-finger pinch/zoom gestures, the transform may need adjustment.
73652                 if (source && source.type === 'wheel') {
73653
73654                     // assume that the gesture is already handled by pointer events
73655                     if (_pointerDown) return;
73656
73657                     var detected = utilDetect();
73658                     var dX = source.deltaX;
73659                     var dY = source.deltaY;
73660                     var x2 = x;
73661                     var y2 = y;
73662                     var k2 = k;
73663                     var t0, p0, p1;
73664
73665                     // Normalize mousewheel scroll speed (Firefox) - #3029
73666                     // If wheel delta is provided in LINE units, recalculate it in PIXEL units
73667                     // We are essentially redoing the calculations that occur here:
73668                     //   https://github.com/d3/d3-zoom/blob/78563a8348aa4133b07cac92e2595c2227ca7cd7/src/zoom.js#L203
73669                     // See this for more info:
73670                     //   https://github.com/basilfx/normalize-wheel/blob/master/src/normalizeWheel.js
73671                     if (source.deltaMode === 1 /* LINE */) {
73672                         // Convert from lines to pixels, more if the user is scrolling fast.
73673                         // (I made up the exp function to roughly match Firefox to what Chrome does)
73674                         // These numbers should be floats, because integers are treated as pan gesture below.
73675                         var lines = Math.abs(source.deltaY);
73676                         var sign = (source.deltaY > 0) ? 1 : -1;
73677                         dY = sign * clamp(
73678                             Math.exp((lines - 1) * 0.75) * 4.000244140625,
73679                             4.000244140625,    // min
73680                             350.000244140625   // max
73681                         );
73682
73683                         // On Firefox Windows and Linux we always get +/- the scroll line amount (default 3)
73684                         // There doesn't seem to be any scroll accelleration.
73685                         // This multiplier increases the speed a little bit - #5512
73686                         if (detected.os !== 'mac') {
73687                             dY *= 5;
73688                         }
73689
73690                         // recalculate x2,y2,k2
73691                         t0 = _isTransformed ? _transformLast : _transformStart;
73692                         p0 = _getMouseCoords(source);
73693                         p1 = t0.invert(p0);
73694                         k2 = t0.k * Math.pow(2, -dY / 500);
73695                         k2 = clamp(k2, kMin, kMax);
73696                         x2 = p0[0] - p1[0] * k2;
73697                         y2 = p0[1] - p1[1] * k2;
73698
73699                     // 2 finger map pinch zooming (Safari) - #5492
73700                     // These are fake `wheel` events we made from Safari `gesturechange` events..
73701                     } else if (source._scale) {
73702                         // recalculate x2,y2,k2
73703                         t0 = _gestureTransformStart;
73704                         p0 = _getMouseCoords(source);
73705                         p1 = t0.invert(p0);
73706                         k2 = t0.k * source._scale;
73707                         k2 = clamp(k2, kMin, kMax);
73708                         x2 = p0[0] - p1[0] * k2;
73709                         y2 = p0[1] - p1[1] * k2;
73710
73711                     // 2 finger map pinch zooming (all browsers except Safari) - #5492
73712                     // Pinch zooming via the `wheel` event will always have:
73713                     // - `ctrlKey = true`
73714                     // - `deltaY` is not round integer pixels (ignore `deltaX`)
73715                     } else if (source.ctrlKey && !isInteger(dY)) {
73716                         dY *= 6;   // slightly scale up whatever the browser gave us
73717
73718                         // recalculate x2,y2,k2
73719                         t0 = _isTransformed ? _transformLast : _transformStart;
73720                         p0 = _getMouseCoords(source);
73721                         p1 = t0.invert(p0);
73722                         k2 = t0.k * Math.pow(2, -dY / 500);
73723                         k2 = clamp(k2, kMin, kMax);
73724                         x2 = p0[0] - p1[0] * k2;
73725                         y2 = p0[1] - p1[1] * k2;
73726
73727                     // Trackpad scroll zooming with shift or alt/option key down
73728                     } else if ((source.altKey || source.shiftKey) && isInteger(dY)) {
73729                         // recalculate x2,y2,k2
73730                         t0 = _isTransformed ? _transformLast : _transformStart;
73731                         p0 = _getMouseCoords(source);
73732                         p1 = t0.invert(p0);
73733                         k2 = t0.k * Math.pow(2, -dY / 500);
73734                         k2 = clamp(k2, kMin, kMax);
73735                         x2 = p0[0] - p1[0] * k2;
73736                         y2 = p0[1] - p1[1] * k2;
73737
73738                     // 2 finger map panning (Mac only, all browsers) - #5492, #5512
73739                     // Panning via the `wheel` event will always have:
73740                     // - `ctrlKey = false`
73741                     // - `deltaX`,`deltaY` are round integer pixels
73742                     } else if (detected.os === 'mac' && !source.ctrlKey && isInteger(dX) && isInteger(dY)) {
73743                         p1 = projection.translate();
73744                         x2 = p1[0] - dX;
73745                         y2 = p1[1] - dY;
73746                         k2 = projection.scale();
73747                         k2 = clamp(k2, kMin, kMax);
73748                     }
73749
73750                     // something changed - replace the event transform
73751                     if (x2 !== x || y2 !== y || k2 !== k) {
73752                         x = x2;
73753                         y = y2;
73754                         k = k2;
73755                         eventTransform = identity$2.translate(x2, y2).scale(k2);
73756                         if (_zoomerPanner._transform) {
73757                             // utilZoomPan interface
73758                             _zoomerPanner._transform(eventTransform);
73759                         } else {
73760                             // d3_zoom interface
73761                             _selection.node().__zoom = eventTransform;
73762                         }
73763                     }
73764
73765                 }
73766
73767                 if (_transformStart.x === x &&
73768                     _transformStart.y === y &&
73769                     _transformStart.k === k) {
73770                     return;  // no change
73771                 }
73772
73773                 var withinEditableZoom = map.withinEditableZoom();
73774                 if (_lastWithinEditableZoom !== withinEditableZoom) {
73775                     if (_lastWithinEditableZoom !== undefined) {
73776                         // notify that the map zoomed in or out over the editable zoom threshold
73777                         dispatch$1.call('crossEditableZoom', this, withinEditableZoom);
73778                     }
73779                     _lastWithinEditableZoom = withinEditableZoom;
73780                 }
73781
73782                 if (geoScaleToZoom(k, TILESIZE) < _minzoom) {
73783                     surface.interrupt();
73784                     dispatch$1.call('hitMinZoom', this, map);
73785                     setCenterZoom(map.center(), context.minEditableZoom(), 0, true);
73786                     scheduleRedraw();
73787                     dispatch$1.call('move', this, map);
73788                     return;
73789                 }
73790
73791                 projection.transform(eventTransform);
73792
73793                 var scale = k / _transformStart.k;
73794                 var tX = (x / scale - _transformStart.x) * scale;
73795                 var tY = (y / scale - _transformStart.y) * scale;
73796
73797                 if (context.inIntro()) {
73798                     curtainProjection.transform({
73799                         x: x - tX,
73800                         y: y - tY,
73801                         k: k
73802                     });
73803                 }
73804
73805                 if (source) {
73806                     _lastPointerEvent = event$1;
73807                 }
73808                 _isTransformed = true;
73809                 _transformLast = eventTransform;
73810                 utilSetTransform(supersurface, tX, tY, scale);
73811                 scheduleRedraw();
73812
73813                 dispatch$1.call('move', this, map);
73814
73815
73816                 function isInteger(val) {
73817                     return typeof val === 'number' && isFinite(val) && Math.floor(val) === val;
73818                 }
73819             }
73820
73821
73822             function resetTransform() {
73823                 if (!_isTransformed) return false;
73824
73825                 utilSetTransform(supersurface, 0, 0);
73826                 _isTransformed = false;
73827                 if (context.inIntro()) {
73828                     curtainProjection.transform(projection.transform());
73829                 }
73830                 return true;
73831             }
73832
73833
73834             function redraw(difference, extent) {
73835                 if (surface.empty() || !_redrawEnabled) return;
73836
73837                 // If we are in the middle of a zoom/pan, we can't do differenced redraws.
73838                 // It would result in artifacts where differenced entities are redrawn with
73839                 // one transform and unchanged entities with another.
73840                 if (resetTransform()) {
73841                     difference = extent = undefined;
73842                 }
73843
73844                 var zoom = map.zoom();
73845                 var z = String(~~zoom);
73846
73847                 if (surface.attr('data-zoom') !== z) {
73848                     surface.attr('data-zoom', z);
73849                 }
73850
73851                 // class surface as `lowzoom` around z17-z18.5 (based on latitude)
73852                 var lat = map.center()[1];
73853                 var lowzoom = linear$2()
73854                     .domain([-60, 0, 60])
73855                     .range([17, 18.5, 17])
73856                     .clamp(true);
73857
73858                 surface
73859                     .classed('low-zoom', zoom <= lowzoom(lat));
73860
73861
73862                 if (!difference) {
73863                     supersurface.call(context.background());
73864                     wrapper.call(drawLayers);
73865                 }
73866
73867                 // OSM
73868                 if (map.editableDataEnabled() || map.isInWideSelection()) {
73869                     context.loadTiles(projection);
73870                     drawEditable(difference, extent);
73871                 } else {
73872                     editOff();
73873                 }
73874
73875                 _transformStart = projection.transform();
73876
73877                 return map;
73878             }
73879
73880
73881
73882             var immediateRedraw = function(difference, extent) {
73883                 if (!difference && !extent) cancelPendingRedraw();
73884                 redraw(difference, extent);
73885             };
73886
73887
73888             map.lastPointerEvent = function() {
73889                 return _lastPointerEvent;
73890             };
73891
73892
73893             map.mouse = function() {
73894                 var event$1 = _lastPointerEvent || event;
73895                 if (event$1) {
73896                     var s;
73897                     while ((s = event$1.sourceEvent)) { event$1 = s; }
73898                     return _getMouseCoords(event$1);
73899                 }
73900                 return null;
73901             };
73902
73903
73904             // returns Lng/Lat
73905             map.mouseCoordinates = function() {
73906                 var coord = map.mouse() || pxCenter();
73907                 return projection.invert(coord);
73908             };
73909
73910
73911             map.dblclickZoomEnable = function(val) {
73912                 if (!arguments.length) return _dblClickZoomEnabled;
73913                 _dblClickZoomEnabled = val;
73914                 return map;
73915             };
73916
73917
73918             map.redrawEnable = function(val) {
73919                 if (!arguments.length) return _redrawEnabled;
73920                 _redrawEnabled = val;
73921                 return map;
73922             };
73923
73924
73925             map.isTransformed = function() {
73926                 return _isTransformed;
73927             };
73928
73929
73930             function setTransform(t2, duration, force) {
73931                 var t = projection.transform();
73932                 if (!force && t2.k === t.k && t2.x === t.x && t2.y === t.y) return false;
73933
73934                 if (duration) {
73935                     _selection
73936                         .transition()
73937                         .duration(duration)
73938                         .on('start', function() { map.startEase(); })
73939                         .call(_zoomerPanner.transform, identity$2.translate(t2.x, t2.y).scale(t2.k));
73940                 } else {
73941                     projection.transform(t2);
73942                     _transformStart = t2;
73943                     _selection.call(_zoomerPanner.transform, _transformStart);
73944                 }
73945
73946                 return true;
73947             }
73948
73949
73950             function setCenterZoom(loc2, z2, duration, force) {
73951                 var c = map.center();
73952                 var z = map.zoom();
73953                 if (loc2[0] === c[0] && loc2[1] === c[1] && z2 === z && !force) return false;
73954
73955                 var proj = geoRawMercator().transform(projection.transform());  // copy projection
73956
73957                 var k2 = clamp(geoZoomToScale(z2, TILESIZE), kMin, kMax);
73958                 proj.scale(k2);
73959
73960                 var t = proj.translate();
73961                 var point = proj(loc2);
73962
73963                 var center = pxCenter();
73964                 t[0] += center[0] - point[0];
73965                 t[1] += center[1] - point[1];
73966
73967                 return setTransform(identity$2.translate(t[0], t[1]).scale(k2), duration, force);
73968             }
73969
73970
73971             map.pan = function(delta, duration) {
73972                 var t = projection.translate();
73973                 var k = projection.scale();
73974
73975                 t[0] += delta[0];
73976                 t[1] += delta[1];
73977
73978                 if (duration) {
73979                     _selection
73980                         .transition()
73981                         .duration(duration)
73982                         .on('start', function() { map.startEase(); })
73983                         .call(_zoomerPanner.transform, identity$2.translate(t[0], t[1]).scale(k));
73984                 } else {
73985                     projection.translate(t);
73986                     _transformStart = projection.transform();
73987                     _selection.call(_zoomerPanner.transform, _transformStart);
73988                     dispatch$1.call('move', this, map);
73989                     immediateRedraw();
73990                 }
73991
73992                 return map;
73993             };
73994
73995
73996             map.dimensions = function(val) {
73997                 if (!arguments.length) return _dimensions;
73998
73999                 _dimensions = val;
74000                 drawLayers.dimensions(_dimensions);
74001                 context.background().dimensions(_dimensions);
74002                 projection.clipExtent([[0, 0], _dimensions]);
74003                 _getMouseCoords = utilFastMouse(supersurface.node());
74004
74005                 scheduleRedraw();
74006                 return map;
74007             };
74008
74009
74010             function zoomIn(delta) {
74011                 setCenterZoom(map.center(), ~~map.zoom() + delta, 250, true);
74012             }
74013
74014             function zoomOut(delta) {
74015                 setCenterZoom(map.center(), ~~map.zoom() - delta, 250, true);
74016             }
74017
74018             map.zoomIn = function() { zoomIn(1); };
74019             map.zoomInFurther = function() { zoomIn(4); };
74020             map.canZoomIn = function() { return map.zoom() < maxZoom; };
74021
74022             map.zoomOut = function() { zoomOut(1); };
74023             map.zoomOutFurther = function() { zoomOut(4); };
74024             map.canZoomOut = function() { return map.zoom() > minZoom; };
74025
74026             map.center = function(loc2) {
74027                 if (!arguments.length) {
74028                     return projection.invert(pxCenter());
74029                 }
74030
74031                 if (setCenterZoom(loc2, map.zoom())) {
74032                     dispatch$1.call('move', this, map);
74033                 }
74034
74035                 scheduleRedraw();
74036                 return map;
74037             };
74038
74039             map.unobscuredCenterZoomEase = function(loc, zoom) {
74040                 var offset = map.unobscuredOffsetPx();
74041
74042                 var proj = geoRawMercator().transform(projection.transform());  // copy projection
74043                 // use the target zoom to calculate the offset center
74044                 proj.scale(geoZoomToScale(zoom, TILESIZE));
74045
74046                 var locPx = proj(loc);
74047                 var offsetLocPx = [locPx[0] + offset[0], locPx[1] + offset[1]];
74048                 var offsetLoc = proj.invert(offsetLocPx);
74049
74050                 map.centerZoomEase(offsetLoc, zoom);
74051             };
74052
74053             map.unobscuredOffsetPx = function() {
74054                 var openPane = context.container().select('.map-panes .map-pane.shown');
74055                 if (!openPane.empty()) {
74056                     return [openPane.node().offsetWidth/2, 0];
74057                 }
74058                 return [0, 0];
74059             };
74060
74061             map.zoom = function(z2) {
74062                 if (!arguments.length) {
74063                     return Math.max(geoScaleToZoom(projection.scale(), TILESIZE), 0);
74064                 }
74065
74066                 if (z2 < _minzoom) {
74067                     surface.interrupt();
74068                     dispatch$1.call('hitMinZoom', this, map);
74069                     z2 = context.minEditableZoom();
74070                 }
74071
74072                 if (setCenterZoom(map.center(), z2)) {
74073                     dispatch$1.call('move', this, map);
74074                 }
74075
74076                 scheduleRedraw();
74077                 return map;
74078             };
74079
74080
74081             map.centerZoom = function(loc2, z2) {
74082                 if (setCenterZoom(loc2, z2)) {
74083                     dispatch$1.call('move', this, map);
74084                 }
74085
74086                 scheduleRedraw();
74087                 return map;
74088             };
74089
74090
74091             map.zoomTo = function(entity) {
74092                 var extent = entity.extent(context.graph());
74093                 if (!isFinite(extent.area())) return map;
74094
74095                 var z2 = clamp(map.trimmedExtentZoom(extent), 0, 20);
74096                 return map.centerZoom(extent.center(), z2);
74097             };
74098
74099
74100             map.centerEase = function(loc2, duration) {
74101                 duration = duration || 250;
74102                 setCenterZoom(loc2, map.zoom(), duration);
74103                 return map;
74104             };
74105
74106
74107             map.zoomEase = function(z2, duration) {
74108                 duration = duration || 250;
74109                 setCenterZoom(map.center(), z2, duration, false);
74110                 return map;
74111             };
74112
74113
74114             map.centerZoomEase = function(loc2, z2, duration) {
74115                 duration = duration || 250;
74116                 setCenterZoom(loc2, z2, duration, false);
74117                 return map;
74118             };
74119
74120
74121             map.transformEase = function(t2, duration) {
74122                 duration = duration || 250;
74123                 setTransform(t2, duration, false /* don't force */);
74124                 return map;
74125             };
74126
74127
74128             map.zoomToEase = function(obj, duration) {
74129                 var extent;
74130                 if (Array.isArray(obj)) {
74131                     obj.forEach(function(entity) {
74132                         var entityExtent = entity.extent(context.graph());
74133                         if (!extent) {
74134                             extent = entityExtent;
74135                         } else {
74136                             extent = extent.extend(entityExtent);
74137                         }
74138                     });
74139                 } else {
74140                     extent = obj.extent(context.graph());
74141                 }
74142                 if (!isFinite(extent.area())) return map;
74143
74144                 var z2 = clamp(map.trimmedExtentZoom(extent), 0, 20);
74145                 return map.centerZoomEase(extent.center(), z2, duration);
74146             };
74147
74148
74149             map.startEase = function() {
74150                 utilBindOnce(surface, _pointerPrefix + 'down.ease', function() {
74151                     map.cancelEase();
74152                 });
74153                 return map;
74154             };
74155
74156
74157             map.cancelEase = function() {
74158                 _selection.interrupt();
74159                 return map;
74160             };
74161
74162
74163             map.extent = function(val) {
74164                 if (!arguments.length) {
74165                     return new geoExtent(
74166                         projection.invert([0, _dimensions[1]]),
74167                         projection.invert([_dimensions[0], 0])
74168                     );
74169                 } else {
74170                     var extent = geoExtent(val);
74171                     map.centerZoom(extent.center(), map.extentZoom(extent));
74172                 }
74173             };
74174
74175
74176             map.trimmedExtent = function(val) {
74177                 if (!arguments.length) {
74178                     var headerY = 71;
74179                     var footerY = 30;
74180                     var pad = 10;
74181                     return new geoExtent(
74182                         projection.invert([pad, _dimensions[1] - footerY - pad]),
74183                         projection.invert([_dimensions[0] - pad, headerY + pad])
74184                     );
74185                 } else {
74186                     var extent = geoExtent(val);
74187                     map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
74188                 }
74189             };
74190
74191
74192             function calcExtentZoom(extent, dim) {
74193                 var tl = projection([extent[0][0], extent[1][1]]);
74194                 var br = projection([extent[1][0], extent[0][1]]);
74195
74196                 // Calculate maximum zoom that fits extent
74197                 var hFactor = (br[0] - tl[0]) / dim[0];
74198                 var vFactor = (br[1] - tl[1]) / dim[1];
74199                 var hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2;
74200                 var vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2;
74201                 var newZoom = map.zoom() - Math.max(hZoomDiff, vZoomDiff);
74202
74203                 return newZoom;
74204             }
74205
74206
74207             map.extentZoom = function(val) {
74208                 return calcExtentZoom(geoExtent(val), _dimensions);
74209             };
74210
74211
74212             map.trimmedExtentZoom = function(val) {
74213                 var trimY = 120;
74214                 var trimX = 40;
74215                 var trimmed = [_dimensions[0] - trimX, _dimensions[1] - trimY];
74216                 return calcExtentZoom(geoExtent(val), trimmed);
74217             };
74218
74219
74220             map.withinEditableZoom = function() {
74221                 return map.zoom() >= context.minEditableZoom();
74222             };
74223
74224
74225             map.isInWideSelection = function() {
74226                 return !map.withinEditableZoom() && context.selectedIDs().length;
74227             };
74228
74229
74230             map.editableDataEnabled = function(skipZoomCheck) {
74231
74232                 var layer = context.layers().layer('osm');
74233                 if (!layer || !layer.enabled()) return false;
74234
74235                 return skipZoomCheck || map.withinEditableZoom();
74236             };
74237
74238
74239             map.notesEditable = function() {
74240                 var layer = context.layers().layer('notes');
74241                 if (!layer || !layer.enabled()) return false;
74242
74243                 return map.withinEditableZoom();
74244             };
74245
74246
74247             map.minzoom = function(val) {
74248                 if (!arguments.length) return _minzoom;
74249                 _minzoom = val;
74250                 return map;
74251             };
74252
74253
74254             map.toggleHighlightEdited = function() {
74255                 surface.classed('highlight-edited', !surface.classed('highlight-edited'));
74256                 map.pan([0,0]);  // trigger a redraw
74257                 dispatch$1.call('changeHighlighting', this);
74258             };
74259
74260
74261             map.areaFillOptions = ['wireframe', 'partial', 'full'];
74262
74263             map.activeAreaFill = function(val) {
74264                 if (!arguments.length) return corePreferences('area-fill') || 'partial';
74265
74266                 corePreferences('area-fill', val);
74267                 if (val !== 'wireframe') {
74268                     corePreferences('area-fill-toggle', val);
74269                 }
74270                 updateAreaFill();
74271                 map.pan([0,0]);  // trigger a redraw
74272                 dispatch$1.call('changeAreaFill', this);
74273                 return map;
74274             };
74275
74276             map.toggleWireframe = function() {
74277
74278                 var activeFill = map.activeAreaFill();
74279
74280                 if (activeFill === 'wireframe') {
74281                     activeFill = corePreferences('area-fill-toggle') || 'partial';
74282                 } else {
74283                     activeFill = 'wireframe';
74284                 }
74285
74286                 map.activeAreaFill(activeFill);
74287             };
74288
74289             function updateAreaFill() {
74290                 var activeFill = map.activeAreaFill();
74291                 map.areaFillOptions.forEach(function(opt) {
74292                     surface.classed('fill-' + opt, Boolean(opt === activeFill));
74293                 });
74294             }
74295
74296
74297             map.layers = () => drawLayers;
74298
74299
74300             map.doubleUpHandler = function() {
74301                 return _doubleUpHandler;
74302             };
74303
74304
74305             return utilRebind(map, dispatch$1, 'on');
74306         }
74307
74308         function rendererPhotos(context) {
74309             var dispatch$1 = dispatch('change');
74310             var _layerIDs = ['streetside', 'mapillary', 'mapillary-map-features', 'mapillary-signs', 'openstreetcam'];
74311             var _allPhotoTypes = ['flat', 'panoramic'];
74312             var _shownPhotoTypes = _allPhotoTypes.slice();   // shallow copy
74313
74314             function photos() {}
74315
74316             function updateStorage() {
74317                 if (window.mocha) return;
74318
74319                 var hash = utilStringQs(window.location.hash);
74320                 var enabled = context.layers().all().filter(function(d) {
74321                     return _layerIDs.indexOf(d.id) !== -1 && d.layer && d.layer.supported() && d.layer.enabled();
74322                 }).map(function(d) {
74323                     return d.id;
74324                 });
74325                 if (enabled.length) {
74326                     hash.photo_overlay = enabled.join(',');
74327                 } else {
74328                     delete hash.photo_overlay;
74329                 }
74330                 window.location.replace('#' + utilQsString(hash, true));
74331             }
74332
74333             photos.overlayLayerIDs = function() {
74334                 return _layerIDs;
74335             };
74336
74337             photos.allPhotoTypes = function() {
74338                 return _allPhotoTypes;
74339             };
74340
74341             function showsLayer(id) {
74342                 var layer = context.layers().layer(id);
74343                 return layer && layer.supported() && layer.enabled();
74344             }
74345
74346             photos.shouldFilterByPhotoType = function() {
74347                 return showsLayer('mapillary') ||
74348                     (showsLayer('streetside') && showsLayer('openstreetcam'));
74349             };
74350
74351             photos.showsPhotoType = function(val) {
74352                 if (!photos.shouldFilterByPhotoType()) return true;
74353
74354                 return _shownPhotoTypes.indexOf(val) !== -1;
74355             };
74356
74357             photos.showsFlat = function() {
74358                 return photos.showsPhotoType('flat');
74359             };
74360
74361             photos.showsPanoramic = function() {
74362                 return photos.showsPhotoType('panoramic');
74363             };
74364
74365             photos.togglePhotoType = function(val) {
74366                 var index = _shownPhotoTypes.indexOf(val);
74367                 if (index !== -1) {
74368                     _shownPhotoTypes.splice(index, 1);
74369                 } else {
74370                     _shownPhotoTypes.push(val);
74371                 }
74372                 dispatch$1.call('change', this);
74373                 return photos;
74374             };
74375
74376             photos.init = function() {
74377                 var hash = utilStringQs(window.location.hash);
74378                 if (hash.photo_overlay) {
74379                     var hashOverlayIDs = hash.photo_overlay.replace(/;/g, ',').split(',');
74380                     hashOverlayIDs.forEach(function(id) {
74381                         var layer = context.layers().layer(id);
74382                         if (layer) layer.enabled(true);
74383                     });
74384                 }
74385
74386                 context.layers().on('change.rendererPhotos', updateStorage);
74387             };
74388
74389             return utilRebind(photos, dispatch$1, 'on');
74390         }
74391
74392         function uiAccount(context) {
74393             var osm = context.connection();
74394
74395
74396             function update(selection) {
74397                 if (!osm) return;
74398
74399                 if (!osm.authenticated()) {
74400                     selection.selectAll('.userLink, .logoutLink')
74401                         .classed('hide', true);
74402                     return;
74403                 }
74404
74405                 osm.userDetails(function(err, details) {
74406                     var userLink = selection.select('.userLink'),
74407                         logoutLink = selection.select('.logoutLink');
74408
74409                     userLink.html('');
74410                     logoutLink.html('');
74411
74412                     if (err || !details) return;
74413
74414                     selection.selectAll('.userLink, .logoutLink')
74415                         .classed('hide', false);
74416
74417                     // Link
74418                     userLink.append('a')
74419                         .attr('href', osm.userURL(details.display_name))
74420                         .attr('target', '_blank');
74421
74422                     // Add thumbnail or dont
74423                     if (details.image_url) {
74424                         userLink.append('img')
74425                             .attr('class', 'icon pre-text user-icon')
74426                             .attr('src', details.image_url);
74427                     } else {
74428                         userLink
74429                             .call(svgIcon('#iD-icon-avatar', 'pre-text light'));
74430                     }
74431
74432                     // Add user name
74433                     userLink.append('span')
74434                         .attr('class', 'label')
74435                         .text(details.display_name);
74436
74437                     logoutLink.append('a')
74438                         .attr('class', 'logout')
74439                         .attr('href', '#')
74440                         .text(_t('logout'))
74441                         .on('click.logout', function() {
74442                             event.preventDefault();
74443                             osm.logout();
74444                         });
74445                 });
74446             }
74447
74448
74449             return function(selection) {
74450                 selection.append('li')
74451                     .attr('class', 'logoutLink')
74452                     .classed('hide', true);
74453
74454                 selection.append('li')
74455                     .attr('class', 'userLink')
74456                     .classed('hide', true);
74457
74458                 if (osm) {
74459                     osm.on('change.account', function() { update(selection); });
74460                     update(selection);
74461                 }
74462             };
74463         }
74464
74465         function uiAttribution(context) {
74466           let _selection = select(null);
74467
74468
74469           function render(selection, data, klass) {
74470             let div = selection.selectAll(`.${klass}`)
74471               .data([0]);
74472
74473             div = div.enter()
74474               .append('div')
74475               .attr('class', klass)
74476               .merge(div);
74477
74478
74479             let attributions = div.selectAll('.attribution')
74480               .data(data, d => d.id);
74481
74482             attributions.exit()
74483               .remove();
74484
74485             attributions = attributions.enter()
74486               .append('span')
74487               .attr('class', 'attribution')
74488               .each((d, i, nodes) => {
74489                 let attribution = select(nodes[i]);
74490
74491                 if (d.terms_html) {
74492                   attribution.html(d.terms_html);
74493                   return;
74494                 }
74495
74496                 if (d.terms_url) {
74497                   attribution = attribution
74498                     .append('a')
74499                     .attr('href', d.terms_url)
74500                     .attr('target', '_blank');
74501                 }
74502
74503                 const sourceID = d.id.replace(/\./g, '<TX_DOT>');
74504                 const terms_text = _t(`imagery.${sourceID}.attribution.text`,
74505                   { default: d.terms_text || d.id || d.name() }
74506                 );
74507
74508                 if (d.icon && !d.overlay) {
74509                   attribution
74510                     .append('img')
74511                     .attr('class', 'source-image')
74512                     .attr('src', d.icon);
74513                 }
74514
74515                 attribution
74516                   .append('span')
74517                   .attr('class', 'attribution-text')
74518                   .text(terms_text);
74519               })
74520               .merge(attributions);
74521
74522
74523             let copyright = attributions.selectAll('.copyright-notice')
74524               .data(d => {
74525                 let notice = d.copyrightNotices(context.map().zoom(), context.map().extent());
74526                 return notice ? [notice] : [];
74527               });
74528
74529             copyright.exit()
74530               .remove();
74531
74532             copyright = copyright.enter()
74533               .append('span')
74534               .attr('class', 'copyright-notice')
74535               .merge(copyright);
74536
74537             copyright
74538               .text(String);
74539           }
74540
74541
74542           function update() {
74543             let baselayer = context.background().baseLayerSource();
74544             _selection
74545               .call(render, (baselayer ? [baselayer] : []), 'base-layer-attribution');
74546
74547             const z = context.map().zoom();
74548             let overlays = context.background().overlayLayerSources() || [];
74549             _selection
74550               .call(render, overlays.filter(s => s.validZoom(z)), 'overlay-layer-attribution');
74551           }
74552
74553
74554           return function(selection) {
74555             _selection = selection;
74556
74557             context.background()
74558               .on('change.attribution', update);
74559
74560             context.map()
74561               .on('move.attribution', throttle(update, 400, { leading: false }));
74562
74563             update();
74564           };
74565         }
74566
74567         function uiContributors(context) {
74568             var osm = context.connection(),
74569                 debouncedUpdate = debounce(function() { update(); }, 1000),
74570                 limit = 4,
74571                 hidden = false,
74572                 wrap = select(null);
74573
74574
74575             function update() {
74576                 if (!osm) return;
74577
74578                 var users = {},
74579                     entities = context.history().intersects(context.map().extent());
74580
74581                 entities.forEach(function(entity) {
74582                     if (entity && entity.user) users[entity.user] = true;
74583                 });
74584
74585                 var u = Object.keys(users),
74586                     subset = u.slice(0, u.length > limit ? limit - 1 : limit);
74587
74588                 wrap.html('')
74589                     .call(svgIcon('#iD-icon-nearby', 'pre-text light'));
74590
74591                 var userList = select(document.createElement('span'));
74592
74593                 userList.selectAll()
74594                     .data(subset)
74595                     .enter()
74596                     .append('a')
74597                     .attr('class', 'user-link')
74598                     .attr('href', function(d) { return osm.userURL(d); })
74599                     .attr('target', '_blank')
74600                     .text(String);
74601
74602                 if (u.length > limit) {
74603                     var count = select(document.createElement('span'));
74604
74605                     count.append('a')
74606                         .attr('target', '_blank')
74607                         .attr('href', function() {
74608                             return osm.changesetsURL(context.map().center(), context.map().zoom());
74609                         })
74610                         .text(u.length - limit + 1);
74611
74612                     wrap.append('span')
74613                         .html(_t('contributors.truncated_list', { users: userList.html(), count: count.html() }));
74614
74615                 } else {
74616                     wrap.append('span')
74617                         .html(_t('contributors.list', { users: userList.html() }));
74618                 }
74619
74620                 if (!u.length) {
74621                     hidden = true;
74622                     wrap
74623                         .transition()
74624                         .style('opacity', 0);
74625
74626                 } else if (hidden) {
74627                     wrap
74628                         .transition()
74629                         .style('opacity', 1);
74630                 }
74631             }
74632
74633
74634             return function(selection) {
74635                 if (!osm) return;
74636                 wrap = selection;
74637                 update();
74638
74639                 osm.on('loaded.contributors', debouncedUpdate);
74640                 context.map().on('move.contributors', debouncedUpdate);
74641             };
74642         }
74643
74644         var _popoverID = 0;
74645
74646         function uiPopover(klass) {
74647             var _id = _popoverID++;
74648             var _anchorSelection = select(null);
74649             var popover = function(selection) {
74650                 _anchorSelection = selection;
74651                 selection.each(setup);
74652             };
74653             var _animation = utilFunctor(false);
74654             var _placement = utilFunctor('top'); // top, bottom, left, right
74655             var _alignment = utilFunctor('center');  // leading, center, trailing
74656             var _scrollContainer = utilFunctor(select(null));
74657             var _content;
74658             var _displayType = utilFunctor('');
74659             var _hasArrow = utilFunctor(true);
74660
74661             // use pointer events on supported platforms; fallback to mouse events
74662             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
74663
74664             popover.displayType = function(val) {
74665                 if (arguments.length) {
74666                     _displayType = utilFunctor(val);
74667                     return popover;
74668                 } else {
74669                     return _displayType;
74670                 }
74671             };
74672
74673             popover.hasArrow = function(val) {
74674                 if (arguments.length) {
74675                     _hasArrow = utilFunctor(val);
74676                     return popover;
74677                 } else {
74678                     return _hasArrow;
74679                 }
74680             };
74681
74682             popover.placement = function(val) {
74683                 if (arguments.length) {
74684                     _placement = utilFunctor(val);
74685                     return popover;
74686                 } else {
74687                     return _placement;
74688                 }
74689             };
74690
74691             popover.alignment = function(val) {
74692                 if (arguments.length) {
74693                     _alignment = utilFunctor(val);
74694                     return popover;
74695                 } else {
74696                     return _alignment;
74697                 }
74698             };
74699
74700             popover.scrollContainer = function(val) {
74701                 if (arguments.length) {
74702                     _scrollContainer = utilFunctor(val);
74703                     return popover;
74704                 } else {
74705                     return _scrollContainer;
74706                 }
74707             };
74708
74709             popover.content = function(val) {
74710                 if (arguments.length) {
74711                     _content = val;
74712                     return popover;
74713                 } else {
74714                     return _content;
74715                 }
74716             };
74717
74718             popover.isShown = function() {
74719                 var popoverSelection = _anchorSelection.select('.popover-' + _id);
74720                 return !popoverSelection.empty() && popoverSelection.classed('in');
74721             };
74722
74723             popover.show = function() {
74724                 _anchorSelection.each(show);
74725             };
74726
74727             popover.updateContent = function() {
74728                 _anchorSelection.each(updateContent);
74729             };
74730
74731             popover.hide = function() {
74732                 _anchorSelection.each(hide);
74733             };
74734
74735             popover.toggle = function() {
74736                 _anchorSelection.each(toggle);
74737             };
74738
74739             popover.destroy = function(selection, selector) {
74740                 // by default, just destroy the current popover
74741                 selector = selector || '.popover-' + _id;
74742
74743                 selection
74744                     .on(_pointerPrefix + 'enter.popover', null)
74745                     .on(_pointerPrefix + 'leave.popover', null)
74746                     .on(_pointerPrefix + 'up.popover', null)
74747                     .on(_pointerPrefix + 'down.popover', null)
74748                     .on('click.popover', null)
74749                     .attr('title', function() {
74750                         return this.getAttribute('data-original-title') || this.getAttribute('title');
74751                     })
74752                     .attr('data-original-title', null)
74753                     .selectAll(selector)
74754                     .remove();
74755             };
74756
74757
74758             popover.destroyAny = function(selection) {
74759                 selection.call(popover.destroy, '.popover');
74760             };
74761
74762             function setup() {
74763                 var anchor = select(this);
74764                 var animate = _animation.apply(this, arguments);
74765                 var popoverSelection = anchor.selectAll('.popover-' + _id)
74766                     .data([0]);
74767
74768
74769                 var enter = popoverSelection.enter()
74770                     .append('div')
74771                     .attr('class', 'popover popover-' + _id + ' ' + (klass ? klass : ''))
74772                     .classed('arrowed', _hasArrow.apply(this, arguments));
74773
74774                 enter
74775                     .append('div')
74776                     .attr('class', 'popover-arrow');
74777
74778                 enter
74779                     .append('div')
74780                     .attr('class', 'popover-inner');
74781
74782                 popoverSelection = enter
74783                     .merge(popoverSelection);
74784
74785                 if (animate) {
74786                     popoverSelection.classed('fade', true);
74787                 }
74788
74789                 var display = _displayType.apply(this, arguments);
74790
74791                 if (display === 'hover') {
74792                     var _lastNonMouseEnterTime;
74793                     anchor.on(_pointerPrefix + 'enter.popover', function() {
74794
74795                         if (event.pointerType) {
74796                             if (event.pointerType !== 'mouse') {
74797                                 _lastNonMouseEnterTime = event.timeStamp;
74798                                 // only allow hover behavior for mouse input
74799                                 return;
74800                             } else if (_lastNonMouseEnterTime &&
74801                                 event.timeStamp - _lastNonMouseEnterTime < 1500) {
74802                                 // HACK: iOS 13.4 sends an erroneous `mouse` type pointerenter
74803                                 // event for non-mouse interactions right after sending
74804                                 // the correct type pointerenter event. Workaround by discarding
74805                                 // any mouse event that occurs immediately after a non-mouse event.
74806                                 return;
74807                             }
74808                         }
74809
74810                         // don't show if buttons are pressed, e.g. during click and drag of map
74811                         if (event.buttons !== 0) return;
74812
74813                         show.apply(this, arguments);
74814                     });
74815                     anchor.on(_pointerPrefix + 'leave.popover', function() {
74816                         hide.apply(this, arguments);
74817                     });
74818
74819                 } else if (display === 'clickFocus') {
74820                     anchor
74821                         .on(_pointerPrefix + 'down.popover', function() {
74822                             event.preventDefault();
74823                             event.stopPropagation();
74824                         })
74825                         .on(_pointerPrefix + 'up.popover', function() {
74826                             event.preventDefault();
74827                             event.stopPropagation();
74828                         })
74829                         .on('click.popover', toggle);
74830
74831                     popoverSelection
74832                         .attr('tabindex', 0)
74833                         .on('blur.popover', function() {
74834                             anchor.each(function() {
74835                                 hide.apply(this, arguments);
74836                             });
74837                         });
74838                 }
74839             }
74840
74841
74842             function show() {
74843                 var anchor = select(this);
74844                 var popoverSelection = anchor.selectAll('.popover-' + _id);
74845
74846                 if (popoverSelection.empty()) {
74847                     // popover was removed somehow, put it back
74848                     anchor.call(popover.destroy);
74849                     anchor.each(setup);
74850                     popoverSelection = anchor.selectAll('.popover-' + _id);
74851                 }
74852
74853                 popoverSelection.classed('in', true);
74854
74855                 var displayType = _displayType.apply(this, arguments);
74856                 if (displayType === 'clickFocus') {
74857                     anchor.classed('active', true);
74858                     popoverSelection.node().focus();
74859                 }
74860
74861                 anchor.each(updateContent);
74862             }
74863
74864             function updateContent() {
74865                 var anchor = select(this);
74866
74867                 if (_content) {
74868                     anchor.selectAll('.popover-' + _id + ' > .popover-inner')
74869                         .call(_content.apply(this, arguments));
74870                 }
74871
74872                 updatePosition.apply(this, arguments);
74873                 // hack: update multiple times to fix instances where the absolute offset is
74874                 // set before the dynamic popover size is calculated by the browser
74875                 updatePosition.apply(this, arguments);
74876                 updatePosition.apply(this, arguments);
74877             }
74878
74879
74880             function updatePosition() {
74881
74882                 var anchor = select(this);
74883                 var popoverSelection = anchor.selectAll('.popover-' + _id);
74884
74885                 var scrollContainer = _scrollContainer && _scrollContainer.apply(this, arguments);
74886                 var scrollNode = scrollContainer && !scrollContainer.empty() && scrollContainer.node();
74887                 var scrollLeft = scrollNode ? scrollNode.scrollLeft : 0;
74888                 var scrollTop = scrollNode ? scrollNode.scrollTop : 0;
74889
74890                 var placement = _placement.apply(this, arguments);
74891                 popoverSelection
74892                     .classed('left', false)
74893                     .classed('right', false)
74894                     .classed('top', false)
74895                     .classed('bottom', false)
74896                     .classed(placement, true);
74897
74898                 var alignment = _alignment.apply(this, arguments);
74899                 var alignFactor = 0.5;
74900                 if (alignment === 'leading') {
74901                     alignFactor = 0;
74902                 } else if (alignment === 'trailing') {
74903                     alignFactor = 1;
74904                 }
74905                 var anchorFrame = getFrame(anchor.node());
74906                 var popoverFrame = getFrame(popoverSelection.node());
74907                 var position;
74908
74909                 switch (placement) {
74910                     case 'top':
74911                     position = {
74912                         x: anchorFrame.x + (anchorFrame.w - popoverFrame.w) * alignFactor,
74913                         y: anchorFrame.y - popoverFrame.h
74914                     };
74915                     break;
74916                     case 'bottom':
74917                     position = {
74918                         x: anchorFrame.x + (anchorFrame.w - popoverFrame.w) * alignFactor,
74919                         y: anchorFrame.y + anchorFrame.h
74920                     };
74921                     break;
74922                     case 'left':
74923                     position = {
74924                         x: anchorFrame.x - popoverFrame.w,
74925                         y: anchorFrame.y + (anchorFrame.h - popoverFrame.h) * alignFactor
74926                     };
74927                     break;
74928                     case 'right':
74929                     position = {
74930                         x: anchorFrame.x + anchorFrame.w,
74931                         y: anchorFrame.y + (anchorFrame.h - popoverFrame.h) * alignFactor
74932                     };
74933                     break;
74934                 }
74935
74936                 if (position) {
74937
74938                     if (scrollNode && (placement === 'top' || placement === 'bottom')) {
74939
74940                         var initialPosX = position.x;
74941
74942                         if (position.x + popoverFrame.w > scrollNode.offsetWidth - 10) {
74943                             position.x = scrollNode.offsetWidth - 10 - popoverFrame.w;
74944                         } else if (position.x < 10) {
74945                             position.x = 10;
74946                         }
74947
74948                         var arrow = anchor.selectAll('.popover-' + _id + ' > .popover-arrow');
74949                         // keep the arrow centered on the button, or as close as possible
74950                         var arrowPosX = Math.min(Math.max(popoverFrame.w / 2 - (position.x - initialPosX), 10), popoverFrame.w - 10);
74951                         arrow.style('left', ~~arrowPosX + 'px');
74952                     }
74953
74954                     popoverSelection.style('left', ~~position.x + 'px').style('top', ~~position.y + 'px');
74955                 } else {
74956                     popoverSelection.style('left', null).style('top', null);
74957                 }
74958
74959                 function getFrame(node) {
74960                     var positionStyle = select(node).style('position');
74961                     if (positionStyle === 'absolute' || positionStyle === 'static') {
74962                         return {
74963                             x: node.offsetLeft - scrollLeft,
74964                             y: node.offsetTop - scrollTop,
74965                             w: node.offsetWidth,
74966                             h: node.offsetHeight
74967                         };
74968                     } else {
74969                         return {
74970                             x: 0,
74971                             y: 0,
74972                             w: node.offsetWidth,
74973                             h: node.offsetHeight
74974                         };
74975                     }
74976                 }
74977             }
74978
74979
74980             function hide() {
74981                 var anchor = select(this);
74982                 if (_displayType.apply(this, arguments) === 'clickFocus') {
74983                     anchor.classed('active', false);
74984                 }
74985                 anchor.selectAll('.popover-' + _id).classed('in', false);
74986             }
74987
74988
74989             function toggle() {
74990                 if (select(this).select('.popover-' + _id).classed('in')) {
74991                     hide.apply(this, arguments);
74992                 } else {
74993                     show.apply(this, arguments);
74994                 }
74995             }
74996
74997
74998             return popover;
74999         }
75000
75001         function uiTooltip(klass) {
75002
75003             var tooltip = uiPopover((klass || '') + ' tooltip')
75004                 .displayType('hover');
75005
75006             var _title = function() {
75007                 var title = this.getAttribute('data-original-title');
75008                 if (title) {
75009                     return title;
75010                 } else {
75011                     title = this.getAttribute('title');
75012                     this.removeAttribute('title');
75013                     this.setAttribute('data-original-title', title);
75014                 }
75015                 return title;
75016             };
75017
75018             var _heading = utilFunctor(null);
75019             var _keys = utilFunctor(null);
75020
75021             tooltip.title = function(val) {
75022                 if (!arguments.length) return _title;
75023                 _title = utilFunctor(val);
75024                 return tooltip;
75025             };
75026
75027             tooltip.heading = function(val) {
75028                 if (!arguments.length) return _heading;
75029                 _heading = utilFunctor(val);
75030                 return tooltip;
75031             };
75032
75033             tooltip.keys = function(val) {
75034                 if (!arguments.length) return _keys;
75035                 _keys = utilFunctor(val);
75036                 return tooltip;
75037             };
75038
75039             tooltip.content(function() {
75040                 var heading = _heading.apply(this, arguments);
75041                 var text = _title.apply(this, arguments);
75042                 var keys = _keys.apply(this, arguments);
75043
75044                 return function(selection) {
75045
75046                     var headingSelect = selection
75047                         .selectAll('.tooltip-heading')
75048                         .data(heading ? [heading] :[]);
75049
75050                     headingSelect.exit()
75051                         .remove();
75052
75053                     headingSelect.enter()
75054                         .append('div')
75055                         .attr('class', 'tooltip-heading')
75056                         .merge(headingSelect)
75057                         .html(heading);
75058
75059                     var textSelect = selection
75060                         .selectAll('.tooltip-text')
75061                         .data(text ? [text] :[]);
75062
75063                     textSelect.exit()
75064                         .remove();
75065
75066                     textSelect.enter()
75067                         .append('div')
75068                         .attr('class', 'tooltip-text')
75069                         .merge(textSelect)
75070                         .html(text);
75071
75072                     var keyhintWrap = selection
75073                         .selectAll('.keyhint-wrap')
75074                         .data(keys && keys.length ? [0] : []);
75075
75076                     keyhintWrap.exit()
75077                         .remove();
75078
75079                     var keyhintWrapEnter = keyhintWrap.enter()
75080                         .append('div')
75081                         .attr('class', 'keyhint-wrap');
75082
75083                     keyhintWrapEnter
75084                         .append('span')
75085                         .html(_t('tooltip_keyhint'));
75086
75087                     keyhintWrap = keyhintWrapEnter.merge(keyhintWrap);
75088
75089                     keyhintWrap.selectAll('kbd.shortcut')
75090                         .data(keys && keys.length ? keys : [])
75091                         .enter()
75092                         .append('kbd')
75093                         .attr('class', 'shortcut')
75094                         .html(function(d) {
75095                             return d;
75096                         });
75097                 };
75098             });
75099
75100             return tooltip;
75101         }
75102
75103         function uiEditMenu(context) {
75104             var dispatch$1 = dispatch('toggled');
75105
75106             var _menu = select(null);
75107             var _operations = [];
75108             // the position the menu should be displayed relative to
75109             var _anchorLoc = [0, 0];
75110             var _anchorLocLonLat = [0, 0];
75111             // a string indicating how the menu was opened
75112             var _triggerType = '';
75113
75114             var _vpTopMargin = 85; // viewport top margin
75115             var _vpBottomMargin = 45; // viewport bottom margin
75116             var _vpSideMargin = 35;   // viewport side margin
75117
75118             var _menuTop = false;
75119             var _menuHeight;
75120             var _menuWidth;
75121
75122             // hardcode these values to make menu positioning easier
75123             var _verticalPadding = 4;
75124
75125             // see also `.edit-menu .tooltip` CSS; include margin
75126             var _tooltipWidth = 210;
75127
75128             // offset the menu slightly from the target location
75129             var _menuSideMargin = 10;
75130
75131             var _tooltips = [];
75132
75133             var editMenu = function(selection) {
75134
75135                 var isTouchMenu = _triggerType.includes('touch') || _triggerType.includes('pen');
75136
75137                 var ops = _operations.filter(function(op) {
75138                     return !isTouchMenu || !op.mouseOnly;
75139                 });
75140
75141                 if (!ops.length) return;
75142
75143                 _tooltips = [];
75144
75145                 // Position the menu above the anchor for stylus and finger input
75146                 // since the mapper's hand likely obscures the screen below the anchor
75147                 _menuTop = isTouchMenu;
75148
75149                 // Show labels for touch input since there aren't hover tooltips
75150                 var showLabels = isTouchMenu;
75151
75152                 var buttonHeight = showLabels ? 32 : 34;
75153                 if (showLabels) {
75154                     // Get a general idea of the width based on the length of the label
75155                     _menuWidth = 52 + Math.min(120, 6 * Math.max.apply(Math, ops.map(function(op) {
75156                         return op.title.length;
75157                     })));
75158                 } else {
75159                     _menuWidth = 44;
75160                 }
75161
75162                 _menuHeight = _verticalPadding * 2 + ops.length * buttonHeight;
75163
75164                 _menu = selection
75165                     .append('div')
75166                     .attr('class', 'edit-menu')
75167                     .classed('touch-menu', isTouchMenu)
75168                     .style('padding', _verticalPadding + 'px 0');
75169
75170                 var buttons = _menu.selectAll('.edit-menu-item')
75171                     .data(ops);
75172
75173                 // enter
75174                 var buttonsEnter = buttons.enter()
75175                     .append('button')
75176                     .attr('class', function (d) { return 'edit-menu-item edit-menu-item-' + d.id; })
75177                     .style('height', buttonHeight + 'px')
75178                     .on('click', click)
75179                     // don't listen for `mouseup` because we only care about non-mouse pointer types
75180                     .on('pointerup', pointerup)
75181                     .on('pointerdown mousedown', function pointerdown() {
75182                         // don't let button presses also act as map input - #1869
75183                         event.stopPropagation();
75184                     });
75185
75186                 buttonsEnter.each(function(d) {
75187                     var tooltip = uiTooltip()
75188                         .heading(d.title)
75189                         .title(d.tooltip())
75190                         .keys([d.keys[0]]);
75191
75192                     _tooltips.push(tooltip);
75193
75194                     select(this)
75195                         .call(tooltip)
75196                         .append('div')
75197                         .attr('class', 'icon-wrap')
75198                         .call(svgIcon('#iD-operation-' + d.id, 'operation'));
75199                 });
75200
75201                 if (showLabels) {
75202                     buttonsEnter.append('span')
75203                         .attr('class', 'label')
75204                         .text(function(d) {
75205                             return d.title;
75206                         });
75207                 }
75208
75209                 // update
75210                 buttons = buttonsEnter
75211                     .merge(buttons)
75212                     .classed('disabled', function(d) { return d.disabled(); });
75213
75214                 updatePosition();
75215
75216                 var initialScale = context.projection.scale();
75217                 context.map()
75218                     .on('move.edit-menu', function() {
75219                         if (initialScale !== context.projection.scale()) {
75220                             editMenu.close();
75221                         }
75222                     })
75223                     .on('drawn.edit-menu', function(info) {
75224                         if (info.full) updatePosition();
75225                     });
75226
75227                 var lastPointerUpType;
75228                 // `pointerup` is always called before `click`
75229                 function pointerup() {
75230                     lastPointerUpType = event.pointerType;
75231                 }
75232
75233                 function click(operation) {
75234                     event.stopPropagation();
75235                     if (operation.disabled()) {
75236                         if (lastPointerUpType === 'touch' ||
75237                             lastPointerUpType === 'pen') {
75238                             // there are no tooltips for touch interactions so flash feedback instead
75239                             context.ui().flash
75240                                 .duration(4000)
75241                                 .iconName('#iD-operation-' + operation.id)
75242                                 .iconClass('operation disabled')
75243                                 .text(operation.tooltip)();
75244                         }
75245                     } else {
75246                         if (lastPointerUpType === 'touch' ||
75247                             lastPointerUpType === 'pen') {
75248                             context.ui().flash
75249                                 .duration(2000)
75250                                 .iconName('#iD-operation-' + operation.id)
75251                                 .iconClass('operation')
75252                                 .text(operation.annotation() || operation.title)();
75253                         }
75254
75255                         operation();
75256                         editMenu.close();
75257                     }
75258                     lastPointerUpType = null;
75259                 }
75260
75261                 dispatch$1.call('toggled', this, true);
75262             };
75263
75264             function updatePosition() {
75265
75266                 if (!_menu || _menu.empty()) return;
75267
75268                 var anchorLoc = context.projection(_anchorLocLonLat);
75269
75270                 var viewport = context.surfaceRect();
75271
75272                 if (anchorLoc[0] < 0 ||
75273                     anchorLoc[0] > viewport.width ||
75274                     anchorLoc[1] < 0 ||
75275                     anchorLoc[1] > viewport.height) {
75276                     // close the menu if it's gone offscreen
75277
75278                     editMenu.close();
75279                     return;
75280                 }
75281
75282                 var menuLeft = displayOnLeft(viewport);
75283
75284                 var offset = [0, 0];
75285
75286                 offset[0] = menuLeft ? -1 * (_menuSideMargin + _menuWidth) : _menuSideMargin;
75287
75288                 if (_menuTop) {
75289                     if (anchorLoc[1] - _menuHeight < _vpTopMargin) {
75290                         // menu is near top viewport edge, shift downward
75291                         offset[1] = -anchorLoc[1] + _vpTopMargin;
75292                     } else {
75293                         offset[1] = -_menuHeight;
75294                     }
75295                 } else {
75296                     if (anchorLoc[1] + _menuHeight > (viewport.height - _vpBottomMargin)) {
75297                         // menu is near bottom viewport edge, shift upwards
75298                         offset[1] = -anchorLoc[1] - _menuHeight + viewport.height - _vpBottomMargin;
75299                     } else {
75300                         offset[1] = 0;
75301                     }
75302                 }
75303
75304                 var origin = geoVecAdd(anchorLoc, offset);
75305
75306                 _menu
75307                     .style('left', origin[0] + 'px')
75308                     .style('top', origin[1] + 'px');
75309
75310                 var tooltipSide = tooltipPosition(viewport, menuLeft);
75311                 _tooltips.forEach(function(tooltip) {
75312                     tooltip.placement(tooltipSide);
75313                 });
75314
75315                 function displayOnLeft(viewport) {
75316                     if (_mainLocalizer.textDirection() === 'ltr') {
75317                         if ((anchorLoc[0] + _menuSideMargin + _menuWidth) > (viewport.width - _vpSideMargin)) {
75318                             // right menu would be too close to the right viewport edge, go left
75319                             return true;
75320                         }
75321                         // prefer right menu
75322                         return false;
75323
75324                     } else { // rtl
75325                         if ((anchorLoc[0] - _menuSideMargin - _menuWidth) < _vpSideMargin) {
75326                             // left menu would be too close to the left viewport edge, go right
75327                             return false;
75328                         }
75329                         // prefer left menu
75330                         return true;
75331                     }
75332                 }
75333
75334                 function tooltipPosition(viewport, menuLeft) {
75335                     if (_mainLocalizer.textDirection() === 'ltr') {
75336                         if (menuLeft) {
75337                             // if there's not room for a right-side menu then there definitely
75338                             // isn't room for right-side tooltips
75339                             return 'left';
75340                         }
75341                         if ((anchorLoc[0] + _menuSideMargin + _menuWidth + _tooltipWidth) > (viewport.width - _vpSideMargin)) {
75342                             // right tooltips would be too close to the right viewport edge, go left
75343                             return 'left';
75344                         }
75345                         // prefer right tooltips
75346                         return 'right';
75347
75348                     } else { // rtl
75349                         if (!menuLeft) {
75350                             return 'right';
75351                         }
75352                         if ((anchorLoc[0] - _menuSideMargin - _menuWidth - _tooltipWidth) < _vpSideMargin) {
75353                             // left tooltips would be too close to the left viewport edge, go right
75354                             return 'right';
75355                         }
75356                         // prefer left tooltips
75357                         return 'left';
75358                     }
75359                 }
75360             }
75361
75362             editMenu.close = function () {
75363
75364                 context.map()
75365                     .on('move.edit-menu', null)
75366                     .on('drawn.edit-menu', null);
75367
75368                 _menu.remove();
75369                 _tooltips = [];
75370
75371                 dispatch$1.call('toggled', this, false);
75372             };
75373
75374             editMenu.anchorLoc = function(val) {
75375                 if (!arguments.length) return _anchorLoc;
75376                 _anchorLoc = val;
75377                 _anchorLocLonLat = context.projection.invert(_anchorLoc);
75378                 return editMenu;
75379             };
75380
75381             editMenu.triggerType = function(val) {
75382                 if (!arguments.length) return _triggerType;
75383                 _triggerType = val;
75384                 return editMenu;
75385             };
75386
75387             editMenu.operations = function(val) {
75388                 if (!arguments.length) return _operations;
75389                 _operations = val;
75390                 return editMenu;
75391             };
75392
75393             return utilRebind(editMenu, dispatch$1, 'on');
75394         }
75395
75396         function uiFeatureInfo(context) {
75397             function update(selection) {
75398                 var features = context.features();
75399                 var stats = features.stats();
75400                 var count = 0;
75401                 var hiddenList = features.hidden().map(function(k) {
75402                     if (stats[k]) {
75403                         count += stats[k];
75404                         return String(stats[k]) + ' ' + _t('feature.' + k + '.description');
75405                     }
75406                 }).filter(Boolean);
75407
75408                 selection.html('');
75409
75410                 if (hiddenList.length) {
75411                     var tooltipBehavior = uiTooltip()
75412                         .placement('top')
75413                         .title(function() {
75414                             return hiddenList.join('<br/>');
75415                         });
75416
75417                     selection.append('a')
75418                         .attr('class', 'chip')
75419                         .attr('href', '#')
75420                         .attr('tabindex', -1)
75421                         .html(_t('feature_info.hidden_warning', { count: count }))
75422                         .call(tooltipBehavior)
75423                         .on('click', function() {
75424                             tooltipBehavior.hide();
75425                             event.preventDefault();
75426                             // open the Map Data pane
75427                             context.ui().togglePanes(context.container().select('.map-panes .map-data-pane'));
75428                         });
75429                 }
75430
75431                 selection
75432                     .classed('hide', !hiddenList.length);
75433             }
75434
75435
75436             return function(selection) {
75437                 update(selection);
75438
75439                 context.features().on('change.feature_info', function() {
75440                     update(selection);
75441                 });
75442             };
75443         }
75444
75445         function uiFlash(context) {
75446             var _flashTimer;
75447
75448             var _duration = 2000;
75449             var _iconName = '#iD-icon-no';
75450             var _iconClass = 'disabled';
75451             var _text = '';
75452             var _textClass;
75453
75454             function flash() {
75455                 if (_flashTimer) {
75456                     _flashTimer.stop();
75457                 }
75458
75459                 context.container().select('.main-footer-wrap')
75460                     .classed('footer-hide', true)
75461                     .classed('footer-show', false);
75462                 context.container().select('.flash-wrap')
75463                     .classed('footer-hide', false)
75464                     .classed('footer-show', true);
75465
75466                 var content = context.container().select('.flash-wrap').selectAll('.flash-content')
75467                     .data([0]);
75468
75469                 // Enter
75470                 var contentEnter = content.enter()
75471                     .append('div')
75472                     .attr('class', 'flash-content');
75473
75474                 var iconEnter = contentEnter
75475                     .append('svg')
75476                     .attr('class', 'flash-icon icon')
75477                     .append('g')
75478                     .attr('transform', 'translate(10,10)');
75479
75480                 iconEnter
75481                     .append('circle')
75482                     .attr('r', 9);
75483
75484                 iconEnter
75485                     .append('use')
75486                     .attr('transform', 'translate(-7,-7)')
75487                     .attr('width', '14')
75488                     .attr('height', '14');
75489
75490                 contentEnter
75491                     .append('div')
75492                     .attr('class', 'flash-text');
75493
75494
75495                 // Update
75496                 content = content
75497                     .merge(contentEnter);
75498
75499                 content
75500                     .selectAll('.flash-icon')
75501                     .attr('class', 'icon flash-icon ' + (_iconClass || ''));
75502
75503                 content
75504                     .selectAll('.flash-icon use')
75505                     .attr('xlink:href', _iconName);
75506
75507                 content
75508                     .selectAll('.flash-text')
75509                     .attr('class', 'flash-text ' + (_textClass || ''))
75510                     .text(_text);
75511
75512
75513                 _flashTimer = d3_timeout(function() {
75514                     _flashTimer = null;
75515                     context.container().select('.main-footer-wrap')
75516                         .classed('footer-hide', false)
75517                         .classed('footer-show', true);
75518                     context.container().select('.flash-wrap')
75519                         .classed('footer-hide', true)
75520                         .classed('footer-show', false);
75521                 }, _duration);
75522
75523                 return content;
75524             }
75525
75526
75527             flash.duration = function(_) {
75528                 if (!arguments.length) return _duration;
75529                 _duration = _;
75530                 return flash;
75531             };
75532
75533             flash.text = function(_) {
75534                 if (!arguments.length) return _text;
75535                 _text = _;
75536                 return flash;
75537             };
75538
75539             flash.textClass = function(_) {
75540                 if (!arguments.length) return _textClass;
75541                 _textClass = _;
75542                 return flash;
75543             };
75544
75545             flash.iconName = function(_) {
75546                 if (!arguments.length) return _iconName;
75547                 _iconName = _;
75548                 return flash;
75549             };
75550
75551             flash.iconClass = function(_) {
75552                 if (!arguments.length) return _iconClass;
75553                 _iconClass = _;
75554                 return flash;
75555             };
75556
75557             return flash;
75558         }
75559
75560         function uiFullScreen(context) {
75561             var element = context.container().node();
75562             // var button = d3_select(null);
75563
75564
75565             function getFullScreenFn() {
75566                 if (element.requestFullscreen) {
75567                     return element.requestFullscreen;
75568                 } else if (element.msRequestFullscreen) {
75569                     return element.msRequestFullscreen;
75570                 } else if (element.mozRequestFullScreen) {
75571                     return element.mozRequestFullScreen;
75572                 } else if (element.webkitRequestFullscreen) {
75573                     return element.webkitRequestFullscreen;
75574                 }
75575             }
75576
75577
75578             function getExitFullScreenFn() {
75579                 if (document.exitFullscreen) {
75580                     return document.exitFullscreen;
75581                 } else if (document.msExitFullscreen) {
75582                     return document.msExitFullscreen;
75583                 } else if (document.mozCancelFullScreen) {
75584                     return document.mozCancelFullScreen;
75585                 } else if (document.webkitExitFullscreen) {
75586                     return document.webkitExitFullscreen;
75587                 }
75588             }
75589
75590
75591             function isFullScreen() {
75592                 return document.fullscreenElement ||
75593                     document.mozFullScreenElement ||
75594                     document.webkitFullscreenElement ||
75595                     document.msFullscreenElement;
75596             }
75597
75598
75599             function isSupported() {
75600                 return !!getFullScreenFn();
75601             }
75602
75603
75604             function fullScreen() {
75605                 event.preventDefault();
75606                 if (!isFullScreen()) {
75607                     // button.classed('active', true);
75608                     getFullScreenFn().apply(element);
75609                 } else {
75610                     // button.classed('active', false);
75611                     getExitFullScreenFn().apply(document);
75612                 }
75613             }
75614
75615
75616             return function() { // selection) {
75617                 if (!isSupported()) return;
75618
75619                 // button = selection.append('button')
75620                 //     .attr('title', t('full_screen'))
75621                 //     .attr('tabindex', -1)
75622                 //     .on('click', fullScreen)
75623                 //     .call(tooltip);
75624
75625                 // button.append('span')
75626                 //     .attr('class', 'icon full-screen');
75627
75628                 var detected = utilDetect();
75629                 var keys = (detected.os === 'mac' ? [uiCmd('⌃⌘F'), 'f11'] : ['f11']);
75630                 context.keybinding().on(keys, fullScreen);
75631             };
75632         }
75633
75634         function uiGeolocate(context) {
75635             var _geolocationOptions = {
75636                 // prioritize speed and power usage over precision
75637                 enableHighAccuracy: false,
75638                 // don't hang indefinitely getting the location
75639                 timeout: 6000 // 6sec
75640             };
75641             var _locating = uiLoading(context).message(_t('geolocate.locating')).blocking(true);
75642             var _layer = context.layers().layer('geolocate');
75643             var _position;
75644             var _extent;
75645             var _timeoutID;
75646             var _button = select(null);
75647
75648             function click() {
75649                 if (context.inIntro()) return;
75650                 if (!_layer.enabled() && !_locating.isShown()) {
75651
75652                     // This timeout ensures that we still call finish() even if
75653                     // the user declines to share their location in Firefox
75654                     _timeoutID = setTimeout(error, 10000 /* 10sec */ );
75655
75656                     context.container().call(_locating);
75657                     // get the latest position even if we already have one
75658                     navigator.geolocation.getCurrentPosition(success, error, _geolocationOptions);
75659                 } else {
75660                     _locating.close();
75661                     _layer.enabled(null, false);
75662                     updateButtonState();
75663                 }
75664             }
75665
75666             function zoomTo() {
75667                 context.enter(modeBrowse(context));
75668
75669                 var map = context.map();
75670                 _layer.enabled(_position, true);
75671                 updateButtonState();
75672                 map.centerZoomEase(_extent.center(), Math.min(20, map.extentZoom(_extent)));
75673             }
75674
75675             function success(geolocation) {
75676                 _position = geolocation;
75677                 var coords = _position.coords;
75678                 _extent = geoExtent([coords.longitude, coords.latitude]).padByMeters(coords.accuracy);
75679                 zoomTo();
75680                 finish();
75681             }
75682
75683             function error() {
75684                 if (_position) {
75685                     // use the position from a previous call if we have one
75686                     zoomTo();
75687                 } else {
75688                     context.ui().flash
75689                         .text(_t('geolocate.location_unavailable'))
75690                         .iconName('#iD-icon-geolocate')();
75691                 }
75692
75693                 finish();
75694             }
75695
75696             function finish() {
75697                 _locating.close();  // unblock ui
75698                 if (_timeoutID) { clearTimeout(_timeoutID); }
75699                 _timeoutID = undefined;
75700             }
75701
75702             function updateButtonState() {
75703                 _button.classed('active', _layer.enabled());
75704             }
75705
75706             return function(selection) {
75707                 if (!navigator.geolocation || !navigator.geolocation.getCurrentPosition) return;
75708
75709                 _button = selection
75710                     .append('button')
75711                     .on('click', click)
75712                     .call(svgIcon('#iD-icon-geolocate', 'light'))
75713                     .call(uiTooltip()
75714                         .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
75715                         .title(_t('geolocate.title'))
75716                         .keys([_t('geolocate.key')])
75717                     );
75718
75719                 context.keybinding().on(_t('geolocate.key'), click);
75720             };
75721         }
75722
75723         function uiPanelBackground(context) {
75724             var background = context.background();
75725             var currSourceName = null;
75726             var metadata = {};
75727             var metadataKeys = [
75728                 'zoom', 'vintage', 'source', 'description', 'resolution', 'accuracy'
75729             ];
75730
75731             var debouncedRedraw = debounce(redraw, 250);
75732
75733             function redraw(selection) {
75734                 var source = background.baseLayerSource();
75735                 if (!source) return;
75736
75737                 var isDG = (source.id.match(/^DigitalGlobe/i) !== null);
75738
75739                 if (currSourceName !== source.name()) {
75740                     currSourceName = source.name();
75741                     metadata = {};
75742                 }
75743
75744                 selection.html('');
75745
75746                 var list = selection
75747                     .append('ul')
75748                     .attr('class', 'background-info');
75749
75750                 list
75751                     .append('li')
75752                     .text(currSourceName);
75753
75754                 metadataKeys.forEach(function(k) {
75755                     // DigitalGlobe vintage is available in raster layers for now.
75756                     if (isDG && k === 'vintage') return;
75757
75758                     list
75759                         .append('li')
75760                         .attr('class', 'background-info-list-' + k)
75761                         .classed('hide', !metadata[k])
75762                         .text(_t('info_panels.background.' + k) + ':')
75763                         .append('span')
75764                         .attr('class', 'background-info-span-' + k)
75765                         .text(metadata[k]);
75766                 });
75767
75768                 debouncedGetMetadata(selection);
75769
75770                 var toggleTiles = context.getDebug('tile') ? 'hide_tiles' : 'show_tiles';
75771
75772                 selection
75773                     .append('a')
75774                     .text(_t('info_panels.background.' + toggleTiles))
75775                     .attr('href', '#')
75776                     .attr('class', 'button button-toggle-tiles')
75777                     .on('click', function() {
75778                         event.preventDefault();
75779                         context.setDebug('tile', !context.getDebug('tile'));
75780                         selection.call(redraw);
75781                     });
75782
75783                 if (isDG) {
75784                     var key = source.id + '-vintage';
75785                     var sourceVintage = context.background().findSource(key);
75786                     var showsVintage = context.background().showsLayer(sourceVintage);
75787                     var toggleVintage = showsVintage ? 'hide_vintage' : 'show_vintage';
75788                     selection
75789                         .append('a')
75790                         .text(_t('info_panels.background.' + toggleVintage))
75791                         .attr('href', '#')
75792                         .attr('class', 'button button-toggle-vintage')
75793                         .on('click', function() {
75794                             event.preventDefault();
75795                             context.background().toggleOverlayLayer(sourceVintage);
75796                             selection.call(redraw);
75797                         });
75798                 }
75799
75800                 // disable if necessary
75801                 ['DigitalGlobe-Premium', 'DigitalGlobe-Standard'].forEach(function(layerId) {
75802                     if (source.id !== layerId) {
75803                         var key = layerId + '-vintage';
75804                         var sourceVintage = context.background().findSource(key);
75805                         if (context.background().showsLayer(sourceVintage)) {
75806                             context.background().toggleOverlayLayer(sourceVintage);
75807                         }
75808                     }
75809                 });
75810             }
75811
75812
75813             var debouncedGetMetadata = debounce(getMetadata, 250);
75814
75815             function getMetadata(selection) {
75816                 var tile = context.container().select('.layer-background img.tile-center');   // tile near viewport center
75817                 if (tile.empty()) return;
75818
75819                 var sourceName = currSourceName;
75820                 var d = tile.datum();
75821                 var zoom = (d && d.length >= 3 && d[2]) || Math.floor(context.map().zoom());
75822                 var center = context.map().center();
75823
75824                 // update zoom
75825                 metadata.zoom = String(zoom);
75826                 selection.selectAll('.background-info-list-zoom')
75827                     .classed('hide', false)
75828                     .selectAll('.background-info-span-zoom')
75829                     .text(metadata.zoom);
75830
75831                 if (!d || !d.length >= 3) return;
75832
75833                 background.baseLayerSource().getMetadata(center, d, function(err, result) {
75834                     if (err || currSourceName !== sourceName) return;
75835
75836                     // update vintage
75837                     var vintage = result.vintage;
75838                     metadata.vintage = (vintage && vintage.range) || _t('info_panels.background.unknown');
75839                     selection.selectAll('.background-info-list-vintage')
75840                         .classed('hide', false)
75841                         .selectAll('.background-info-span-vintage')
75842                         .text(metadata.vintage);
75843
75844                     // update other metdata
75845                     metadataKeys.forEach(function(k) {
75846                         if (k === 'zoom' || k === 'vintage') return;  // done already
75847                         var val = result[k];
75848                         metadata[k] = val;
75849                         selection.selectAll('.background-info-list-' + k)
75850                             .classed('hide', !val)
75851                             .selectAll('.background-info-span-' + k)
75852                             .text(val);
75853                     });
75854                 });
75855             }
75856
75857
75858             var panel = function(selection) {
75859                 selection.call(redraw);
75860
75861                 context.map()
75862                     .on('drawn.info-background', function() {
75863                         selection.call(debouncedRedraw);
75864                     })
75865                     .on('move.info-background', function() {
75866                         selection.call(debouncedGetMetadata);
75867                     });
75868
75869             };
75870
75871             panel.off = function() {
75872                 context.map()
75873                     .on('drawn.info-background', null)
75874                     .on('move.info-background', null);
75875             };
75876
75877             panel.id = 'background';
75878             panel.title = _t('info_panels.background.title');
75879             panel.key = _t('info_panels.background.key');
75880
75881
75882             return panel;
75883         }
75884
75885         function uiPanelHistory(context) {
75886             var osm;
75887
75888             function displayTimestamp(timestamp) {
75889                 if (!timestamp) return _t('info_panels.history.unknown');
75890                 var options = {
75891                     day: 'numeric', month: 'short', year: 'numeric',
75892                     hour: 'numeric', minute: 'numeric', second: 'numeric'
75893                 };
75894                 var d = new Date(timestamp);
75895                 if (isNaN(d.getTime())) return _t('info_panels.history.unknown');
75896                 return d.toLocaleString(_mainLocalizer.localeCode(), options);
75897             }
75898
75899
75900             function displayUser(selection, userName) {
75901                 if (!userName) {
75902                     selection
75903                         .append('span')
75904                         .text(_t('info_panels.history.unknown'));
75905                     return;
75906                 }
75907
75908                 selection
75909                     .append('span')
75910                     .attr('class', 'user-name')
75911                     .text(userName);
75912
75913                 var links = selection
75914                     .append('div')
75915                     .attr('class', 'links');
75916
75917                 if (osm) {
75918                     links
75919                         .append('a')
75920                         .attr('class', 'user-osm-link')
75921                         .attr('href', osm.userURL(userName))
75922                         .attr('target', '_blank')
75923                         .attr('tabindex', -1)
75924                         .text('OSM');
75925                 }
75926
75927                 links
75928                     .append('a')
75929                     .attr('class', 'user-hdyc-link')
75930                     .attr('href', 'https://hdyc.neis-one.org/?' + userName)
75931                     .attr('target', '_blank')
75932                     .attr('tabindex', -1)
75933                     .text('HDYC');
75934             }
75935
75936
75937             function displayChangeset(selection, changeset) {
75938                 if (!changeset) {
75939                     selection
75940                         .append('span')
75941                         .text(_t('info_panels.history.unknown'));
75942                     return;
75943                 }
75944
75945                 selection
75946                     .append('span')
75947                     .attr('class', 'changeset-id')
75948                     .text(changeset);
75949
75950                 var links = selection
75951                     .append('div')
75952                     .attr('class', 'links');
75953
75954                 if (osm) {
75955                     links
75956                         .append('a')
75957                         .attr('class', 'changeset-osm-link')
75958                         .attr('href', osm.changesetURL(changeset))
75959                         .attr('target', '_blank')
75960                         .attr('tabindex', -1)
75961                         .text('OSM');
75962                 }
75963
75964                 links
75965                     .append('a')
75966                     .attr('class', 'changeset-osmcha-link')
75967                     .attr('href', 'https://osmcha.org/changesets/' + changeset)
75968                     .attr('target', '_blank')
75969                     .attr('tabindex', -1)
75970                     .text('OSMCha');
75971
75972                 links
75973                     .append('a')
75974                     .attr('class', 'changeset-achavi-link')
75975                     .attr('href', 'https://overpass-api.de/achavi/?changeset=' + changeset)
75976                     .attr('target', '_blank')
75977                     .attr('tabindex', -1)
75978                     .text('Achavi');
75979             }
75980
75981
75982             function redraw(selection) {
75983                 var selectedNoteID = context.selectedNoteID();
75984                 osm = context.connection();
75985
75986                 var selected, note, entity;
75987                 if (selectedNoteID && osm) {       // selected 1 note
75988                     selected = [ _t('note.note') + ' ' + selectedNoteID ];
75989                     note = osm.getNote(selectedNoteID);
75990                 } else {                           // selected 1..n entities
75991                     selected = context.selectedIDs()
75992                         .filter(function(e) { return context.hasEntity(e); });
75993                     if (selected.length) {
75994                         entity = context.entity(selected[0]);
75995                     }
75996                 }
75997
75998                 var singular = selected.length === 1 ? selected[0] : null;
75999
76000                 selection.html('');
76001
76002                 selection
76003                     .append('h4')
76004                     .attr('class', 'history-heading')
76005                     .text(singular || _t('info_panels.history.selected', { n: selected.length }));
76006
76007                 if (!singular) return;
76008
76009                 if (entity) {
76010                     selection.call(redrawEntity, entity);
76011                 } else if (note) {
76012                     selection.call(redrawNote, note);
76013                 }
76014             }
76015
76016
76017             function redrawNote(selection, note) {
76018                 if (!note || note.isNew()) {
76019                     selection
76020                         .append('div')
76021                         .text(_t('info_panels.history.note_no_history'));
76022                     return;
76023                 }
76024
76025                 var list = selection
76026                     .append('ul');
76027
76028                 list
76029                     .append('li')
76030                     .text(_t('info_panels.history.note_comments') + ':')
76031                     .append('span')
76032                     .text(note.comments.length);
76033
76034                 if (note.comments.length) {
76035                     list
76036                         .append('li')
76037                         .text(_t('info_panels.history.note_created_date') + ':')
76038                         .append('span')
76039                         .text(displayTimestamp(note.comments[0].date));
76040
76041                     list
76042                         .append('li')
76043                         .text(_t('info_panels.history.note_created_user') + ':')
76044                         .call(displayUser, note.comments[0].user);
76045                 }
76046
76047                 if (osm) {
76048                     selection
76049                         .append('a')
76050                         .attr('class', 'view-history-on-osm')
76051                         .attr('target', '_blank')
76052                         .attr('tabindex', -1)
76053                         .attr('href', osm.noteURL(note))
76054                         .call(svgIcon('#iD-icon-out-link', 'inline'))
76055                         .append('span')
76056                         .text(_t('info_panels.history.note_link_text'));
76057                 }
76058             }
76059
76060
76061             function redrawEntity(selection, entity) {
76062                 if (!entity || entity.isNew()) {
76063                     selection
76064                         .append('div')
76065                         .text(_t('info_panels.history.no_history'));
76066                     return;
76067                 }
76068
76069                 var links = selection
76070                     .append('div')
76071                     .attr('class', 'links');
76072
76073                 if (osm) {
76074                     links
76075                         .append('a')
76076                         .attr('class', 'view-history-on-osm')
76077                         .attr('href', osm.historyURL(entity))
76078                         .attr('target', '_blank')
76079                         .attr('tabindex', -1)
76080                         .attr('title', _t('info_panels.history.link_text'))
76081                         .text('OSM');
76082                 }
76083                 links
76084                     .append('a')
76085                     .attr('class', 'pewu-history-viewer-link')
76086                     .attr('href', 'https://pewu.github.io/osm-history/#/' + entity.type + '/' + entity.osmId())
76087                     .attr('target', '_blank')
76088                     .attr('tabindex', -1)
76089                     .text('PeWu');
76090
76091                 var list = selection
76092                     .append('ul');
76093
76094                 list
76095                     .append('li')
76096                     .text(_t('info_panels.history.version') + ':')
76097                     .append('span')
76098                     .text(entity.version);
76099
76100                 list
76101                     .append('li')
76102                     .text(_t('info_panels.history.last_edit') + ':')
76103                     .append('span')
76104                     .text(displayTimestamp(entity.timestamp));
76105
76106                 list
76107                     .append('li')
76108                     .text(_t('info_panels.history.edited_by') + ':')
76109                     .call(displayUser, entity.user);
76110
76111                 list
76112                     .append('li')
76113                     .text(_t('info_panels.history.changeset') + ':')
76114                     .call(displayChangeset, entity.changeset);
76115             }
76116
76117
76118             var panel = function(selection) {
76119                 selection.call(redraw);
76120
76121                 context.map()
76122                     .on('drawn.info-history', function() {
76123                         selection.call(redraw);
76124                     });
76125
76126                 context
76127                     .on('enter.info-history', function() {
76128                         selection.call(redraw);
76129                     });
76130             };
76131
76132             panel.off = function() {
76133                 context.map().on('drawn.info-history', null);
76134                 context.on('enter.info-history', null);
76135             };
76136
76137             panel.id = 'history';
76138             panel.title = _t('info_panels.history.title');
76139             panel.key = _t('info_panels.history.key');
76140
76141
76142             return panel;
76143         }
76144
76145         var OSM_PRECISION = 7;
76146
76147         /**
76148          * Returns a localized representation of the given length measurement.
76149          *
76150          * @param {Number} m area in meters
76151          * @param {Boolean} isImperial true for U.S. customary units; false for metric
76152          */
76153         function displayLength(m, isImperial) {
76154             var d = m * (isImperial ? 3.28084 : 1);
76155             var unit;
76156
76157             if (isImperial) {
76158                 if (d >= 5280) {
76159                     d /= 5280;
76160                     unit = 'miles';
76161                 } else {
76162                     unit = 'feet';
76163                 }
76164             } else {
76165                 if (d >= 1000) {
76166                     d /= 1000;
76167                     unit = 'kilometers';
76168                 } else {
76169                     unit = 'meters';
76170                 }
76171             }
76172
76173             return _t('units.' + unit, {
76174                 quantity: d.toLocaleString(_mainLocalizer.localeCode(), {
76175                     maximumSignificantDigits: 4
76176                 })
76177             });
76178         }
76179
76180         /**
76181          * Returns a localized representation of the given area measurement.
76182          *
76183          * @param {Number} m2 area in square meters
76184          * @param {Boolean} isImperial true for U.S. customary units; false for metric
76185          */
76186         function displayArea(m2, isImperial) {
76187             var locale = _mainLocalizer.localeCode();
76188             var d = m2 * (isImperial ? 10.7639111056 : 1);
76189             var d1, d2, area;
76190             var unit1 = '';
76191             var unit2 = '';
76192
76193             if (isImperial) {
76194                 if (d >= 6969600) { // > 0.25mi² show mi²
76195                     d1 = d / 27878400;
76196                     unit1 = 'square_miles';
76197                 } else {
76198                     d1 = d;
76199                     unit1 = 'square_feet';
76200                 }
76201
76202                 if (d > 4356 && d < 43560000) { // 0.1 - 1000 acres
76203                     d2 = d / 43560;
76204                     unit2 = 'acres';
76205                 }
76206
76207             } else {
76208                 if (d >= 250000) { // > 0.25km² show km²
76209                     d1 = d / 1000000;
76210                     unit1 = 'square_kilometers';
76211                 } else {
76212                     d1 = d;
76213                     unit1 = 'square_meters';
76214                 }
76215
76216                 if (d > 1000 && d < 10000000) { // 0.1 - 1000 hectares
76217                     d2 = d / 10000;
76218                     unit2 = 'hectares';
76219                 }
76220             }
76221
76222             area = _t('units.' + unit1, {
76223                 quantity: d1.toLocaleString(locale, {
76224                     maximumSignificantDigits: 4
76225                 })
76226             });
76227
76228             if (d2) {
76229                 return _t('units.area_pair', {
76230                     area1: area,
76231                     area2: _t('units.' + unit2, {
76232                         quantity: d2.toLocaleString(locale, {
76233                             maximumSignificantDigits: 2
76234                         })
76235                     })
76236                 });
76237             } else {
76238                 return area;
76239             }
76240         }
76241
76242         function wrap(x, min, max) {
76243             var d = max - min;
76244             return ((x - min) % d + d) % d + min;
76245         }
76246
76247         function clamp$1(x, min, max) {
76248             return Math.max(min, Math.min(x, max));
76249         }
76250
76251         function displayCoordinate(deg, pos, neg) {
76252             var locale = _mainLocalizer.localeCode();
76253             var min = (Math.abs(deg) - Math.floor(Math.abs(deg))) * 60;
76254             var sec = (min - Math.floor(min)) * 60;
76255             var displayDegrees = _t('units.arcdegrees', {
76256                 quantity: Math.floor(Math.abs(deg)).toLocaleString(locale)
76257             });
76258             var displayCoordinate;
76259
76260             if (Math.floor(sec) > 0) {
76261                 displayCoordinate = displayDegrees +
76262                     _t('units.arcminutes', {
76263                         quantity: Math.floor(min).toLocaleString(locale)
76264                     }) +
76265                     _t('units.arcseconds', {
76266                         quantity: Math.round(sec).toLocaleString(locale)
76267                     });
76268             } else if (Math.floor(min) > 0) {
76269                 displayCoordinate = displayDegrees +
76270                     _t('units.arcminutes', {
76271                         quantity: Math.round(min).toLocaleString(locale)
76272                     });
76273             } else {
76274                 displayCoordinate = _t('units.arcdegrees', {
76275                     quantity: Math.round(Math.abs(deg)).toLocaleString(locale)
76276                 });
76277             }
76278
76279             if (deg === 0) {
76280                 return displayCoordinate;
76281             } else {
76282                 return _t('units.coordinate', {
76283                     coordinate: displayCoordinate,
76284                     direction: _t('units.' + (deg > 0 ? pos : neg))
76285                 });
76286             }
76287         }
76288
76289         /**
76290          * Returns given coordinate pair in degree-minute-second format.
76291          *
76292          * @param {Array<Number>} coord longitude and latitude
76293          */
76294         function dmsCoordinatePair(coord) {
76295             return _t('units.coordinate_pair', {
76296                 latitude: displayCoordinate(clamp$1(coord[1], -90, 90), 'north', 'south'),
76297                 longitude: displayCoordinate(wrap(coord[0], -180, 180), 'east', 'west')
76298             });
76299         }
76300
76301         /**
76302          * Returns the given coordinate pair in decimal format.
76303          * note: unlocalized to avoid comma ambiguity - see #4765
76304          *
76305          * @param {Array<Number>} coord longitude and latitude
76306          */
76307         function decimalCoordinatePair(coord) {
76308             return _t('units.coordinate_pair', {
76309                 latitude: clamp$1(coord[1], -90, 90).toFixed(OSM_PRECISION),
76310                 longitude: wrap(coord[0], -180, 180).toFixed(OSM_PRECISION)
76311             });
76312         }
76313
76314         function uiPanelLocation(context) {
76315             var currLocation = '';
76316
76317
76318             function redraw(selection) {
76319                 selection.html('');
76320
76321                 var list = selection
76322                     .append('ul');
76323
76324                 // Mouse coordinates
76325                 var coord = context.map().mouseCoordinates();
76326                 if (coord.some(isNaN)) {
76327                     coord = context.map().center();
76328                 }
76329
76330                 list
76331                     .append('li')
76332                     .text(dmsCoordinatePair(coord))
76333                     .append('li')
76334                     .text(decimalCoordinatePair(coord));
76335
76336                 // Location Info
76337                 selection
76338                     .append('div')
76339                     .attr('class', 'location-info')
76340                     .text(currLocation || ' ');
76341
76342                 debouncedGetLocation(selection, coord);
76343             }
76344
76345
76346             var debouncedGetLocation = debounce(getLocation, 250);
76347             function getLocation(selection, coord) {
76348                 if (!services.geocoder) {
76349                     currLocation = _t('info_panels.location.unknown_location');
76350                     selection.selectAll('.location-info')
76351                         .text(currLocation);
76352                 } else {
76353                     services.geocoder.reverse(coord, function(err, result) {
76354                         currLocation = result ? result.display_name : _t('info_panels.location.unknown_location');
76355                         selection.selectAll('.location-info')
76356                             .text(currLocation);
76357                     });
76358                 }
76359             }
76360
76361
76362             var panel = function(selection) {
76363                 selection.call(redraw);
76364
76365                 context.surface()
76366                     .on(('PointerEvent' in window ? 'pointer' : 'mouse') + 'move.info-location', function() {
76367                         selection.call(redraw);
76368                     });
76369             };
76370
76371             panel.off = function() {
76372                 context.surface()
76373                     .on('.info-location', null);
76374             };
76375
76376             panel.id = 'location';
76377             panel.title = _t('info_panels.location.title');
76378             panel.key = _t('info_panels.location.key');
76379
76380
76381             return panel;
76382         }
76383
76384         function uiPanelMeasurement(context) {
76385             var locale = _mainLocalizer.localeCode();
76386             var isImperial = !_mainLocalizer.usesMetric();
76387
76388
76389             function radiansToMeters(r) {
76390                 // using WGS84 authalic radius (6371007.1809 m)
76391                 return r * 6371007.1809;
76392             }
76393
76394             function steradiansToSqmeters(r) {
76395                 // http://gis.stackexchange.com/a/124857/40446
76396                 return r / (4 * Math.PI) * 510065621724000;
76397             }
76398
76399
76400             function toLineString(feature) {
76401                 if (feature.type === 'LineString') return feature;
76402
76403                 var result = { type: 'LineString', coordinates: [] };
76404                 if (feature.type === 'Polygon') {
76405                     result.coordinates = feature.coordinates[0];
76406                 } else if (feature.type === 'MultiPolygon') {
76407                     result.coordinates = feature.coordinates[0][0];
76408                 }
76409
76410                 return result;
76411             }
76412
76413
76414             function redraw(selection) {
76415                 var graph = context.graph();
76416                 var selectedNoteID = context.selectedNoteID();
76417                 var osm = services.osm;
76418
76419                 var heading;
76420                 var center, location, centroid;
76421                 var closed, geometry;
76422                 var totalNodeCount, length = 0, area = 0;
76423
76424                 if (selectedNoteID && osm) {       // selected 1 note
76425
76426                     var note = osm.getNote(selectedNoteID);
76427                     heading = _t('note.note') + ' ' + selectedNoteID;
76428                     location = note.loc;
76429                     geometry = 'note';
76430
76431                 } else {                           // selected 1..n entities
76432                     var selectedIDs = context.selectedIDs().filter(function(id) {
76433                         return context.hasEntity(id);
76434                     });
76435                     var selected = selectedIDs.map(function(id) {
76436                         return context.entity(id);
76437                     });
76438
76439                     heading = selected.length === 1 ? selected[0].id :
76440                         _t('info_panels.measurement.selected', { n: selected.length.toLocaleString(locale) });
76441
76442                     if (selected.length) {
76443                         var extent = geoExtent();
76444                         for (var i in selected) {
76445                             var entity = selected[i];
76446                             extent._extend(entity.extent(graph));
76447
76448                             geometry = entity.geometry(graph);
76449                             if (geometry === 'line' || geometry === 'area') {
76450                                 closed = (entity.type === 'relation') || (entity.isClosed() && !entity.isDegenerate());
76451                                 var feature = entity.asGeoJSON(graph);
76452                                 length += radiansToMeters(d3_geoLength(toLineString(feature)));
76453                                 centroid = d3_geoCentroid(feature);
76454                                 if (closed) {
76455                                     area += steradiansToSqmeters(entity.area(graph));
76456                                 }
76457                             }
76458                         }
76459
76460                         if (selected.length > 1) {
76461                             geometry = null;
76462                             closed = null;
76463                             centroid = null;
76464                         }
76465
76466                         if (selected.length === 1 && selected[0].type === 'node') {
76467                             location = selected[0].loc;
76468                         } else {
76469                             totalNodeCount = utilGetAllNodes(selectedIDs, context.graph()).length;
76470                         }
76471
76472                         if (!location && !centroid) {
76473                             center = extent.center();
76474                         }
76475                     }
76476                 }
76477
76478                 selection.html('');
76479
76480                 if (heading) {
76481                     selection
76482                         .append('h4')
76483                         .attr('class', 'measurement-heading')
76484                         .text(heading);
76485                 }
76486
76487                 var list = selection
76488                     .append('ul');
76489                 var coordItem;
76490
76491                 if (geometry) {
76492                     list
76493                         .append('li')
76494                         .text(_t('info_panels.measurement.geometry') + ':')
76495                         .append('span')
76496                         .text(
76497                             closed ? _t('info_panels.measurement.closed_' + geometry) : _t('geometry.' + geometry)
76498                         );
76499                 }
76500
76501                 if (totalNodeCount) {
76502                     list
76503                         .append('li')
76504                         .text(_t('info_panels.measurement.node_count') + ':')
76505                         .append('span')
76506                         .text(totalNodeCount.toLocaleString(locale));
76507                 }
76508
76509                 if (area) {
76510                     list
76511                         .append('li')
76512                         .text(_t('info_panels.measurement.area') + ':')
76513                         .append('span')
76514                         .text(displayArea(area, isImperial));
76515                 }
76516
76517                 if (length) {
76518                     var lengthLabel = _t('info_panels.measurement.' + (closed ? 'perimeter' : 'length'));
76519                     list
76520                         .append('li')
76521                         .text(lengthLabel + ':')
76522                         .append('span')
76523                         .text(displayLength(length, isImperial));
76524                 }
76525
76526                 if (location) {
76527                     coordItem = list
76528                         .append('li')
76529                         .text(_t('info_panels.measurement.location') + ':');
76530                     coordItem.append('span')
76531                         .text(dmsCoordinatePair(location));
76532                     coordItem.append('span')
76533                         .text(decimalCoordinatePair(location));
76534                 }
76535
76536                 if (centroid) {
76537                     coordItem = list
76538                         .append('li')
76539                         .text(_t('info_panels.measurement.centroid') + ':');
76540                     coordItem.append('span')
76541                         .text(dmsCoordinatePair(centroid));
76542                     coordItem.append('span')
76543                         .text(decimalCoordinatePair(centroid));
76544                 }
76545
76546                 if (center) {
76547                     coordItem = list
76548                         .append('li')
76549                         .text(_t('info_panels.measurement.center') + ':');
76550                     coordItem.append('span')
76551                         .text(dmsCoordinatePair(center));
76552                     coordItem.append('span')
76553                         .text(decimalCoordinatePair(center));
76554                 }
76555
76556                 if (length || area) {
76557                     var toggle  = isImperial ? 'imperial' : 'metric';
76558                     selection
76559                         .append('a')
76560                         .text(_t('info_panels.measurement.' + toggle))
76561                         .attr('href', '#')
76562                         .attr('class', 'button button-toggle-units')
76563                         .on('click', function() {
76564                             event.preventDefault();
76565                             isImperial = !isImperial;
76566                             selection.call(redraw);
76567                         });
76568                 }
76569             }
76570
76571
76572             var panel = function(selection) {
76573                 selection.call(redraw);
76574
76575                 context.map()
76576                     .on('drawn.info-measurement', function() {
76577                         selection.call(redraw);
76578                     });
76579
76580                 context
76581                     .on('enter.info-measurement', function() {
76582                         selection.call(redraw);
76583                     });
76584             };
76585
76586             panel.off = function() {
76587                 context.map().on('drawn.info-measurement', null);
76588                 context.on('enter.info-measurement', null);
76589             };
76590
76591             panel.id = 'measurement';
76592             panel.title = _t('info_panels.measurement.title');
76593             panel.key = _t('info_panels.measurement.key');
76594
76595
76596             return panel;
76597         }
76598
76599         var uiInfoPanels = {
76600             background: uiPanelBackground,
76601             history: uiPanelHistory,
76602             location: uiPanelLocation,
76603             measurement: uiPanelMeasurement,
76604         };
76605
76606         function uiInfo(context) {
76607             var ids = Object.keys(uiInfoPanels);
76608             var wasActive = ['measurement'];
76609             var panels = {};
76610             var active = {};
76611
76612             // create panels
76613             ids.forEach(function(k) {
76614                 if (!panels[k]) {
76615                     panels[k] = uiInfoPanels[k](context);
76616                     active[k] = false;
76617                 }
76618             });
76619
76620
76621             function info(selection) {
76622
76623                 function redraw() {
76624                     var activeids = ids.filter(function(k) { return active[k]; }).sort();
76625
76626                     var containers = infoPanels.selectAll('.panel-container')
76627                         .data(activeids, function(k) { return k; });
76628
76629                     containers.exit()
76630                         .style('opacity', 1)
76631                         .transition()
76632                         .duration(200)
76633                         .style('opacity', 0)
76634                         .on('end', function(d) {
76635                             select(this)
76636                                 .call(panels[d].off)
76637                                 .remove();
76638                         });
76639
76640                     var enter = containers.enter()
76641                         .append('div')
76642                         .attr('class', function(d) { return 'fillD2 panel-container panel-container-' + d; });
76643
76644                     enter
76645                         .style('opacity', 0)
76646                         .transition()
76647                         .duration(200)
76648                         .style('opacity', 1);
76649
76650                     var title = enter
76651                         .append('div')
76652                         .attr('class', 'panel-title fillD2');
76653
76654                     title
76655                         .append('h3')
76656                         .text(function(d) { return panels[d].title; });
76657
76658                     title
76659                         .append('button')
76660                         .attr('class', 'close')
76661                         .on('click', function (d) { info.toggle(d); })
76662                         .call(svgIcon('#iD-icon-close'));
76663
76664                     enter
76665                         .append('div')
76666                         .attr('class', function(d) { return 'panel-content panel-content-' + d; });
76667
76668
76669                     // redraw the panels
76670                     infoPanels.selectAll('.panel-content')
76671                         .each(function(d) {
76672                             select(this).call(panels[d]);
76673                         });
76674                 }
76675
76676
76677                 info.toggle = function(which) {
76678                     if (event) {
76679                         event.stopImmediatePropagation();
76680                         event.preventDefault();
76681                     }
76682
76683                     var activeids = ids.filter(function(k) { return active[k]; });
76684
76685                     if (which) {  // toggle one
76686                         active[which] = !active[which];
76687                         if (activeids.length === 1 && activeids[0] === which) {  // none active anymore
76688                             wasActive = [which];
76689                         }
76690
76691                         context.container().select('.' + which + '-panel-toggle-item')
76692                             .classed('active', active[which])
76693                             .select('input')
76694                             .property('checked', active[which]);
76695
76696                     } else {      // toggle all
76697                         if (activeids.length) {
76698                             wasActive = activeids;
76699                             activeids.forEach(function(k) { active[k] = false; });
76700                         } else {
76701                             wasActive.forEach(function(k) { active[k] = true; });
76702                         }
76703                     }
76704
76705                     redraw();
76706                 };
76707
76708
76709                 var infoPanels = selection.selectAll('.info-panels')
76710                     .data([0]);
76711
76712                 infoPanels = infoPanels.enter()
76713                     .append('div')
76714                     .attr('class', 'info-panels')
76715                     .merge(infoPanels);
76716
76717                 redraw();
76718
76719                 context.keybinding()
76720                     .on(uiCmd('⌘' + _t('info_panels.key')), info.toggle);
76721
76722                 ids.forEach(function(k) {
76723                     var key = _t('info_panels.' + k + '.key', { default: null });
76724                     if (!key) return;
76725                     context.keybinding()
76726                         .on(uiCmd('⌘⇧' + key), function() { info.toggle(k); });
76727                 });
76728             }
76729
76730             return info;
76731         }
76732
76733         function pointBox(loc, context) {
76734             var rect = context.surfaceRect();
76735             var point = context.curtainProjection(loc);
76736             return {
76737                 left: point[0] + rect.left - 40,
76738                 top: point[1] + rect.top - 60,
76739                 width: 80,
76740                 height: 90
76741             };
76742         }
76743
76744
76745         function pad(locOrBox, padding, context) {
76746             var box;
76747             if (locOrBox instanceof Array) {
76748                 var rect = context.surfaceRect();
76749                 var point = context.curtainProjection(locOrBox);
76750                 box = {
76751                     left: point[0] + rect.left,
76752                     top: point[1] + rect.top
76753                 };
76754             } else {
76755                 box = locOrBox;
76756             }
76757
76758             return {
76759                 left: box.left - padding,
76760                 top: box.top - padding,
76761                 width: (box.width || 0) + 2 * padding,
76762                 height: (box.width || 0) + 2 * padding
76763             };
76764         }
76765
76766
76767         function icon(name, svgklass, useklass) {
76768             return '<svg class="icon ' + (svgklass || '') + '">' +
76769                  '<use xlink:href="' + name + '"' +
76770                  (useklass ? ' class="' + useklass + '"' : '') + '></use></svg>';
76771         }
76772
76773         var helpStringReplacements;
76774
76775         // Returns the localized string for `id` with a standardized set of icon, key, and
76776         // label replacements suitable for tutorials and documentation. Optionally supplemented
76777         // with custom `replacements`
76778         function helpString(id, replacements) {
76779             // only load these the first time
76780             if (!helpStringReplacements) helpStringReplacements = {
76781                 // insert icons corresponding to various UI elements
76782                 point_icon: icon('#iD-icon-point', 'pre-text'),
76783                 line_icon: icon('#iD-icon-line', 'pre-text'),
76784                 area_icon: icon('#iD-icon-area', 'pre-text'),
76785                 note_icon: icon('#iD-icon-note', 'pre-text add-note'),
76786                 plus: icon('#iD-icon-plus', 'pre-text'),
76787                 minus: icon('#iD-icon-minus', 'pre-text'),
76788                 move_icon: icon('#iD-operation-move', 'pre-text operation'),
76789                 merge_icon: icon('#iD-operation-merge', 'pre-text operation'),
76790                 delete_icon: icon('#iD-operation-delete', 'pre-text operation'),
76791                 circularize_icon: icon('#iD-operation-circularize', 'pre-text operation'),
76792                 split_icon: icon('#iD-operation-split', 'pre-text operation'),
76793                 orthogonalize_icon: icon('#iD-operation-orthogonalize', 'pre-text operation'),
76794                 disconnect_icon: icon('#iD-operation-disconnect', 'pre-text operation'),
76795                 layers_icon: icon('#iD-icon-layers', 'pre-text'),
76796                 data_icon: icon('#iD-icon-data', 'pre-text'),
76797                 inspect: icon('#iD-icon-inspect', 'pre-text'),
76798                 help_icon: icon('#iD-icon-help', 'pre-text'),
76799                 undo_icon: icon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-redo' : '#iD-icon-undo', 'pre-text'),
76800                 redo_icon: icon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-undo' : '#iD-icon-redo', 'pre-text'),
76801                 save_icon: icon('#iD-icon-save', 'pre-text'),
76802                 leftclick: icon('#iD-walkthrough-mouse-left', 'pre-text operation'),
76803                 rightclick: icon('#iD-walkthrough-mouse-right', 'pre-text operation'),
76804                 mousewheel_icon: icon('#iD-walkthrough-mousewheel', 'pre-text operation'),
76805                 tap_icon: icon('#iD-walkthrough-tap', 'pre-text operation'),
76806                 doubletap_icon: icon('#iD-walkthrough-doubletap', 'pre-text operation'),
76807                 longpress_icon: icon('#iD-walkthrough-longpress', 'pre-text operation'),
76808                 touchdrag_icon: icon('#iD-walkthrough-touchdrag', 'pre-text operation'),
76809                 pinch_icon: icon('#iD-walkthrough-pinch-apart', 'pre-text operation'),
76810
76811                 // insert keys; may be localized and platform-dependent
76812                 shift: uiCmd.display('⇧'),
76813                 alt: uiCmd.display('⌥'),
76814                 return: uiCmd.display('↵'),
76815                 esc: _t('shortcuts.key.esc'),
76816                 space: _t('shortcuts.key.space'),
76817                 add_note_key: _t('modes.add_note.key'),
76818                 help_key: _t('help.key'),
76819                 shortcuts_key: _t('shortcuts.toggle.key'),
76820
76821                 // reference localized UI labels directly so that they'll always match
76822                 save: _t('save.title'),
76823                 undo: _t('undo.title'),
76824                 redo: _t('redo.title'),
76825                 upload: _t('commit.save'),
76826                 point: _t('modes.add_point.title'),
76827                 line: _t('modes.add_line.title'),
76828                 area: _t('modes.add_area.title'),
76829                 note: _t('modes.add_note.title'),
76830                 delete: _t('operations.delete.title'),
76831                 move: _t('operations.move.title'),
76832                 orthogonalize: _t('operations.orthogonalize.title'),
76833                 circularize: _t('operations.circularize.title'),
76834                 merge: _t('operations.merge.title'),
76835                 disconnect: _t('operations.disconnect.title'),
76836                 split: _t('operations.split.title'),
76837                 map_data: _t('map_data.title'),
76838                 osm_notes: _t('map_data.layers.notes.title'),
76839                 fields: _t('inspector.fields'),
76840                 tags: _t('inspector.tags'),
76841                 relations: _t('inspector.relations'),
76842                 new_relation: _t('inspector.new_relation'),
76843                 turn_restrictions: _t('presets.fields.restrictions.label'),
76844                 background_settings: _t('background.description'),
76845                 imagery_offset: _t('background.fix_misalignment'),
76846                 start_the_walkthrough: _t('splash.walkthrough'),
76847                 help: _t('help.title'),
76848                 ok: _t('intro.ok')
76849             };
76850
76851             var reps;
76852             if (replacements) {
76853                 reps = Object.assign(replacements, helpStringReplacements);
76854             } else {
76855                 reps = helpStringReplacements;
76856             }
76857
76858             return _t(id, reps)
76859                  // use keyboard key styling for shortcuts
76860                 .replace(/\`(.*?)\`/g, '<kbd>$1</kbd>');
76861         }
76862
76863
76864         function slugify(text) {
76865             return text.toString().toLowerCase()
76866                 .replace(/\s+/g, '-')           // Replace spaces with -
76867                 .replace(/[^\w\-]+/g, '')       // Remove all non-word chars
76868                 .replace(/\-\-+/g, '-')         // Replace multiple - with single -
76869                 .replace(/^-+/, '')             // Trim - from start of text
76870                 .replace(/-+$/, '');            // Trim - from end of text
76871         }
76872
76873
76874         // console warning for missing walkthrough names
76875         var missingStrings = {};
76876         function checkKey(key, text) {
76877             if (_t(key, { default: undefined}) === undefined) {
76878                 if (missingStrings.hasOwnProperty(key)) return;  // warn once
76879                 missingStrings[key] = text;
76880                 var missing = key + ': ' + text;
76881                 if (typeof console !== 'undefined') console.log(missing); // eslint-disable-line
76882             }
76883         }
76884
76885
76886         function localize(obj) {
76887             var key;
76888
76889             // Assign name if entity has one..
76890             var name = obj.tags && obj.tags.name;
76891             if (name) {
76892                 key = 'intro.graph.name.' + slugify(name);
76893                 obj.tags.name = _t(key, { default: name });
76894                 checkKey(key, name);
76895             }
76896
76897             // Assign street name if entity has one..
76898             var street = obj.tags && obj.tags['addr:street'];
76899             if (street) {
76900                 key = 'intro.graph.name.' + slugify(street);
76901                 obj.tags['addr:street'] = _t(key, { default: street });
76902                 checkKey(key, street);
76903
76904                 // Add address details common across walkthrough..
76905                 var addrTags = [
76906                     'block_number', 'city', 'county', 'district', 'hamlet', 'neighbourhood',
76907                     'postcode', 'province', 'quarter', 'state', 'subdistrict', 'suburb'
76908                 ];
76909                 addrTags.forEach(function(k) {
76910                     var key = 'intro.graph.' + k;
76911                     var tag = 'addr:' + k;
76912                     var val = obj.tags && obj.tags[tag];
76913                     var str = _t(key, { default: val });
76914
76915                     if (str) {
76916                         if (str.match(/^<.*>$/) !== null) {
76917                             delete obj.tags[tag];
76918                         } else {
76919                             obj.tags[tag] = str;
76920                         }
76921                     }
76922                 });
76923             }
76924
76925             return obj;
76926         }
76927
76928
76929         // Used to detect squareness.. some duplicataion of code from actionOrthogonalize.
76930         function isMostlySquare(points) {
76931             // note: uses 15 here instead of the 12 from actionOrthogonalize because
76932             // actionOrthogonalize can actually straighten some larger angles as it iterates
76933             var threshold = 15; // degrees within right or straight
76934             var lowerBound = Math.cos((90 - threshold) * Math.PI / 180);  // near right
76935             var upperBound = Math.cos(threshold * Math.PI / 180);         // near straight
76936
76937             for (var i = 0; i < points.length; i++) {
76938                 var a = points[(i - 1 + points.length) % points.length];
76939                 var origin = points[i];
76940                 var b = points[(i + 1) % points.length];
76941
76942                 var dotp = geoVecNormalizedDot(a, b, origin);
76943                 var mag = Math.abs(dotp);
76944                 if (mag > lowerBound && mag < upperBound) {
76945                     return false;
76946                 }
76947             }
76948
76949             return true;
76950         }
76951
76952
76953         function selectMenuItem(context, operation) {
76954             return context.container().select('.edit-menu .edit-menu-item-' + operation);
76955         }
76956
76957
76958         function transitionTime(point1, point2) {
76959             var distance = geoSphericalDistance(point1, point2);
76960             if (distance === 0)
76961                 return 0;
76962             else if (distance < 80)
76963                 return 500;
76964             else
76965                 return 1000;
76966         }
76967
76968         // Tooltips and svg mask used to highlight certain features
76969         function uiCurtain(containerNode) {
76970
76971             var surface = select(null),
76972                 tooltip = select(null),
76973                 darkness = select(null);
76974
76975             function curtain(selection) {
76976                 surface = selection
76977                     .append('svg')
76978                     .attr('class', 'curtain')
76979                     .style('top', 0)
76980                     .style('left', 0);
76981
76982                 darkness = surface.append('path')
76983                     .attr('x', 0)
76984                     .attr('y', 0)
76985                     .attr('class', 'curtain-darkness');
76986
76987                 select(window).on('resize.curtain', resize);
76988
76989                 tooltip = selection.append('div')
76990                     .attr('class', 'tooltip');
76991
76992                 tooltip
76993                     .append('div')
76994                     .attr('class', 'popover-arrow');
76995
76996                 tooltip
76997                     .append('div')
76998                     .attr('class', 'popover-inner');
76999
77000                 resize();
77001
77002
77003                 function resize() {
77004                     surface
77005                         .attr('width', containerNode.clientWidth)
77006                         .attr('height', containerNode.clientHeight);
77007                     curtain.cut(darkness.datum());
77008                 }
77009             }
77010
77011
77012             /**
77013              * Reveal cuts the curtain to highlight the given box,
77014              * and shows a tooltip with instructions next to the box.
77015              *
77016              * @param  {String|ClientRect} [box]   box used to cut the curtain
77017              * @param  {String}    [text]          text for a tooltip
77018              * @param  {Object}    [options]
77019              * @param  {string}    [options.tooltipClass]    optional class to add to the tooltip
77020              * @param  {integer}   [options.duration]        transition time in milliseconds
77021              * @param  {string}    [options.buttonText]      if set, create a button with this text label
77022              * @param  {function}  [options.buttonCallback]  if set, the callback for the button
77023              * @param  {function}  [options.padding]         extra margin in px to put around bbox
77024              * @param  {String|ClientRect} [options.tooltipBox]  box for tooltip position, if different from box for the curtain
77025              */
77026             curtain.reveal = function(box, text, options) {
77027                 options = options || {};
77028
77029                 if (typeof box === 'string') {
77030                     box = select(box).node();
77031                 }
77032                 if (box && box.getBoundingClientRect) {
77033                     box = copyBox(box.getBoundingClientRect());
77034                     var containerRect = containerNode.getBoundingClientRect();
77035                     box.top -= containerRect.top;
77036                     box.left -= containerRect.left;
77037                 }
77038                 if (box && options.padding) {
77039                     box.top -= options.padding;
77040                     box.left -= options.padding;
77041                     box.bottom += options.padding;
77042                     box.right += options.padding;
77043                     box.height += options.padding * 2;
77044                     box.width += options.padding * 2;
77045                 }
77046
77047                 var tooltipBox;
77048                 if (options.tooltipBox) {
77049                     tooltipBox = options.tooltipBox;
77050                     if (typeof tooltipBox === 'string') {
77051                         tooltipBox = select(tooltipBox).node();
77052                     }
77053                     if (tooltipBox && tooltipBox.getBoundingClientRect) {
77054                         tooltipBox = copyBox(tooltipBox.getBoundingClientRect());
77055                     }
77056                 } else {
77057                     tooltipBox = box;
77058                 }
77059
77060                 if (tooltipBox && text) {
77061                     // pseudo markdown bold text for the instruction section..
77062                     var parts = text.split('**');
77063                     var html = parts[0] ? '<span>' + parts[0] + '</span>' : '';
77064                     if (parts[1]) {
77065                         html += '<span class="instruction">' + parts[1] + '</span>';
77066                     }
77067
77068                     html = html.replace(/\*(.*?)\*/g, '<em>$1</em>');   // emphasis
77069                     html = html.replace(/\{br\}/g, '<br/><br/>');       // linebreak
77070
77071                     if (options.buttonText && options.buttonCallback) {
77072                         html += '<div class="button-section">' +
77073                             '<button href="#" class="button action">' + options.buttonText + '</button></div>';
77074                     }
77075
77076                     var classes = 'curtain-tooltip popover tooltip arrowed in ' + (options.tooltipClass || '');
77077                     tooltip
77078                         .classed(classes, true)
77079                         .selectAll('.popover-inner')
77080                         .html(html);
77081
77082                     if (options.buttonText && options.buttonCallback) {
77083                         var button = tooltip.selectAll('.button-section .button.action');
77084                         button
77085                             .on('click', function() {
77086                                 event.preventDefault();
77087                                 options.buttonCallback();
77088                             });
77089                     }
77090
77091                     var tip = copyBox(tooltip.node().getBoundingClientRect()),
77092                         w = containerNode.clientWidth,
77093                         h = containerNode.clientHeight,
77094                         tooltipWidth = 200,
77095                         tooltipArrow = 5,
77096                         side, pos;
77097
77098
77099                     // hack: this will have bottom placement,
77100                     // so need to reserve extra space for the tooltip illustration.
77101                     if (options.tooltipClass === 'intro-mouse') {
77102                         tip.height += 80;
77103                     }
77104
77105                     // trim box dimensions to just the portion that fits in the container..
77106                     if (tooltipBox.top + tooltipBox.height > h) {
77107                         tooltipBox.height -= (tooltipBox.top + tooltipBox.height - h);
77108                     }
77109                     if (tooltipBox.left + tooltipBox.width > w) {
77110                         tooltipBox.width -= (tooltipBox.left + tooltipBox.width - w);
77111                     }
77112
77113                     // determine tooltip placement..
77114
77115                     if (tooltipBox.top + tooltipBox.height < 100) {
77116                         // tooltip below box..
77117                         side = 'bottom';
77118                         pos = [
77119                             tooltipBox.left + tooltipBox.width / 2 - tip.width / 2,
77120                             tooltipBox.top + tooltipBox.height
77121                         ];
77122
77123                     } else if (tooltipBox.top > h - 140) {
77124                         // tooltip above box..
77125                         side = 'top';
77126                         pos = [
77127                             tooltipBox.left + tooltipBox.width / 2 - tip.width / 2,
77128                             tooltipBox.top - tip.height
77129                         ];
77130
77131                     } else {
77132                         // tooltip to the side of the tooltipBox..
77133                         var tipY = tooltipBox.top + tooltipBox.height / 2 - tip.height / 2;
77134
77135                         if (_mainLocalizer.textDirection() === 'rtl') {
77136                             if (tooltipBox.left - tooltipWidth - tooltipArrow < 70) {
77137                                 side = 'right';
77138                                 pos = [tooltipBox.left + tooltipBox.width + tooltipArrow, tipY];
77139
77140                             } else {
77141                                 side = 'left';
77142                                 pos = [tooltipBox.left - tooltipWidth - tooltipArrow, tipY];
77143                             }
77144
77145                         } else {
77146                             if (tooltipBox.left + tooltipBox.width + tooltipArrow + tooltipWidth > w - 70) {
77147                                 side = 'left';
77148                                 pos = [tooltipBox.left - tooltipWidth - tooltipArrow, tipY];
77149                             }
77150                             else {
77151                                 side = 'right';
77152                                 pos = [tooltipBox.left + tooltipBox.width + tooltipArrow, tipY];
77153                             }
77154                         }
77155                     }
77156
77157                     if (options.duration !== 0 || !tooltip.classed(side)) {
77158                         tooltip.call(uiToggle(true));
77159                     }
77160
77161                     tooltip
77162                         .style('top', pos[1] + 'px')
77163                         .style('left', pos[0] + 'px')
77164                         .attr('class', classes + ' ' + side);
77165
77166
77167                     // shift popover-inner if it is very close to the top or bottom edge
77168                     // (doesn't affect the placement of the popover-arrow)
77169                     var shiftY = 0;
77170                     if (side === 'left' || side === 'right') {
77171                         if (pos[1] < 60) {
77172                             shiftY = 60 - pos[1];
77173                         }
77174                         else if (pos[1] + tip.height > h - 100) {
77175                             shiftY = h - pos[1] - tip.height - 100;
77176                         }
77177                     }
77178                     tooltip.selectAll('.popover-inner')
77179                         .style('top', shiftY + 'px');
77180
77181                 } else {
77182                     tooltip
77183                         .classed('in', false)
77184                         .call(uiToggle(false));
77185                 }
77186
77187                 curtain.cut(box, options.duration);
77188
77189                 return tooltip;
77190             };
77191
77192
77193             curtain.cut = function(datum, duration) {
77194                 darkness.datum(datum)
77195                     .interrupt();
77196
77197                 var selection;
77198                 if (duration === 0) {
77199                     selection = darkness;
77200                 } else {
77201                     selection = darkness
77202                         .transition()
77203                         .duration(duration || 600)
77204                         .ease(linear$1);
77205                 }
77206
77207                 selection
77208                     .attr('d', function(d) {
77209                         var containerWidth = containerNode.clientWidth;
77210                         var containerHeight = containerNode.clientHeight;
77211                         var string = 'M 0,0 L 0,' + containerHeight + ' L ' +
77212                             containerWidth + ',' + containerHeight + 'L' +
77213                             containerWidth + ',0 Z';
77214
77215                         if (!d) return string;
77216                         return string + 'M' +
77217                             d.left + ',' + d.top + 'L' +
77218                             d.left + ',' + (d.top + d.height) + 'L' +
77219                             (d.left + d.width) + ',' + (d.top + d.height) + 'L' +
77220                             (d.left + d.width) + ',' + (d.top) + 'Z';
77221
77222                     });
77223             };
77224
77225
77226             curtain.remove = function() {
77227                 surface.remove();
77228                 tooltip.remove();
77229                 select(window).on('resize.curtain', null);
77230             };
77231
77232
77233             // ClientRects are immutable, so copy them to an object,
77234             // in case we need to trim the height/width.
77235             function copyBox(src) {
77236                 return {
77237                     top: src.top,
77238                     right: src.right,
77239                     bottom: src.bottom,
77240                     left: src.left,
77241                     width: src.width,
77242                     height: src.height
77243                 };
77244             }
77245
77246
77247             return curtain;
77248         }
77249
77250         function uiIntroWelcome(context, reveal) {
77251             var dispatch$1 = dispatch('done');
77252
77253             var chapter = {
77254                 title: 'intro.welcome.title'
77255             };
77256
77257
77258             function welcome() {
77259                 context.map().centerZoom([-85.63591, 41.94285], 19);
77260                 reveal('.intro-nav-wrap .chapter-welcome',
77261                     helpString('intro.welcome.welcome'),
77262                     { buttonText: _t('intro.ok'), buttonCallback: practice }
77263                 );
77264             }
77265
77266             function practice() {
77267                 reveal('.intro-nav-wrap .chapter-welcome',
77268                     helpString('intro.welcome.practice'),
77269                     { buttonText: _t('intro.ok'), buttonCallback: words }
77270                 );
77271             }
77272
77273             function words() {
77274                 reveal('.intro-nav-wrap .chapter-welcome',
77275                     helpString('intro.welcome.words'),
77276                     { buttonText: _t('intro.ok'), buttonCallback: chapters }
77277                 );
77278             }
77279
77280
77281             function chapters() {
77282                 dispatch$1.call('done');
77283                 reveal('.intro-nav-wrap .chapter-navigation',
77284                     helpString('intro.welcome.chapters', { next: _t('intro.navigation.title') })
77285                 );
77286             }
77287
77288
77289             chapter.enter = function() {
77290                 welcome();
77291             };
77292
77293
77294             chapter.exit = function() {
77295                 context.container().select('.curtain-tooltip.intro-mouse')
77296                     .selectAll('.counter')
77297                     .remove();
77298             };
77299
77300
77301             chapter.restart = function() {
77302                 chapter.exit();
77303                 chapter.enter();
77304             };
77305
77306
77307             return utilRebind(chapter, dispatch$1, 'on');
77308         }
77309
77310         function uiIntroNavigation(context, reveal) {
77311             var dispatch$1 = dispatch('done');
77312             var timeouts = [];
77313             var hallId = 'n2061';
77314             var townHall = [-85.63591, 41.94285];
77315             var springStreetId = 'w397';
77316             var springStreetEndId = 'n1834';
77317             var springStreet = [-85.63582, 41.94255];
77318             var onewayField = _mainPresetIndex.field('oneway');
77319             var maxspeedField = _mainPresetIndex.field('maxspeed');
77320
77321
77322             var chapter = {
77323                 title: 'intro.navigation.title'
77324             };
77325
77326
77327             function timeout(f, t) {
77328                 timeouts.push(window.setTimeout(f, t));
77329             }
77330
77331
77332             function eventCancel() {
77333                 event.stopPropagation();
77334                 event.preventDefault();
77335             }
77336
77337
77338             function isTownHallSelected() {
77339                 var ids = context.selectedIDs();
77340                 return ids.length === 1 && ids[0] === hallId;
77341             }
77342
77343
77344             function dragMap() {
77345                 context.enter(modeBrowse(context));
77346                 context.history().reset('initial');
77347
77348                 var msec = transitionTime(townHall, context.map().center());
77349                 if (msec) { reveal(null, null, { duration: 0 }); }
77350                 context.map().centerZoomEase(townHall, 19, msec);
77351
77352                 timeout(function() {
77353                     var centerStart = context.map().center();
77354
77355                     var textId = context.lastPointerType() === 'mouse' ? 'drag' : 'drag_touch';
77356                     var dragString = helpString('intro.navigation.map_info') + '{br}' + helpString('intro.navigation.' + textId);
77357                     reveal('.surface', dragString);
77358                     context.map().on('drawn.intro', function() {
77359                         reveal('.surface', dragString, { duration: 0 });
77360                     });
77361
77362                     context.map().on('move.intro', function() {
77363                         var centerNow = context.map().center();
77364                         if (centerStart[0] !== centerNow[0] || centerStart[1] !== centerNow[1]) {
77365                             context.map().on('move.intro', null);
77366                             timeout(function() { continueTo(zoomMap); }, 3000);
77367                         }
77368                     });
77369
77370                 }, msec + 100);
77371
77372                 function continueTo(nextStep) {
77373                     context.map().on('move.intro drawn.intro', null);
77374                     nextStep();
77375                 }
77376             }
77377
77378
77379             function zoomMap() {
77380                 var zoomStart = context.map().zoom();
77381
77382                 var textId = context.lastPointerType() === 'mouse' ? 'zoom' : 'zoom_touch';
77383                 var zoomString = helpString('intro.navigation.' + textId);
77384
77385                 reveal('.surface', zoomString);
77386
77387                 context.map().on('drawn.intro', function() {
77388                     reveal('.surface', zoomString, { duration: 0 });
77389                 });
77390
77391                 context.map().on('move.intro', function() {
77392                     if (context.map().zoom() !== zoomStart) {
77393                         context.map().on('move.intro', null);
77394                         timeout(function() { continueTo(features); }, 3000);
77395                     }
77396                 });
77397
77398                 function continueTo(nextStep) {
77399                     context.map().on('move.intro drawn.intro', null);
77400                     nextStep();
77401                 }
77402             }
77403
77404
77405             function features() {
77406                 var onClick = function() { continueTo(pointsLinesAreas); };
77407
77408                 reveal('.surface', helpString('intro.navigation.features'),
77409                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77410                 );
77411
77412                 context.map().on('drawn.intro', function() {
77413                     reveal('.surface', helpString('intro.navigation.features'),
77414                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick }
77415                     );
77416                 });
77417
77418                 function continueTo(nextStep) {
77419                     context.map().on('drawn.intro', null);
77420                     nextStep();
77421                 }
77422             }
77423
77424             function pointsLinesAreas() {
77425                 var onClick = function() { continueTo(nodesWays); };
77426
77427                 reveal('.surface', helpString('intro.navigation.points_lines_areas'),
77428                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77429                 );
77430
77431                 context.map().on('drawn.intro', function() {
77432                     reveal('.surface', helpString('intro.navigation.points_lines_areas'),
77433                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick }
77434                     );
77435                 });
77436
77437                 function continueTo(nextStep) {
77438                     context.map().on('drawn.intro', null);
77439                     nextStep();
77440                 }
77441             }
77442
77443             function nodesWays() {
77444                 var onClick = function() { continueTo(clickTownHall); };
77445
77446                 reveal('.surface', helpString('intro.navigation.nodes_ways'),
77447                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77448                 );
77449
77450                 context.map().on('drawn.intro', function() {
77451                     reveal('.surface', helpString('intro.navigation.nodes_ways'),
77452                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick }
77453                     );
77454                 });
77455
77456                 function continueTo(nextStep) {
77457                     context.map().on('drawn.intro', null);
77458                     nextStep();
77459                 }
77460             }
77461
77462             function clickTownHall() {
77463                 context.enter(modeBrowse(context));
77464                 context.history().reset('initial');
77465
77466                 var entity = context.hasEntity(hallId);
77467                 if (!entity) return;
77468                 reveal(null, null, { duration: 0 });
77469                 context.map().centerZoomEase(entity.loc, 19, 500);
77470
77471                 timeout(function() {
77472                     var entity = context.hasEntity(hallId);
77473                     if (!entity) return;
77474                     var box = pointBox(entity.loc, context);
77475                     var textId = context.lastPointerType() === 'mouse' ? 'click_townhall' : 'tap_townhall';
77476                     reveal(box, helpString('intro.navigation.' + textId));
77477
77478                     context.map().on('move.intro drawn.intro', function() {
77479                         var entity = context.hasEntity(hallId);
77480                         if (!entity) return;
77481                         var box = pointBox(entity.loc, context);
77482                         reveal(box, helpString('intro.navigation.' + textId), { duration: 0 });
77483                     });
77484
77485                     context.on('enter.intro', function() {
77486                         if (isTownHallSelected()) continueTo(selectedTownHall);
77487                     });
77488
77489                 }, 550);  // after centerZoomEase
77490
77491                 context.history().on('change.intro', function() {
77492                     if (!context.hasEntity(hallId)) {
77493                         continueTo(clickTownHall);
77494                     }
77495                 });
77496
77497                 function continueTo(nextStep) {
77498                     context.on('enter.intro', null);
77499                     context.map().on('move.intro drawn.intro', null);
77500                     context.history().on('change.intro', null);
77501                     nextStep();
77502                 }
77503             }
77504
77505
77506             function selectedTownHall() {
77507                 if (!isTownHallSelected()) return clickTownHall();
77508
77509                 var entity = context.hasEntity(hallId);
77510                 if (!entity) return clickTownHall();
77511
77512                 var box = pointBox(entity.loc, context);
77513                 var onClick = function() { continueTo(editorTownHall); };
77514
77515                 reveal(box, helpString('intro.navigation.selected_townhall'),
77516                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77517                 );
77518
77519                 context.map().on('move.intro drawn.intro', function() {
77520                     var entity = context.hasEntity(hallId);
77521                     if (!entity) return;
77522                     var box = pointBox(entity.loc, context);
77523                     reveal(box, helpString('intro.navigation.selected_townhall'),
77524                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick }
77525                     );
77526                 });
77527
77528                 context.history().on('change.intro', function() {
77529                     if (!context.hasEntity(hallId)) {
77530                         continueTo(clickTownHall);
77531                     }
77532                 });
77533
77534                 function continueTo(nextStep) {
77535                     context.map().on('move.intro drawn.intro', null);
77536                     context.history().on('change.intro', null);
77537                     nextStep();
77538                 }
77539             }
77540
77541
77542             function editorTownHall() {
77543                 if (!isTownHallSelected()) return clickTownHall();
77544
77545                 // disallow scrolling
77546                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
77547
77548                 var onClick = function() { continueTo(presetTownHall); };
77549
77550                 reveal('.entity-editor-pane',
77551                     helpString('intro.navigation.editor_townhall'),
77552                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77553                 );
77554
77555                 context.on('exit.intro', function() {
77556                     continueTo(clickTownHall);
77557                 });
77558
77559                 context.history().on('change.intro', function() {
77560                     if (!context.hasEntity(hallId)) {
77561                         continueTo(clickTownHall);
77562                     }
77563                 });
77564
77565                 function continueTo(nextStep) {
77566                     context.on('exit.intro', null);
77567                     context.history().on('change.intro', null);
77568                     context.container().select('.inspector-wrap').on('wheel.intro', null);
77569                     nextStep();
77570                 }
77571             }
77572
77573
77574             function presetTownHall() {
77575                 if (!isTownHallSelected()) return clickTownHall();
77576
77577                 // reset pane, in case user happened to change it..
77578                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
77579                 // disallow scrolling
77580                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
77581
77582                 // preset match, in case the user happened to change it.
77583                 var entity = context.entity(context.selectedIDs()[0]);
77584                 var preset = _mainPresetIndex.match(entity, context.graph());
77585
77586                 var onClick = function() { continueTo(fieldsTownHall); };
77587
77588                 reveal('.entity-editor-pane .section-feature-type',
77589                     helpString('intro.navigation.preset_townhall', { preset: preset.name() }),
77590                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77591                 );
77592
77593                 context.on('exit.intro', function() {
77594                     continueTo(clickTownHall);
77595                 });
77596
77597                 context.history().on('change.intro', function() {
77598                     if (!context.hasEntity(hallId)) {
77599                         continueTo(clickTownHall);
77600                     }
77601                 });
77602
77603                 function continueTo(nextStep) {
77604                     context.on('exit.intro', null);
77605                     context.history().on('change.intro', null);
77606                     context.container().select('.inspector-wrap').on('wheel.intro', null);
77607                     nextStep();
77608                 }
77609             }
77610
77611
77612             function fieldsTownHall() {
77613                 if (!isTownHallSelected()) return clickTownHall();
77614
77615                 // reset pane, in case user happened to change it..
77616                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
77617                 // disallow scrolling
77618                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
77619
77620                 var onClick = function() { continueTo(closeTownHall); };
77621
77622                 reveal('.entity-editor-pane .section-preset-fields',
77623                     helpString('intro.navigation.fields_townhall'),
77624                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77625                 );
77626
77627                 context.on('exit.intro', function() {
77628                     continueTo(clickTownHall);
77629                 });
77630
77631                 context.history().on('change.intro', function() {
77632                     if (!context.hasEntity(hallId)) {
77633                         continueTo(clickTownHall);
77634                     }
77635                 });
77636
77637                 function continueTo(nextStep) {
77638                     context.on('exit.intro', null);
77639                     context.history().on('change.intro', null);
77640                     context.container().select('.inspector-wrap').on('wheel.intro', null);
77641                     nextStep();
77642                 }
77643             }
77644
77645
77646             function closeTownHall() {
77647                 if (!isTownHallSelected()) return clickTownHall();
77648
77649                 var selector = '.entity-editor-pane button.close svg use';
77650                 var href = select(selector).attr('href') || '#iD-icon-close';
77651
77652                 reveal('.entity-editor-pane',
77653                     helpString('intro.navigation.close_townhall', { button: icon(href, 'pre-text') })
77654                 );
77655
77656                 context.on('exit.intro', function() {
77657                     continueTo(searchStreet);
77658                 });
77659
77660                 context.history().on('change.intro', function() {
77661                     // update the close icon in the tooltip if the user edits something.
77662                     var selector = '.entity-editor-pane button.close svg use';
77663                     var href = select(selector).attr('href') || '#iD-icon-close';
77664
77665                     reveal('.entity-editor-pane',
77666                         helpString('intro.navigation.close_townhall', { button: icon(href, 'pre-text') }),
77667                         { duration: 0 }
77668                     );
77669                 });
77670
77671                 function continueTo(nextStep) {
77672                     context.on('exit.intro', null);
77673                     context.history().on('change.intro', null);
77674                     nextStep();
77675                 }
77676             }
77677
77678
77679             function searchStreet() {
77680                 context.enter(modeBrowse(context));
77681                 context.history().reset('initial');  // ensure spring street exists
77682
77683                 var msec = transitionTime(springStreet, context.map().center());
77684                 if (msec) { reveal(null, null, { duration: 0 }); }
77685                 context.map().centerZoomEase(springStreet, 19, msec);  // ..and user can see it
77686
77687                 timeout(function() {
77688                     reveal('.search-header input',
77689                         helpString('intro.navigation.search_street', { name: _t('intro.graph.name.spring-street') })
77690                     );
77691
77692                     context.container().select('.search-header input')
77693                         .on('keyup.intro', checkSearchResult);
77694                 }, msec + 100);
77695             }
77696
77697
77698             function checkSearchResult() {
77699                 var first = context.container().select('.feature-list-item:nth-child(0n+2)');  // skip "No Results" item
77700                 var firstName = first.select('.entity-name');
77701                 var name = _t('intro.graph.name.spring-street');
77702
77703                 if (!firstName.empty() && firstName.text() === name) {
77704                     reveal(first.node(),
77705                         helpString('intro.navigation.choose_street', { name: name }),
77706                         { duration: 300 }
77707                     );
77708
77709                     context.on('exit.intro', function() {
77710                         continueTo(selectedStreet);
77711                     });
77712
77713                     context.container().select('.search-header input')
77714                         .on('keydown.intro', eventCancel, true)
77715                         .on('keyup.intro', null);
77716                 }
77717
77718                 function continueTo(nextStep) {
77719                     context.on('exit.intro', null);
77720                     context.container().select('.search-header input')
77721                         .on('keydown.intro', null)
77722                         .on('keyup.intro', null);
77723                     nextStep();
77724                 }
77725             }
77726
77727
77728             function selectedStreet() {
77729                 if (!context.hasEntity(springStreetEndId) || !context.hasEntity(springStreetId)) {
77730                     return searchStreet();
77731                 }
77732
77733                 var onClick = function() { continueTo(editorStreet); };
77734                 var entity = context.entity(springStreetEndId);
77735                 var box = pointBox(entity.loc, context);
77736                 box.height = 500;
77737
77738                 reveal(box,
77739                     helpString('intro.navigation.selected_street', { name: _t('intro.graph.name.spring-street') }),
77740                     { duration: 600, buttonText: _t('intro.ok'), buttonCallback: onClick }
77741                 );
77742
77743                 timeout(function() {
77744                     context.map().on('move.intro drawn.intro', function() {
77745                         var entity = context.hasEntity(springStreetEndId);
77746                         if (!entity) return;
77747                         var box = pointBox(entity.loc, context);
77748                         box.height = 500;
77749                         reveal(box,
77750                             helpString('intro.navigation.selected_street', { name: _t('intro.graph.name.spring-street') }),
77751                             { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick }
77752                         );
77753                     });
77754                 }, 600);  // after reveal.
77755
77756                 context.on('enter.intro', function(mode) {
77757                     if (!context.hasEntity(springStreetId)) {
77758                         return continueTo(searchStreet);
77759                     }
77760                     var ids = context.selectedIDs();
77761                     if (mode.id !== 'select' || !ids.length || ids[0] !== springStreetId) {
77762                         // keep Spring Street selected..
77763                         context.enter(modeSelect(context, [springStreetId]));
77764                     }
77765                 });
77766
77767                 context.history().on('change.intro', function() {
77768                     if (!context.hasEntity(springStreetEndId) || !context.hasEntity(springStreetId)) {
77769                         timeout(function() {
77770                             continueTo(searchStreet);
77771                         }, 300);  // after any transition (e.g. if user deleted intersection)
77772                     }
77773                 });
77774
77775                 function continueTo(nextStep) {
77776                     context.map().on('move.intro drawn.intro', null);
77777                     context.on('enter.intro', null);
77778                     context.history().on('change.intro', null);
77779                     nextStep();
77780                 }
77781             }
77782
77783
77784             function editorStreet() {
77785                 var selector = '.entity-editor-pane button.close svg use';
77786                 var href = select(selector).attr('href') || '#iD-icon-close';
77787
77788                 reveal('.entity-editor-pane', helpString('intro.navigation.street_different_fields') + '{br}' +
77789                     helpString('intro.navigation.editor_street', {
77790                         button: icon(href, 'pre-text'),
77791                         field1: onewayField.label(),
77792                         field2: maxspeedField.label()
77793                     }));
77794
77795                 context.on('exit.intro', function() {
77796                     continueTo(play);
77797                 });
77798
77799                 context.history().on('change.intro', function() {
77800                     // update the close icon in the tooltip if the user edits something.
77801                     var selector = '.entity-editor-pane button.close svg use';
77802                     var href = select(selector).attr('href') || '#iD-icon-close';
77803
77804                     reveal('.entity-editor-pane', helpString('intro.navigation.street_different_fields') + '{br}' +
77805                         helpString('intro.navigation.editor_street', {
77806                             button: icon(href, 'pre-text'),
77807                             field1: onewayField.label(),
77808                             field2: maxspeedField.label()
77809                         }), { duration: 0 }
77810                     );
77811                 });
77812
77813                 function continueTo(nextStep) {
77814                     context.on('exit.intro', null);
77815                     context.history().on('change.intro', null);
77816                     nextStep();
77817                 }
77818             }
77819
77820
77821             function play() {
77822                 dispatch$1.call('done');
77823                 reveal('.ideditor',
77824                     helpString('intro.navigation.play', { next: _t('intro.points.title') }), {
77825                         tooltipBox: '.intro-nav-wrap .chapter-point',
77826                         buttonText: _t('intro.ok'),
77827                         buttonCallback: function() { reveal('.ideditor'); }
77828                     }
77829                 );
77830             }
77831
77832
77833             chapter.enter = function() {
77834                 dragMap();
77835             };
77836
77837
77838             chapter.exit = function() {
77839                 timeouts.forEach(window.clearTimeout);
77840                 context.on('enter.intro exit.intro', null);
77841                 context.map().on('move.intro drawn.intro', null);
77842                 context.history().on('change.intro', null);
77843                 context.container().select('.inspector-wrap').on('wheel.intro', null);
77844                 context.container().select('.search-header input').on('keydown.intro keyup.intro', null);
77845             };
77846
77847
77848             chapter.restart = function() {
77849                 chapter.exit();
77850                 chapter.enter();
77851             };
77852
77853
77854             return utilRebind(chapter, dispatch$1, 'on');
77855         }
77856
77857         function uiIntroPoint(context, reveal) {
77858             var dispatch$1 = dispatch('done');
77859             var timeouts = [];
77860             var intersection = [-85.63279, 41.94394];
77861             var building = [-85.632422, 41.944045];
77862             var cafePreset = _mainPresetIndex.item('amenity/cafe');
77863             var _pointID = null;
77864
77865
77866             var chapter = {
77867                 title: 'intro.points.title'
77868             };
77869
77870
77871             function timeout(f, t) {
77872                 timeouts.push(window.setTimeout(f, t));
77873             }
77874
77875
77876             function eventCancel() {
77877                 event.stopPropagation();
77878                 event.preventDefault();
77879             }
77880
77881
77882             function addPoint() {
77883                 context.enter(modeBrowse(context));
77884                 context.history().reset('initial');
77885
77886                 var msec = transitionTime(intersection, context.map().center());
77887                 if (msec) { reveal(null, null, { duration: 0 }); }
77888                 context.map().centerZoomEase(intersection, 19, msec);
77889
77890                 timeout(function() {
77891                     var tooltip = reveal('button.add-point',
77892                         helpString('intro.points.points_info') + '{br}' + helpString('intro.points.add_point'));
77893
77894                     _pointID = null;
77895
77896                     tooltip.selectAll('.popover-inner')
77897                         .insert('svg', 'span')
77898                         .attr('class', 'tooltip-illustration')
77899                         .append('use')
77900                         .attr('xlink:href', '#iD-graphic-points');
77901
77902                     context.on('enter.intro', function(mode) {
77903                         if (mode.id !== 'add-point') return;
77904                         continueTo(placePoint);
77905                     });
77906                 }, msec + 100);
77907
77908                 function continueTo(nextStep) {
77909                     context.on('enter.intro', null);
77910                     nextStep();
77911                 }
77912             }
77913
77914
77915             function placePoint() {
77916                 if (context.mode().id !== 'add-point') {
77917                     return chapter.restart();
77918                 }
77919
77920                 var pointBox = pad(building, 150, context);
77921                 var textId = context.lastPointerType() === 'mouse' ? 'place_point' : 'place_point_touch';
77922                 reveal(pointBox, helpString('intro.points.' + textId));
77923
77924                 context.map().on('move.intro drawn.intro', function() {
77925                     pointBox = pad(building, 150, context);
77926                     reveal(pointBox, helpString('intro.points.' + textId), { duration: 0 });
77927                 });
77928
77929                 context.on('enter.intro', function(mode) {
77930                     if (mode.id !== 'select') return chapter.restart();
77931                     _pointID = context.mode().selectedIDs()[0];
77932                     continueTo(searchPreset);
77933                 });
77934
77935                 function continueTo(nextStep) {
77936                     context.map().on('move.intro drawn.intro', null);
77937                     context.on('enter.intro', null);
77938                     nextStep();
77939                 }
77940             }
77941
77942
77943             function searchPreset() {
77944                 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
77945                     return addPoint();
77946                 }
77947
77948                 // disallow scrolling
77949                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
77950
77951                 context.container().select('.preset-search-input')
77952                     .on('keydown.intro', null)
77953                     .on('keyup.intro', checkPresetSearch);
77954
77955                 reveal('.preset-search-input',
77956                     helpString('intro.points.search_cafe', { preset: cafePreset.name() })
77957                 );
77958
77959                 context.on('enter.intro', function(mode) {
77960                     if (!_pointID || !context.hasEntity(_pointID)) {
77961                         return continueTo(addPoint);
77962                     }
77963
77964                     var ids = context.selectedIDs();
77965                     if (mode.id !== 'select' || !ids.length || ids[0] !== _pointID) {
77966                         // keep the user's point selected..
77967                         context.enter(modeSelect(context, [_pointID]));
77968
77969                         // disallow scrolling
77970                         context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
77971
77972                         context.container().select('.preset-search-input')
77973                             .on('keydown.intro', null)
77974                             .on('keyup.intro', checkPresetSearch);
77975
77976                         reveal('.preset-search-input',
77977                             helpString('intro.points.search_cafe', { preset: cafePreset.name() })
77978                         );
77979
77980                         context.history().on('change.intro', null);
77981                     }
77982                 });
77983
77984
77985                 function checkPresetSearch() {
77986                     var first = context.container().select('.preset-list-item:first-child');
77987
77988                     if (first.classed('preset-amenity-cafe')) {
77989                         context.container().select('.preset-search-input')
77990                             .on('keydown.intro', eventCancel, true)
77991                             .on('keyup.intro', null);
77992
77993                         reveal(first.select('.preset-list-button').node(),
77994                             helpString('intro.points.choose_cafe', { preset: cafePreset.name() }),
77995                             { duration: 300 }
77996                         );
77997
77998                         context.history().on('change.intro', function() {
77999                             continueTo(aboutFeatureEditor);
78000                         });
78001                     }
78002                 }
78003
78004                 function continueTo(nextStep) {
78005                     context.on('enter.intro', null);
78006                     context.history().on('change.intro', null);
78007                     context.container().select('.inspector-wrap').on('wheel.intro', null);
78008                     context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
78009                     nextStep();
78010                 }
78011             }
78012
78013
78014             function aboutFeatureEditor() {
78015                 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
78016                     return addPoint();
78017                 }
78018
78019                 timeout(function() {
78020                     reveal('.entity-editor-pane', helpString('intro.points.feature_editor'), {
78021                         tooltipClass: 'intro-points-describe',
78022                         buttonText: _t('intro.ok'),
78023                         buttonCallback: function() { continueTo(addName); }
78024                     });
78025                 }, 400);
78026
78027                 context.on('exit.intro', function() {
78028                     // if user leaves select mode here, just continue with the tutorial.
78029                     continueTo(reselectPoint);
78030                 });
78031
78032                 function continueTo(nextStep) {
78033                     context.on('exit.intro', null);
78034                     nextStep();
78035                 }
78036             }
78037
78038
78039             function addName() {
78040                 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
78041                     return addPoint();
78042                 }
78043
78044                 // reset pane, in case user happened to change it..
78045                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78046
78047                 var addNameString = helpString('intro.points.fields_info') + '{br}' + helpString('intro.points.add_name');
78048
78049                 timeout(function() {
78050                     // It's possible for the user to add a name in a previous step..
78051                     // If so, don't tell them to add the name in this step.
78052                     // Give them an OK button instead.
78053                     var entity = context.entity(_pointID);
78054                     if (entity.tags.name) {
78055                         var tooltip = reveal('.entity-editor-pane', addNameString, {
78056                             tooltipClass: 'intro-points-describe',
78057                             buttonText: _t('intro.ok'),
78058                             buttonCallback: function() { continueTo(addCloseEditor); }
78059                         });
78060                         tooltip.select('.instruction').style('display', 'none');
78061
78062                     } else {
78063                         reveal('.entity-editor-pane', addNameString,
78064                             { tooltipClass: 'intro-points-describe' }
78065                         );
78066                     }
78067                 }, 400);
78068
78069                 context.history().on('change.intro', function() {
78070                     continueTo(addCloseEditor);
78071                 });
78072
78073                 context.on('exit.intro', function() {
78074                     // if user leaves select mode here, just continue with the tutorial.
78075                     continueTo(reselectPoint);
78076                 });
78077
78078                 function continueTo(nextStep) {
78079                     context.on('exit.intro', null);
78080                     context.history().on('change.intro', null);
78081                     nextStep();
78082                 }
78083             }
78084
78085
78086             function addCloseEditor() {
78087                 // reset pane, in case user happened to change it..
78088                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78089
78090                 var selector = '.entity-editor-pane button.close svg use';
78091                 var href = select(selector).attr('href') || '#iD-icon-close';
78092
78093                 context.on('exit.intro', function() {
78094                     continueTo(reselectPoint);
78095                 });
78096
78097                 reveal('.entity-editor-pane',
78098                     helpString('intro.points.add_close', { button: icon(href, 'pre-text') })
78099                 );
78100
78101                 function continueTo(nextStep) {
78102                     context.on('exit.intro', null);
78103                     nextStep();
78104                 }
78105             }
78106
78107
78108             function reselectPoint() {
78109                 if (!_pointID) return chapter.restart();
78110                 var entity = context.hasEntity(_pointID);
78111                 if (!entity) return chapter.restart();
78112
78113                 // make sure it's still a cafe, in case user somehow changed it..
78114                 var oldPreset = _mainPresetIndex.match(entity, context.graph());
78115                 context.replace(actionChangePreset(_pointID, oldPreset, cafePreset));
78116
78117                 context.enter(modeBrowse(context));
78118
78119                 var msec = transitionTime(entity.loc, context.map().center());
78120                 if (msec) { reveal(null, null, { duration: 0 }); }
78121                 context.map().centerEase(entity.loc, msec);
78122
78123                 timeout(function() {
78124                     var box = pointBox(entity.loc, context);
78125                     reveal(box, helpString('intro.points.reselect'), { duration: 600 });
78126
78127                     timeout(function() {
78128                         context.map().on('move.intro drawn.intro', function() {
78129                             var entity = context.hasEntity(_pointID);
78130                             if (!entity) return chapter.restart();
78131                             var box = pointBox(entity.loc, context);
78132                             reveal(box, helpString('intro.points.reselect'), { duration: 0 });
78133                         });
78134                     }, 600); // after reveal..
78135
78136                     context.on('enter.intro', function(mode) {
78137                         if (mode.id !== 'select') return;
78138                         continueTo(updatePoint);
78139                     });
78140
78141                 }, msec + 100);
78142
78143                 function continueTo(nextStep) {
78144                     context.map().on('move.intro drawn.intro', null);
78145                     context.on('enter.intro', null);
78146                     nextStep();
78147                 }
78148             }
78149
78150
78151             function updatePoint() {
78152                 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
78153                     return continueTo(reselectPoint);
78154                 }
78155
78156                 // reset pane, in case user happened to untag the point..
78157                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78158
78159                 context.on('exit.intro', function() {
78160                     continueTo(reselectPoint);
78161                 });
78162
78163                 context.history().on('change.intro', function() {
78164                     continueTo(updateCloseEditor);
78165                 });
78166
78167                 timeout(function() {
78168                     reveal('.entity-editor-pane', helpString('intro.points.update'),
78169                         { tooltipClass: 'intro-points-describe' }
78170                     );
78171                 }, 400);
78172
78173                 function continueTo(nextStep) {
78174                     context.on('exit.intro', null);
78175                     context.history().on('change.intro', null);
78176                     nextStep();
78177                 }
78178             }
78179
78180
78181             function updateCloseEditor() {
78182                 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
78183                     return continueTo(reselectPoint);
78184                 }
78185
78186                 // reset pane, in case user happened to change it..
78187                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78188
78189                 context.on('exit.intro', function() {
78190                     continueTo(rightClickPoint);
78191                 });
78192
78193                 timeout(function() {
78194                     reveal('.entity-editor-pane',
78195                         helpString('intro.points.update_close', { button: icon('#iD-icon-close', 'pre-text') })
78196                     );
78197                 }, 500);
78198
78199                 function continueTo(nextStep) {
78200                     context.on('exit.intro', null);
78201                     nextStep();
78202                 }
78203             }
78204
78205
78206             function rightClickPoint() {
78207                 if (!_pointID) return chapter.restart();
78208                 var entity = context.hasEntity(_pointID);
78209                 if (!entity) return chapter.restart();
78210
78211                 context.enter(modeBrowse(context));
78212
78213                 var box = pointBox(entity.loc, context);
78214                 var textId = context.lastPointerType() === 'mouse' ? 'rightclick' : 'edit_menu_touch';
78215                 reveal(box, helpString('intro.points.' + textId), { duration: 600 });
78216
78217                 timeout(function() {
78218                     context.map().on('move.intro', function() {
78219                         var entity = context.hasEntity(_pointID);
78220                         if (!entity) return chapter.restart();
78221                         var box = pointBox(entity.loc, context);
78222                         reveal(box, helpString('intro.points.' + textId), { duration: 0 });
78223                     });
78224                 }, 600); // after reveal
78225
78226                 context.on('enter.intro', function(mode) {
78227                     if (mode.id !== 'select') return;
78228                     var ids = context.selectedIDs();
78229                     if (ids.length !== 1 || ids[0] !== _pointID) return;
78230
78231                     timeout(function() {
78232                         var node = selectMenuItem(context, 'delete').node();
78233                         if (!node) return;
78234                         continueTo(enterDelete);
78235                     }, 50);  // after menu visible
78236                 });
78237
78238                 function continueTo(nextStep) {
78239                     context.on('enter.intro', null);
78240                     context.map().on('move.intro', null);
78241                     nextStep();
78242                 }
78243             }
78244
78245
78246             function enterDelete() {
78247                 if (!_pointID) return chapter.restart();
78248                 var entity = context.hasEntity(_pointID);
78249                 if (!entity) return chapter.restart();
78250
78251                 var node = selectMenuItem(context, 'delete').node();
78252                 if (!node) { return continueTo(rightClickPoint); }
78253
78254                 reveal('.edit-menu',
78255                     helpString('intro.points.delete'),
78256                     { padding: 50 }
78257                 );
78258
78259                 timeout(function() {
78260                     context.map().on('move.intro', function() {
78261                         reveal('.edit-menu',
78262                             helpString('intro.points.delete'),
78263                             { duration: 0,  padding: 50 }
78264                         );
78265                     });
78266                 }, 300); // after menu visible
78267
78268                 context.on('exit.intro', function() {
78269                     if (!_pointID) return chapter.restart();
78270                     var entity = context.hasEntity(_pointID);
78271                     if (entity) return continueTo(rightClickPoint);  // point still exists
78272                 });
78273
78274                 context.history().on('change.intro', function(changed) {
78275                     if (changed.deleted().length) {
78276                         continueTo(undo);
78277                     }
78278                 });
78279
78280                 function continueTo(nextStep) {
78281                     context.map().on('move.intro', null);
78282                     context.history().on('change.intro', null);
78283                     context.on('exit.intro', null);
78284                     nextStep();
78285                 }
78286             }
78287
78288
78289             function undo() {
78290                 context.history().on('change.intro', function() {
78291                     continueTo(play);
78292                 });
78293
78294                 reveal('.top-toolbar button.undo-button',
78295                     helpString('intro.points.undo')
78296                 );
78297
78298                 function continueTo(nextStep) {
78299                     context.history().on('change.intro', null);
78300                     nextStep();
78301                 }
78302             }
78303
78304
78305             function play() {
78306                 dispatch$1.call('done');
78307                 reveal('.ideditor',
78308                     helpString('intro.points.play', { next: _t('intro.areas.title') }), {
78309                         tooltipBox: '.intro-nav-wrap .chapter-area',
78310                         buttonText: _t('intro.ok'),
78311                         buttonCallback: function() { reveal('.ideditor'); }
78312                     }
78313                 );
78314             }
78315
78316
78317             chapter.enter = function() {
78318                 addPoint();
78319             };
78320
78321
78322             chapter.exit = function() {
78323                 timeouts.forEach(window.clearTimeout);
78324                 context.on('enter.intro exit.intro', null);
78325                 context.map().on('move.intro drawn.intro', null);
78326                 context.history().on('change.intro', null);
78327                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
78328                 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
78329             };
78330
78331
78332             chapter.restart = function() {
78333                 chapter.exit();
78334                 chapter.enter();
78335             };
78336
78337
78338             return utilRebind(chapter, dispatch$1, 'on');
78339         }
78340
78341         function uiIntroArea(context, reveal) {
78342             var dispatch$1 = dispatch('done');
78343             var playground = [-85.63552, 41.94159];
78344             var playgroundPreset = _mainPresetIndex.item('leisure/playground');
78345             var nameField = _mainPresetIndex.field('name');
78346             var descriptionField = _mainPresetIndex.field('description');
78347             var timeouts = [];
78348             var _areaID;
78349
78350
78351             var chapter = {
78352                 title: 'intro.areas.title'
78353             };
78354
78355
78356             function timeout(f, t) {
78357                 timeouts.push(window.setTimeout(f, t));
78358             }
78359
78360
78361             function eventCancel() {
78362                 event.stopPropagation();
78363                 event.preventDefault();
78364             }
78365
78366
78367             function revealPlayground(center, text, options) {
78368                 var padding = 180 * Math.pow(2, context.map().zoom() - 19.5);
78369                 var box = pad(center, padding, context);
78370                 reveal(box, text, options);
78371             }
78372
78373
78374             function addArea() {
78375                 context.enter(modeBrowse(context));
78376                 context.history().reset('initial');
78377                 _areaID = null;
78378
78379                 var msec = transitionTime(playground, context.map().center());
78380                 if (msec) { reveal(null, null, { duration: 0 }); }
78381                 context.map().centerZoomEase(playground, 19, msec);
78382
78383                 timeout(function() {
78384                     var tooltip = reveal('button.add-area',
78385                         helpString('intro.areas.add_playground'));
78386
78387                     tooltip.selectAll('.popover-inner')
78388                         .insert('svg', 'span')
78389                         .attr('class', 'tooltip-illustration')
78390                         .append('use')
78391                         .attr('xlink:href', '#iD-graphic-areas');
78392
78393                     context.on('enter.intro', function(mode) {
78394                         if (mode.id !== 'add-area') return;
78395                         continueTo(startPlayground);
78396                     });
78397                 }, msec + 100);
78398
78399                 function continueTo(nextStep) {
78400                     context.on('enter.intro', null);
78401                     nextStep();
78402                 }
78403             }
78404
78405
78406             function startPlayground() {
78407                 if (context.mode().id !== 'add-area') {
78408                     return chapter.restart();
78409                 }
78410
78411                 _areaID = null;
78412                 context.map().zoomEase(19.5, 500);
78413
78414                 timeout(function() {
78415                     var textId = context.lastPointerType() === 'mouse' ? 'starting_node_click' : 'starting_node_tap';
78416                     var startDrawString = helpString('intro.areas.start_playground') + helpString('intro.areas.' + textId);
78417                     revealPlayground(playground,
78418                         startDrawString, { duration: 250 }
78419                     );
78420
78421                     timeout(function() {
78422                         context.map().on('move.intro drawn.intro', function() {
78423                             revealPlayground(playground,
78424                                 startDrawString, { duration: 0 }
78425                             );
78426                         });
78427                         context.on('enter.intro', function(mode) {
78428                             if (mode.id !== 'draw-area') return chapter.restart();
78429                             continueTo(continuePlayground);
78430                         });
78431                     }, 250);  // after reveal
78432
78433                 }, 550);  // after easing
78434
78435                 function continueTo(nextStep) {
78436                     context.map().on('move.intro drawn.intro', null);
78437                     context.on('enter.intro', null);
78438                     nextStep();
78439                 }
78440             }
78441
78442
78443             function continuePlayground() {
78444                 if (context.mode().id !== 'draw-area') {
78445                     return chapter.restart();
78446                 }
78447
78448                 _areaID = null;
78449                 revealPlayground(playground,
78450                     helpString('intro.areas.continue_playground'),
78451                     { duration: 250 }
78452                 );
78453
78454                 timeout(function() {
78455                     context.map().on('move.intro drawn.intro', function() {
78456                         revealPlayground(playground,
78457                             helpString('intro.areas.continue_playground'),
78458                             { duration: 0 }
78459                         );
78460                     });
78461                 }, 250);  // after reveal
78462
78463                 context.on('enter.intro', function(mode) {
78464                     if (mode.id === 'draw-area') {
78465                         var entity = context.hasEntity(context.selectedIDs()[0]);
78466                         if (entity && entity.nodes.length >= 6) {
78467                             return continueTo(finishPlayground);
78468                         } else {
78469                             return;
78470                         }
78471                     } else if (mode.id === 'select') {
78472                         _areaID = context.selectedIDs()[0];
78473                         return continueTo(searchPresets);
78474                     } else {
78475                         return chapter.restart();
78476                     }
78477                 });
78478
78479                 function continueTo(nextStep) {
78480                     context.map().on('move.intro drawn.intro', null);
78481                     context.on('enter.intro', null);
78482                     nextStep();
78483                 }
78484             }
78485
78486
78487             function finishPlayground() {
78488                 if (context.mode().id !== 'draw-area') {
78489                     return chapter.restart();
78490                 }
78491
78492                 _areaID = null;
78493
78494                 var finishString = helpString('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) +
78495                     helpString('intro.areas.finish_playground');
78496                 revealPlayground(playground,
78497                     finishString, { duration: 250 }
78498                 );
78499
78500                 timeout(function() {
78501                     context.map().on('move.intro drawn.intro', function() {
78502                         revealPlayground(playground,
78503                             finishString, { duration: 0 }
78504                         );
78505                     });
78506                 }, 250);  // after reveal
78507
78508                 context.on('enter.intro', function(mode) {
78509                     if (mode.id === 'draw-area') {
78510                         return;
78511                     } else if (mode.id === 'select') {
78512                         _areaID = context.selectedIDs()[0];
78513                         return continueTo(searchPresets);
78514                     } else {
78515                         return chapter.restart();
78516                     }
78517                 });
78518
78519                 function continueTo(nextStep) {
78520                     context.map().on('move.intro drawn.intro', null);
78521                     context.on('enter.intro', null);
78522                     nextStep();
78523                 }
78524             }
78525
78526
78527             function searchPresets() {
78528                 if (!_areaID || !context.hasEntity(_areaID)) {
78529                     return addArea();
78530                 }
78531                 var ids = context.selectedIDs();
78532                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
78533                     context.enter(modeSelect(context, [_areaID]));
78534                 }
78535
78536                 // disallow scrolling
78537                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
78538
78539                 timeout(function() {
78540                     // reset pane, in case user somehow happened to change it..
78541                     context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
78542
78543                     context.container().select('.preset-search-input')
78544                         .on('keydown.intro', null)
78545                         .on('keyup.intro', checkPresetSearch);
78546
78547                     reveal('.preset-search-input',
78548                         helpString('intro.areas.search_playground', { preset: playgroundPreset.name() })
78549                     );
78550                 }, 400);  // after preset list pane visible..
78551
78552                 context.on('enter.intro', function(mode) {
78553                     if (!_areaID || !context.hasEntity(_areaID)) {
78554                         return continueTo(addArea);
78555                     }
78556
78557                     var ids = context.selectedIDs();
78558                     if (mode.id !== 'select' || !ids.length || ids[0] !== _areaID) {
78559                         // keep the user's area selected..
78560                         context.enter(modeSelect(context, [_areaID]));
78561
78562                         // reset pane, in case user somehow happened to change it..
78563                         context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
78564                         // disallow scrolling
78565                         context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
78566
78567                         context.container().select('.preset-search-input')
78568                             .on('keydown.intro', null)
78569                             .on('keyup.intro', checkPresetSearch);
78570
78571                         reveal('.preset-search-input',
78572                             helpString('intro.areas.search_playground', { preset: playgroundPreset.name() })
78573                         );
78574
78575                         context.history().on('change.intro', null);
78576                     }
78577                 });
78578
78579                 function checkPresetSearch() {
78580                     var first = context.container().select('.preset-list-item:first-child');
78581
78582                     if (first.classed('preset-leisure-playground')) {
78583                         reveal(first.select('.preset-list-button').node(),
78584                             helpString('intro.areas.choose_playground', { preset: playgroundPreset.name() }),
78585                             { duration: 300 }
78586                         );
78587
78588                         context.container().select('.preset-search-input')
78589                             .on('keydown.intro', eventCancel, true)
78590                             .on('keyup.intro', null);
78591
78592                         context.history().on('change.intro', function() {
78593                             continueTo(clickAddField);
78594                         });
78595                     }
78596                 }
78597
78598                 function continueTo(nextStep) {
78599                     context.container().select('.inspector-wrap').on('wheel.intro', null);
78600                     context.on('enter.intro', null);
78601                     context.history().on('change.intro', null);
78602                     context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
78603                     nextStep();
78604                 }
78605             }
78606
78607
78608             function clickAddField() {
78609                 if (!_areaID || !context.hasEntity(_areaID)) {
78610                     return addArea();
78611                 }
78612                 var ids = context.selectedIDs();
78613                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
78614                     return searchPresets();
78615                 }
78616
78617                 if (!context.container().select('.form-field-description').empty()) {
78618                     return continueTo(describePlayground);
78619                 }
78620
78621                 // disallow scrolling
78622                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
78623
78624                 timeout(function() {
78625                     // reset pane, in case user somehow happened to change it..
78626                     context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78627
78628                     // It's possible for the user to add a description in a previous step..
78629                     // If they did this already, just continue to next step.
78630                     var entity = context.entity(_areaID);
78631                     if (entity.tags.description) {
78632                         return continueTo(play);
78633                     }
78634
78635                     // scroll "Add field" into view
78636                     var box = context.container().select('.more-fields').node().getBoundingClientRect();
78637                     if (box.top > 300) {
78638                         var pane = context.container().select('.entity-editor-pane .inspector-body');
78639                         var start = pane.node().scrollTop;
78640                         var end = start + (box.top - 300);
78641
78642                         pane
78643                             .transition()
78644                             .duration(250)
78645                             .tween('scroll.inspector', function() {
78646                                 var node = this;
78647                                 var i = d3_interpolateNumber(start, end);
78648                                 return function(t) {
78649                                     node.scrollTop = i(t);
78650                                 };
78651                             });
78652                     }
78653
78654                     timeout(function() {
78655                         reveal('.more-fields .combobox-input',
78656                             helpString('intro.areas.add_field', {
78657                                 name: nameField.label(),
78658                                 description: descriptionField.label()
78659                             }),
78660                             { duration: 300 }
78661                         );
78662
78663                         context.container().select('.more-fields .combobox-input')
78664                             .on('click.intro', function() {
78665                                 // Watch for the combobox to appear...
78666                                 var watcher;
78667                                 watcher = window.setInterval(function() {
78668                                     if (!context.container().select('div.combobox').empty()) {
78669                                         window.clearInterval(watcher);
78670                                         continueTo(chooseDescriptionField);
78671                                     }
78672                                 }, 300);
78673                             });
78674                     }, 300);  // after "Add Field" visible
78675
78676                 }, 400);  // after editor pane visible
78677
78678                 context.on('exit.intro', function() {
78679                     return continueTo(searchPresets);
78680                 });
78681
78682                 function continueTo(nextStep) {
78683                     context.container().select('.inspector-wrap').on('wheel.intro', null);
78684                     context.container().select('.more-fields .combobox-input').on('click.intro', null);
78685                     context.on('exit.intro', null);
78686                     nextStep();
78687                 }
78688             }
78689
78690
78691             function chooseDescriptionField() {
78692                 if (!_areaID || !context.hasEntity(_areaID)) {
78693                     return addArea();
78694                 }
78695                 var ids = context.selectedIDs();
78696                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
78697                     return searchPresets();
78698                 }
78699
78700                 if (!context.container().select('.form-field-description').empty()) {
78701                     return continueTo(describePlayground);
78702                 }
78703
78704                 // Make sure combobox is ready..
78705                 if (context.container().select('div.combobox').empty()) {
78706                     return continueTo(clickAddField);
78707                 }
78708                 // Watch for the combobox to go away..
78709                 var watcher;
78710                 watcher = window.setInterval(function() {
78711                     if (context.container().select('div.combobox').empty()) {
78712                         window.clearInterval(watcher);
78713                         timeout(function() {
78714                             if (context.container().select('.form-field-description').empty()) {
78715                                 continueTo(retryChooseDescription);
78716                             } else {
78717                                 continueTo(describePlayground);
78718                             }
78719                         }, 300);  // after description field added.
78720                     }
78721                 }, 300);
78722
78723                 reveal('div.combobox',
78724                     helpString('intro.areas.choose_field', { field: descriptionField.label() }),
78725                     { duration: 300 }
78726                 );
78727
78728                 context.on('exit.intro', function() {
78729                     return continueTo(searchPresets);
78730                 });
78731
78732                 function continueTo(nextStep) {
78733                     if (watcher) window.clearInterval(watcher);
78734                     context.on('exit.intro', null);
78735                     nextStep();
78736                 }
78737             }
78738
78739
78740             function describePlayground() {
78741                 if (!_areaID || !context.hasEntity(_areaID)) {
78742                     return addArea();
78743                 }
78744                 var ids = context.selectedIDs();
78745                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
78746                     return searchPresets();
78747                 }
78748
78749                 // reset pane, in case user happened to change it..
78750                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78751
78752                 if (context.container().select('.form-field-description').empty()) {
78753                     return continueTo(retryChooseDescription);
78754                 }
78755
78756                 context.on('exit.intro', function() {
78757                     continueTo(play);
78758                 });
78759
78760                 reveal('.entity-editor-pane',
78761                     helpString('intro.areas.describe_playground', { button: icon('#iD-icon-close', 'pre-text') }),
78762                     { duration: 300 }
78763                 );
78764
78765                 function continueTo(nextStep) {
78766                     context.on('exit.intro', null);
78767                     nextStep();
78768                 }
78769             }
78770
78771
78772             function retryChooseDescription() {
78773                 if (!_areaID || !context.hasEntity(_areaID)) {
78774                     return addArea();
78775                 }
78776                 var ids = context.selectedIDs();
78777                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
78778                     return searchPresets();
78779                 }
78780
78781                 // reset pane, in case user happened to change it..
78782                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78783
78784                 reveal('.entity-editor-pane',
78785                     helpString('intro.areas.retry_add_field', { field: descriptionField.label() }), {
78786                     buttonText: _t('intro.ok'),
78787                     buttonCallback: function() { continueTo(clickAddField); }
78788                 });
78789
78790                 context.on('exit.intro', function() {
78791                     return continueTo(searchPresets);
78792                 });
78793
78794                 function continueTo(nextStep) {
78795                     context.on('exit.intro', null);
78796                     nextStep();
78797                 }
78798             }
78799
78800
78801             function play() {
78802                 dispatch$1.call('done');
78803                 reveal('.ideditor',
78804                     helpString('intro.areas.play', { next: _t('intro.lines.title') }), {
78805                         tooltipBox: '.intro-nav-wrap .chapter-line',
78806                         buttonText: _t('intro.ok'),
78807                         buttonCallback: function() { reveal('.ideditor'); }
78808                     }
78809                 );
78810             }
78811
78812
78813             chapter.enter = function() {
78814                 addArea();
78815             };
78816
78817
78818             chapter.exit = function() {
78819                 timeouts.forEach(window.clearTimeout);
78820                 context.on('enter.intro exit.intro', null);
78821                 context.map().on('move.intro drawn.intro', null);
78822                 context.history().on('change.intro', null);
78823                 context.container().select('.inspector-wrap').on('wheel.intro', null);
78824                 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
78825                 context.container().select('.more-fields .combobox-input').on('click.intro', null);
78826             };
78827
78828
78829             chapter.restart = function() {
78830                 chapter.exit();
78831                 chapter.enter();
78832             };
78833
78834
78835             return utilRebind(chapter, dispatch$1, 'on');
78836         }
78837
78838         function uiIntroLine(context, reveal) {
78839             var dispatch$1 = dispatch('done');
78840             var timeouts = [];
78841             var _tulipRoadID = null;
78842             var flowerRoadID = 'w646';
78843             var tulipRoadStart = [-85.6297754121684, 41.95805253325314];
78844             var tulipRoadMidpoint = [-85.62975395449628, 41.95787501510204];
78845             var tulipRoadIntersection = [-85.62974496187628, 41.95742515554585];
78846             var roadCategory = _mainPresetIndex.item('category-road_minor');
78847             var residentialPreset = _mainPresetIndex.item('highway/residential');
78848             var woodRoadID = 'w525';
78849             var woodRoadEndID = 'n2862';
78850             var woodRoadAddNode = [-85.62390110349587, 41.95397111462291];
78851             var woodRoadDragEndpoint = [-85.623867390213, 41.95466987786487];
78852             var woodRoadDragMidpoint = [-85.62386254803509, 41.95430395953872];
78853             var washingtonStreetID = 'w522';
78854             var twelfthAvenueID = 'w1';
78855             var eleventhAvenueEndID = 'n3550';
78856             var twelfthAvenueEndID = 'n5';
78857             var _washingtonSegmentID = null;
78858             var eleventhAvenueEnd = context.entity(eleventhAvenueEndID).loc;
78859             var twelfthAvenueEnd = context.entity(twelfthAvenueEndID).loc;
78860             var deleteLinesLoc = [-85.6219395542764, 41.95228033922477];
78861             var twelfthAvenue = [-85.62219310052491, 41.952505413152956];
78862
78863
78864             var chapter = {
78865                 title: 'intro.lines.title'
78866             };
78867
78868
78869             function timeout(f, t) {
78870                 timeouts.push(window.setTimeout(f, t));
78871             }
78872
78873
78874             function eventCancel() {
78875                 event.stopPropagation();
78876                 event.preventDefault();
78877             }
78878
78879
78880             function addLine() {
78881                 context.enter(modeBrowse(context));
78882                 context.history().reset('initial');
78883
78884                 var msec = transitionTime(tulipRoadStart, context.map().center());
78885                 if (msec) { reveal(null, null, { duration: 0 }); }
78886                 context.map().centerZoomEase(tulipRoadStart, 18.5, msec);
78887
78888                 timeout(function() {
78889                     var tooltip = reveal('button.add-line',
78890                         helpString('intro.lines.add_line'));
78891
78892                     tooltip.selectAll('.popover-inner')
78893                         .insert('svg', 'span')
78894                         .attr('class', 'tooltip-illustration')
78895                         .append('use')
78896                         .attr('xlink:href', '#iD-graphic-lines');
78897
78898                     context.on('enter.intro', function(mode) {
78899                         if (mode.id !== 'add-line') return;
78900                         continueTo(startLine);
78901                     });
78902                 }, msec + 100);
78903
78904                 function continueTo(nextStep) {
78905                     context.on('enter.intro', null);
78906                     nextStep();
78907                 }
78908             }
78909
78910
78911             function startLine() {
78912                 if (context.mode().id !== 'add-line') return chapter.restart();
78913
78914                 _tulipRoadID = null;
78915
78916                 var padding = 70 * Math.pow(2, context.map().zoom() - 18);
78917                 var box = pad(tulipRoadStart, padding, context);
78918                 box.height = box.height + 100;
78919
78920                 var textId = context.lastPointerType() === 'mouse' ? 'start_line' : 'start_line_tap';
78921                 var startLineString = helpString('intro.lines.missing_road') + '{br}' +
78922                     helpString('intro.lines.line_draw_info') +
78923                     helpString('intro.lines.' + textId);
78924                 reveal(box, startLineString);
78925
78926                 context.map().on('move.intro drawn.intro', function() {
78927                     padding = 70 * Math.pow(2, context.map().zoom() - 18);
78928                     box = pad(tulipRoadStart, padding, context);
78929                     box.height = box.height + 100;
78930                     reveal(box, startLineString, { duration: 0 });
78931                 });
78932
78933                 context.on('enter.intro', function(mode) {
78934                     if (mode.id !== 'draw-line') return chapter.restart();
78935                     continueTo(drawLine);
78936                 });
78937
78938                 function continueTo(nextStep) {
78939                     context.map().on('move.intro drawn.intro', null);
78940                     context.on('enter.intro', null);
78941                     nextStep();
78942                 }
78943             }
78944
78945
78946             function drawLine() {
78947                 if (context.mode().id !== 'draw-line') return chapter.restart();
78948
78949                 _tulipRoadID = context.mode().selectedIDs()[0];
78950                 context.map().centerEase(tulipRoadMidpoint, 500);
78951
78952                 timeout(function() {
78953                     var padding = 200 * Math.pow(2, context.map().zoom() - 18.5);
78954                     var box = pad(tulipRoadMidpoint, padding, context);
78955                     box.height = box.height * 2;
78956                     reveal(box,
78957                         helpString('intro.lines.intersect', { name: _t('intro.graph.name.flower-street') })
78958                     );
78959
78960                     context.map().on('move.intro drawn.intro', function() {
78961                         padding = 200 * Math.pow(2, context.map().zoom() - 18.5);
78962                         box = pad(tulipRoadMidpoint, padding, context);
78963                         box.height = box.height * 2;
78964                         reveal(box,
78965                             helpString('intro.lines.intersect', { name: _t('intro.graph.name.flower-street') }),
78966                             { duration: 0 }
78967                         );
78968                     });
78969                 }, 550);  // after easing..
78970
78971                 context.history().on('change.intro', function() {
78972                     if (isLineConnected()) {
78973                         continueTo(continueLine);
78974                     }
78975                 });
78976
78977                 context.on('enter.intro', function(mode) {
78978                     if (mode.id === 'draw-line') {
78979                         return;
78980                     } else if (mode.id === 'select') {
78981                         continueTo(retryIntersect);
78982                         return;
78983                     } else {
78984                         return chapter.restart();
78985                     }
78986                 });
78987
78988                 function continueTo(nextStep) {
78989                     context.map().on('move.intro drawn.intro', null);
78990                     context.history().on('change.intro', null);
78991                     context.on('enter.intro', null);
78992                     nextStep();
78993                 }
78994             }
78995
78996
78997             function isLineConnected() {
78998                 var entity = _tulipRoadID && context.hasEntity(_tulipRoadID);
78999                 if (!entity) return false;
79000
79001                 var drawNodes = context.graph().childNodes(entity);
79002                 return drawNodes.some(function(node) {
79003                     return context.graph().parentWays(node).some(function(parent) {
79004                         return parent.id === flowerRoadID;
79005                     });
79006                 });
79007             }
79008
79009
79010             function retryIntersect() {
79011                 select(window).on('pointerdown.intro mousedown.intro', eventCancel, true);
79012
79013                 var box = pad(tulipRoadIntersection, 80, context);
79014                 reveal(box,
79015                     helpString('intro.lines.retry_intersect', { name: _t('intro.graph.name.flower-street') })
79016                 );
79017
79018                 timeout(chapter.restart, 3000);
79019             }
79020
79021
79022             function continueLine() {
79023                 if (context.mode().id !== 'draw-line') return chapter.restart();
79024                 var entity = _tulipRoadID && context.hasEntity(_tulipRoadID);
79025                 if (!entity) return chapter.restart();
79026
79027                 context.map().centerEase(tulipRoadIntersection, 500);
79028
79029                 var continueLineText = helpString('intro.lines.continue_line') + '{br}' +
79030                     helpString('intro.lines.finish_line_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) +
79031                     helpString('intro.lines.finish_road');
79032
79033                 reveal('.surface', continueLineText);
79034
79035                 context.on('enter.intro', function(mode) {
79036                     if (mode.id === 'draw-line')
79037                         return;
79038                     else if (mode.id === 'select')
79039                         return continueTo(chooseCategoryRoad);
79040                     else
79041                         return chapter.restart();
79042                 });
79043
79044                 function continueTo(nextStep) {
79045                     context.on('enter.intro', null);
79046                     nextStep();
79047                 }
79048             }
79049
79050
79051             function chooseCategoryRoad() {
79052                 if (context.mode().id !== 'select') return chapter.restart();
79053
79054                 context.on('exit.intro', function() {
79055                     return chapter.restart();
79056                 });
79057
79058                 var button = context.container().select('.preset-category-road_minor .preset-list-button');
79059                 if (button.empty()) return chapter.restart();
79060
79061                 // disallow scrolling
79062                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
79063
79064                 timeout(function() {
79065                     // reset pane, in case user somehow happened to change it..
79066                     context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
79067
79068                     reveal(button.node(),
79069                         helpString('intro.lines.choose_category_road', { category: roadCategory.name() })
79070                     );
79071
79072                     button.on('click.intro', function() {
79073                         continueTo(choosePresetResidential);
79074                     });
79075
79076                 }, 400);  // after editor pane visible
79077
79078                 function continueTo(nextStep) {
79079                     context.container().select('.inspector-wrap').on('wheel.intro', null);
79080                     context.container().select('.preset-list-button').on('click.intro', null);
79081                     context.on('exit.intro', null);
79082                     nextStep();
79083                 }
79084             }
79085
79086
79087             function choosePresetResidential() {
79088                 if (context.mode().id !== 'select') return chapter.restart();
79089
79090                 context.on('exit.intro', function() {
79091                     return chapter.restart();
79092                 });
79093
79094                 var subgrid = context.container().select('.preset-category-road_minor .subgrid');
79095                 if (subgrid.empty()) return chapter.restart();
79096
79097                 subgrid.selectAll(':not(.preset-highway-residential) .preset-list-button')
79098                     .on('click.intro', function() {
79099                         continueTo(retryPresetResidential);
79100                     });
79101
79102                 subgrid.selectAll('.preset-highway-residential .preset-list-button')
79103                     .on('click.intro', function() {
79104                         continueTo(nameRoad);
79105                     });
79106
79107                 timeout(function() {
79108                     reveal(subgrid.node(),
79109                         helpString('intro.lines.choose_preset_residential', { preset: residentialPreset.name() }),
79110                         { tooltipBox: '.preset-highway-residential .preset-list-button', duration: 300 }
79111                     );
79112                 }, 300);
79113
79114                 function continueTo(nextStep) {
79115                     context.container().select('.preset-list-button').on('click.intro', null);
79116                     context.on('exit.intro', null);
79117                     nextStep();
79118                 }
79119             }
79120
79121
79122             // selected wrong road type
79123             function retryPresetResidential() {
79124                 if (context.mode().id !== 'select') return chapter.restart();
79125
79126                 context.on('exit.intro', function() {
79127                     return chapter.restart();
79128                 });
79129
79130                 // disallow scrolling
79131                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
79132
79133                 timeout(function() {
79134                     var button = context.container().select('.entity-editor-pane .preset-list-button');
79135
79136                     reveal(button.node(),
79137                         helpString('intro.lines.retry_preset_residential', { preset: residentialPreset.name() })
79138                     );
79139
79140                     button.on('click.intro', function() {
79141                         continueTo(chooseCategoryRoad);
79142                     });
79143
79144                 }, 500);
79145
79146                 function continueTo(nextStep) {
79147                     context.container().select('.inspector-wrap').on('wheel.intro', null);
79148                     context.container().select('.preset-list-button').on('click.intro', null);
79149                     context.on('exit.intro', null);
79150                     nextStep();
79151                 }
79152             }
79153
79154
79155             function nameRoad() {
79156                 context.on('exit.intro', function() {
79157                     continueTo(didNameRoad);
79158                 });
79159
79160                 timeout(function() {
79161                     reveal('.entity-editor-pane',
79162                         helpString('intro.lines.name_road', { button: icon('#iD-icon-close', 'pre-text') }),
79163                         { tooltipClass: 'intro-lines-name_road' }
79164                     );
79165                 }, 500);
79166
79167                 function continueTo(nextStep) {
79168                     context.on('exit.intro', null);
79169                     nextStep();
79170                 }
79171             }
79172
79173
79174             function didNameRoad() {
79175                 context.history().checkpoint('doneAddLine');
79176
79177                 timeout(function() {
79178                     reveal('.surface', helpString('intro.lines.did_name_road'), {
79179                         buttonText: _t('intro.ok'),
79180                         buttonCallback: function() { continueTo(updateLine); }
79181                     });
79182                 }, 500);
79183
79184                 function continueTo(nextStep) {
79185                     nextStep();
79186                 }
79187             }
79188
79189
79190             function updateLine() {
79191                 context.history().reset('doneAddLine');
79192                 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79193                     return chapter.restart();
79194                 }
79195
79196                 var msec = transitionTime(woodRoadDragMidpoint, context.map().center());
79197                 if (msec) { reveal(null, null, { duration: 0 }); }
79198                 context.map().centerZoomEase(woodRoadDragMidpoint, 19, msec);
79199
79200                 timeout(function() {
79201                     var padding = 250 * Math.pow(2, context.map().zoom() - 19);
79202                     var box = pad(woodRoadDragMidpoint, padding, context);
79203                     var advance = function() { continueTo(addNode); };
79204
79205                     reveal(box, helpString('intro.lines.update_line'),
79206                         { buttonText: _t('intro.ok'), buttonCallback: advance }
79207                     );
79208
79209                     context.map().on('move.intro drawn.intro', function() {
79210                         var padding = 250 * Math.pow(2, context.map().zoom() - 19);
79211                         var box = pad(woodRoadDragMidpoint, padding, context);
79212                         reveal(box, helpString('intro.lines.update_line'),
79213                             { duration: 0, buttonText: _t('intro.ok'), buttonCallback: advance }
79214                         );
79215                     });
79216                 }, msec + 100);
79217
79218                 function continueTo(nextStep) {
79219                     context.map().on('move.intro drawn.intro', null);
79220                     nextStep();
79221                 }
79222             }
79223
79224
79225             function addNode() {
79226                 context.history().reset('doneAddLine');
79227                 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79228                     return chapter.restart();
79229                 }
79230
79231                 var padding = 40 * Math.pow(2, context.map().zoom() - 19);
79232                 var box = pad(woodRoadAddNode, padding, context);
79233                 var addNodeString = helpString('intro.lines.add_node' + (context.lastPointerType() === 'mouse' ? '' : '_touch'));
79234                 reveal(box, addNodeString);
79235
79236                 context.map().on('move.intro drawn.intro', function() {
79237                     var padding = 40 * Math.pow(2, context.map().zoom() - 19);
79238                     var box = pad(woodRoadAddNode, padding, context);
79239                     reveal(box, addNodeString, { duration: 0 });
79240                 });
79241
79242                 context.history().on('change.intro', function(changed) {
79243                     if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79244                         return continueTo(updateLine);
79245                     }
79246                     if (changed.created().length === 1) {
79247                         timeout(function() { continueTo(startDragEndpoint); }, 500);
79248                     }
79249                 });
79250
79251                 context.on('enter.intro', function(mode) {
79252                     if (mode.id !== 'select') {
79253                         continueTo(updateLine);
79254                     }
79255                 });
79256
79257                 function continueTo(nextStep) {
79258                     context.map().on('move.intro drawn.intro', null);
79259                     context.history().on('change.intro', null);
79260                     context.on('enter.intro', null);
79261                     nextStep();
79262                 }
79263             }
79264
79265
79266             function startDragEndpoint() {
79267                 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79268                     return continueTo(updateLine);
79269                 }
79270                 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
79271                 var box = pad(woodRoadDragEndpoint, padding, context);
79272                 var startDragString = helpString('intro.lines.start_drag_endpoint' + (context.lastPointerType() === 'mouse' ? '' : '_touch')) +
79273                     helpString('intro.lines.drag_to_intersection');
79274                 reveal(box, startDragString);
79275
79276                 context.map().on('move.intro drawn.intro', function() {
79277                     if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79278                         return continueTo(updateLine);
79279                     }
79280                     var padding = 100 * Math.pow(2, context.map().zoom() - 19);
79281                     var box = pad(woodRoadDragEndpoint, padding, context);
79282                     reveal(box, startDragString, { duration: 0 });
79283
79284                     var entity = context.entity(woodRoadEndID);
79285                     if (geoSphericalDistance(entity.loc, woodRoadDragEndpoint) <= 4) {
79286                         continueTo(finishDragEndpoint);
79287                     }
79288                 });
79289
79290                 function continueTo(nextStep) {
79291                     context.map().on('move.intro drawn.intro', null);
79292                     nextStep();
79293                 }
79294             }
79295
79296
79297             function finishDragEndpoint() {
79298                 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79299                     return continueTo(updateLine);
79300                 }
79301
79302                 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
79303                 var box = pad(woodRoadDragEndpoint, padding, context);
79304                 var finishDragString = helpString('intro.lines.spot_looks_good') +
79305                     helpString('intro.lines.finish_drag_endpoint' + (context.lastPointerType() === 'mouse' ? '' : '_touch'));
79306                 reveal(box, finishDragString);
79307
79308                 context.map().on('move.intro drawn.intro', function() {
79309                     if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79310                         return continueTo(updateLine);
79311                     }
79312                     var padding = 100 * Math.pow(2, context.map().zoom() - 19);
79313                     var box = pad(woodRoadDragEndpoint, padding, context);
79314                     reveal(box, finishDragString, { duration: 0 });
79315
79316                     var entity = context.entity(woodRoadEndID);
79317                     if (geoSphericalDistance(entity.loc, woodRoadDragEndpoint) > 4) {
79318                         continueTo(startDragEndpoint);
79319                     }
79320                 });
79321
79322                 context.on('enter.intro', function() {
79323                     continueTo(startDragMidpoint);
79324                 });
79325
79326                 function continueTo(nextStep) {
79327                     context.map().on('move.intro drawn.intro', null);
79328                     context.on('enter.intro', null);
79329                     nextStep();
79330                 }
79331             }
79332
79333
79334             function startDragMidpoint() {
79335                 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79336                     return continueTo(updateLine);
79337                 }
79338                 if (context.selectedIDs().indexOf(woodRoadID) === -1) {
79339                     context.enter(modeSelect(context, [woodRoadID]));
79340                 }
79341
79342                 var padding = 80 * Math.pow(2, context.map().zoom() - 19);
79343                 var box = pad(woodRoadDragMidpoint, padding, context);
79344                 reveal(box, helpString('intro.lines.start_drag_midpoint'));
79345
79346                 context.map().on('move.intro drawn.intro', function() {
79347                     if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79348                         return continueTo(updateLine);
79349                     }
79350                     var padding = 80 * Math.pow(2, context.map().zoom() - 19);
79351                     var box = pad(woodRoadDragMidpoint, padding, context);
79352                     reveal(box, helpString('intro.lines.start_drag_midpoint'), { duration: 0 });
79353                 });
79354
79355                 context.history().on('change.intro', function(changed) {
79356                     if (changed.created().length === 1) {
79357                         continueTo(continueDragMidpoint);
79358                     }
79359                 });
79360
79361                 context.on('enter.intro', function(mode) {
79362                     if (mode.id !== 'select') {
79363                         // keep Wood Road selected so midpoint triangles are drawn..
79364                         context.enter(modeSelect(context, [woodRoadID]));
79365                     }
79366                 });
79367
79368                 function continueTo(nextStep) {
79369                     context.map().on('move.intro drawn.intro', null);
79370                     context.history().on('change.intro', null);
79371                     context.on('enter.intro', null);
79372                     nextStep();
79373                 }
79374             }
79375
79376
79377             function continueDragMidpoint() {
79378                 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79379                     return continueTo(updateLine);
79380                 }
79381
79382                 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
79383                 var box = pad(woodRoadDragEndpoint, padding, context);
79384                 box.height += 400;
79385
79386                 var advance = function() {
79387                     context.history().checkpoint('doneUpdateLine');
79388                     continueTo(deleteLines);
79389                 };
79390
79391                 reveal(box, helpString('intro.lines.continue_drag_midpoint'),
79392                     { buttonText: _t('intro.ok'), buttonCallback: advance }
79393                 );
79394
79395                 context.map().on('move.intro drawn.intro', function() {
79396                     if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79397                         return continueTo(updateLine);
79398                     }
79399                     var padding = 100 * Math.pow(2, context.map().zoom() - 19);
79400                     var box = pad(woodRoadDragEndpoint, padding, context);
79401                     box.height += 400;
79402                     reveal(box, helpString('intro.lines.continue_drag_midpoint'),
79403                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: advance }
79404                     );
79405                 });
79406
79407                 function continueTo(nextStep) {
79408                     context.map().on('move.intro drawn.intro', null);
79409                     nextStep();
79410                 }
79411             }
79412
79413
79414             function deleteLines() {
79415                 context.history().reset('doneUpdateLine');
79416                 context.enter(modeBrowse(context));
79417
79418                 if (!context.hasEntity(washingtonStreetID) ||
79419                     !context.hasEntity(twelfthAvenueID) ||
79420                     !context.hasEntity(eleventhAvenueEndID)) {
79421                     return chapter.restart();
79422                 }
79423
79424                 var msec = transitionTime(deleteLinesLoc, context.map().center());
79425                 if (msec) { reveal(null, null, { duration: 0 }); }
79426                 context.map().centerZoomEase(deleteLinesLoc, 18, msec);
79427
79428                 timeout(function() {
79429                     var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79430                     var box = pad(deleteLinesLoc, padding, context);
79431                     box.top -= 200;
79432                     box.height += 400;
79433                     var advance = function() { continueTo(rightClickIntersection); };
79434
79435                     reveal(box, helpString('intro.lines.delete_lines', { street: _t('intro.graph.name.12th-avenue') }),
79436                         { buttonText: _t('intro.ok'), buttonCallback: advance }
79437                     );
79438
79439                     context.map().on('move.intro drawn.intro', function() {
79440                         var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79441                         var box = pad(deleteLinesLoc, padding, context);
79442                         box.top -= 200;
79443                         box.height += 400;
79444                         reveal(box, helpString('intro.lines.delete_lines', { street: _t('intro.graph.name.12th-avenue') }),
79445                             { duration: 0, buttonText: _t('intro.ok'), buttonCallback: advance }
79446                         );
79447                     });
79448
79449                     context.history().on('change.intro', function() {
79450                         timeout(function() {
79451                             continueTo(deleteLines);
79452                         }, 500);  // after any transition (e.g. if user deleted intersection)
79453                     });
79454
79455                 }, msec + 100);
79456
79457                 function continueTo(nextStep) {
79458                     context.map().on('move.intro drawn.intro', null);
79459                     context.history().on('change.intro', null);
79460                     nextStep();
79461                 }
79462             }
79463
79464
79465             function rightClickIntersection() {
79466                 context.history().reset('doneUpdateLine');
79467                 context.enter(modeBrowse(context));
79468
79469                 context.map().centerZoomEase(eleventhAvenueEnd, 18, 500);
79470
79471                 var rightClickString = helpString('intro.lines.split_street', {
79472                         street1: _t('intro.graph.name.11th-avenue'),
79473                         street2: _t('intro.graph.name.washington-street')
79474                     }) +
79475                     helpString('intro.lines.' + (context.lastPointerType() === 'mouse' ? 'rightclick_intersection' : 'edit_menu_intersection_touch'));
79476
79477                 timeout(function() {
79478                     var padding = 60 * Math.pow(2, context.map().zoom() - 18);
79479                     var box = pad(eleventhAvenueEnd, padding, context);
79480                     reveal(box, rightClickString);
79481
79482                     context.map().on('move.intro drawn.intro', function() {
79483                         var padding = 60 * Math.pow(2, context.map().zoom() - 18);
79484                         var box = pad(eleventhAvenueEnd, padding, context);
79485                         reveal(box, rightClickString,
79486                             { duration: 0 }
79487                         );
79488                     });
79489
79490                     context.on('enter.intro', function(mode) {
79491                         if (mode.id !== 'select') return;
79492                         var ids = context.selectedIDs();
79493                         if (ids.length !== 1 || ids[0] !== eleventhAvenueEndID) return;
79494
79495                         timeout(function() {
79496                             var node = selectMenuItem(context, 'split').node();
79497                             if (!node) return;
79498                             continueTo(splitIntersection);
79499                         }, 50);  // after menu visible
79500                     });
79501
79502                     context.history().on('change.intro', function() {
79503                         timeout(function() {
79504                             continueTo(deleteLines);
79505                         }, 300);  // after any transition (e.g. if user deleted intersection)
79506                     });
79507
79508                 }, 600);
79509
79510                 function continueTo(nextStep) {
79511                     context.map().on('move.intro drawn.intro', null);
79512                     context.on('enter.intro', null);
79513                     context.history().on('change.intro', null);
79514                     nextStep();
79515                 }
79516             }
79517
79518
79519             function splitIntersection() {
79520                 if (!context.hasEntity(washingtonStreetID) ||
79521                     !context.hasEntity(twelfthAvenueID) ||
79522                     !context.hasEntity(eleventhAvenueEndID)) {
79523                     return continueTo(deleteLines);
79524                 }
79525
79526                 var node = selectMenuItem(context, 'split').node();
79527                 if (!node) { return continueTo(rightClickIntersection); }
79528
79529                 var wasChanged = false;
79530                 _washingtonSegmentID = null;
79531
79532                 reveal('.edit-menu', helpString('intro.lines.split_intersection',
79533                     { street: _t('intro.graph.name.washington-street') }),
79534                     { padding: 50 }
79535                 );
79536
79537                 context.map().on('move.intro drawn.intro', function() {
79538                     var node = selectMenuItem(context, 'split').node();
79539                     if (!wasChanged && !node) { return continueTo(rightClickIntersection); }
79540
79541                     reveal('.edit-menu', helpString('intro.lines.split_intersection',
79542                         { street: _t('intro.graph.name.washington-street') }),
79543                         { duration: 0, padding: 50 }
79544                     );
79545                 });
79546
79547                 context.history().on('change.intro', function(changed) {
79548                     wasChanged = true;
79549                     timeout(function() {
79550                         if (context.history().undoAnnotation() === _t('operations.split.annotation.line')) {
79551                             _washingtonSegmentID = changed.created()[0].id;
79552                             continueTo(didSplit);
79553                         } else {
79554                             _washingtonSegmentID = null;
79555                             continueTo(retrySplit);
79556                         }
79557                     }, 300);  // after any transition (e.g. if user deleted intersection)
79558                 });
79559
79560                 function continueTo(nextStep) {
79561                     context.map().on('move.intro drawn.intro', null);
79562                     context.history().on('change.intro', null);
79563                     nextStep();
79564                 }
79565             }
79566
79567
79568             function retrySplit() {
79569                 context.enter(modeBrowse(context));
79570                 context.map().centerZoomEase(eleventhAvenueEnd, 18, 500);
79571                 var advance = function() { continueTo(rightClickIntersection); };
79572
79573                 var padding = 60 * Math.pow(2, context.map().zoom() - 18);
79574                 var box = pad(eleventhAvenueEnd, padding, context);
79575                 reveal(box, helpString('intro.lines.retry_split'),
79576                     { buttonText: _t('intro.ok'), buttonCallback: advance }
79577                 );
79578
79579                 context.map().on('move.intro drawn.intro', function() {
79580                     var padding = 60 * Math.pow(2, context.map().zoom() - 18);
79581                     var box = pad(eleventhAvenueEnd, padding, context);
79582                     reveal(box, helpString('intro.lines.retry_split'),
79583                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: advance }
79584                     );
79585                 });
79586
79587                 function continueTo(nextStep) {
79588                     context.map().on('move.intro drawn.intro', null);
79589                     nextStep();
79590                 }
79591             }
79592
79593
79594             function didSplit() {
79595                 if (!_washingtonSegmentID ||
79596                     !context.hasEntity(_washingtonSegmentID) ||
79597                     !context.hasEntity(washingtonStreetID) ||
79598                     !context.hasEntity(twelfthAvenueID) ||
79599                     !context.hasEntity(eleventhAvenueEndID)) {
79600                     return continueTo(rightClickIntersection);
79601                 }
79602
79603                 var ids = context.selectedIDs();
79604                 var string = 'intro.lines.did_split_' + (ids.length > 1 ? 'multi' : 'single');
79605                 var street = _t('intro.graph.name.washington-street');
79606
79607                 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79608                 var box = pad(twelfthAvenue, padding, context);
79609                 box.width = box.width / 2;
79610                 reveal(box, helpString(string, { street1: street, street2: street }),
79611                     { duration: 500 }
79612                 );
79613
79614                 timeout(function() {
79615                     context.map().centerZoomEase(twelfthAvenue, 18, 500);
79616
79617                     context.map().on('move.intro drawn.intro', function() {
79618                         var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79619                         var box = pad(twelfthAvenue, padding, context);
79620                         box.width = box.width / 2;
79621                         reveal(box, helpString(string, { street1: street, street2: street }),
79622                             { duration: 0 }
79623                         );
79624                     });
79625                 }, 600);  // after initial reveal and curtain cut
79626
79627                 context.on('enter.intro', function() {
79628                     var ids = context.selectedIDs();
79629                     if (ids.length === 1 && ids[0] === _washingtonSegmentID) {
79630                         continueTo(multiSelect);
79631                     }
79632                 });
79633
79634                 context.history().on('change.intro', function() {
79635                     if (!_washingtonSegmentID ||
79636                         !context.hasEntity(_washingtonSegmentID) ||
79637                         !context.hasEntity(washingtonStreetID) ||
79638                         !context.hasEntity(twelfthAvenueID) ||
79639                         !context.hasEntity(eleventhAvenueEndID)) {
79640                         return continueTo(rightClickIntersection);
79641                     }
79642                 });
79643
79644                 function continueTo(nextStep) {
79645                     context.map().on('move.intro drawn.intro', null);
79646                     context.on('enter.intro', null);
79647                     context.history().on('change.intro', null);
79648                     nextStep();
79649                 }
79650             }
79651
79652
79653             function multiSelect() {
79654                 if (!_washingtonSegmentID ||
79655                     !context.hasEntity(_washingtonSegmentID) ||
79656                     !context.hasEntity(washingtonStreetID) ||
79657                     !context.hasEntity(twelfthAvenueID) ||
79658                     !context.hasEntity(eleventhAvenueEndID)) {
79659                     return continueTo(rightClickIntersection);
79660                 }
79661
79662                 var ids = context.selectedIDs();
79663                 var hasWashington = ids.indexOf(_washingtonSegmentID) !== -1;
79664                 var hasTwelfth = ids.indexOf(twelfthAvenueID) !== -1;
79665
79666                 if (hasWashington && hasTwelfth) {
79667                     return continueTo(multiRightClick);
79668                 } else if (!hasWashington && !hasTwelfth) {
79669                     return continueTo(didSplit);
79670                 }
79671
79672                 context.map().centerZoomEase(twelfthAvenue, 18, 500);
79673
79674                 timeout(function() {
79675                     var selected, other, padding, box;
79676                     if (hasWashington) {
79677                         selected = _t('intro.graph.name.washington-street');
79678                         other = _t('intro.graph.name.12th-avenue');
79679                         padding = 60 * Math.pow(2, context.map().zoom() - 18);
79680                         box = pad(twelfthAvenueEnd, padding, context);
79681                         box.width *= 3;
79682                     } else {
79683                         selected = _t('intro.graph.name.12th-avenue');
79684                         other = _t('intro.graph.name.washington-street');
79685                         padding = 200 * Math.pow(2, context.map().zoom() - 18);
79686                         box = pad(twelfthAvenue, padding, context);
79687                         box.width /= 2;
79688                     }
79689
79690                     reveal(box,
79691                         helpString('intro.lines.multi_select',
79692                             { selected: selected, other1: other }) + ' ' +
79693                         helpString('intro.lines.add_to_selection_' + (context.lastPointerType() === 'mouse' ? 'click' : 'touch'),
79694                             { selected: selected, other2: other })
79695                     );
79696
79697                     context.map().on('move.intro drawn.intro', function() {
79698                         if (hasWashington) {
79699                             selected = _t('intro.graph.name.washington-street');
79700                             other = _t('intro.graph.name.12th-avenue');
79701                             padding = 60 * Math.pow(2, context.map().zoom() - 18);
79702                             box = pad(twelfthAvenueEnd, padding, context);
79703                             box.width *= 3;
79704                         } else {
79705                             selected = _t('intro.graph.name.12th-avenue');
79706                             other = _t('intro.graph.name.washington-street');
79707                             padding = 200 * Math.pow(2, context.map().zoom() - 18);
79708                             box = pad(twelfthAvenue, padding, context);
79709                             box.width /= 2;
79710                         }
79711
79712                         reveal(box,
79713                             helpString('intro.lines.multi_select',
79714                                 { selected: selected, other1: other }) + ' ' +
79715                             helpString('intro.lines.add_to_selection_' + (context.lastPointerType() === 'mouse' ? 'click' : 'touch'),
79716                                 { selected: selected, other2: other }),
79717                             { duration: 0 }
79718                         );
79719                     });
79720
79721                     context.on('enter.intro', function() {
79722                         continueTo(multiSelect);
79723                     });
79724
79725                     context.history().on('change.intro', function() {
79726                         if (!_washingtonSegmentID ||
79727                             !context.hasEntity(_washingtonSegmentID) ||
79728                             !context.hasEntity(washingtonStreetID) ||
79729                             !context.hasEntity(twelfthAvenueID) ||
79730                             !context.hasEntity(eleventhAvenueEndID)) {
79731                             return continueTo(rightClickIntersection);
79732                         }
79733                     });
79734                 }, 600);
79735
79736                 function continueTo(nextStep) {
79737                     context.map().on('move.intro drawn.intro', null);
79738                     context.on('enter.intro', null);
79739                     context.history().on('change.intro', null);
79740                     nextStep();
79741                 }
79742             }
79743
79744
79745             function multiRightClick() {
79746                 if (!_washingtonSegmentID ||
79747                     !context.hasEntity(_washingtonSegmentID) ||
79748                     !context.hasEntity(washingtonStreetID) ||
79749                     !context.hasEntity(twelfthAvenueID) ||
79750                     !context.hasEntity(eleventhAvenueEndID)) {
79751                     return continueTo(rightClickIntersection);
79752                 }
79753
79754                 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79755                 var box = pad(twelfthAvenue, padding, context);
79756
79757                 var rightClickString = helpString('intro.lines.multi_select_success') +
79758                     helpString('intro.lines.multi_' + (context.lastPointerType() === 'mouse' ? 'rightclick' : 'edit_menu_touch'));
79759                 reveal(box, rightClickString);
79760
79761                 context.map().on('move.intro drawn.intro', function() {
79762                     var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79763                     var box = pad(twelfthAvenue, padding, context);
79764                     reveal(box, rightClickString, { duration: 0 });
79765                 });
79766
79767                 context.ui().editMenu().on('toggled.intro', function(open) {
79768                     if (!open) return;
79769
79770                     timeout(function() {
79771                         var ids = context.selectedIDs();
79772                         if (ids.length === 2 &&
79773                             ids.indexOf(twelfthAvenueID) !== -1 &&
79774                             ids.indexOf(_washingtonSegmentID) !== -1) {
79775                                 var node = selectMenuItem(context, 'delete').node();
79776                                 if (!node) return;
79777                                 continueTo(multiDelete);
79778                         } else if (ids.length === 1 &&
79779                             ids.indexOf(_washingtonSegmentID) !== -1) {
79780                             return continueTo(multiSelect);
79781                         } else {
79782                             return continueTo(didSplit);
79783                         }
79784                     }, 300);  // after edit menu visible
79785                 });
79786
79787                 context.history().on('change.intro', function() {
79788                     if (!_washingtonSegmentID ||
79789                         !context.hasEntity(_washingtonSegmentID) ||
79790                         !context.hasEntity(washingtonStreetID) ||
79791                         !context.hasEntity(twelfthAvenueID) ||
79792                         !context.hasEntity(eleventhAvenueEndID)) {
79793                         return continueTo(rightClickIntersection);
79794                     }
79795                 });
79796
79797                 function continueTo(nextStep) {
79798                     context.map().on('move.intro drawn.intro', null);
79799                     context.ui().editMenu().on('toggled.intro', null);
79800                     context.history().on('change.intro', null);
79801                     nextStep();
79802                 }
79803             }
79804
79805
79806             function multiDelete() {
79807                 if (!_washingtonSegmentID ||
79808                     !context.hasEntity(_washingtonSegmentID) ||
79809                     !context.hasEntity(washingtonStreetID) ||
79810                     !context.hasEntity(twelfthAvenueID) ||
79811                     !context.hasEntity(eleventhAvenueEndID)) {
79812                     return continueTo(rightClickIntersection);
79813                 }
79814
79815                 var node = selectMenuItem(context, 'delete').node();
79816                 if (!node) return continueTo(multiRightClick);
79817
79818                 reveal('.edit-menu',
79819                     helpString('intro.lines.multi_delete'),
79820                     { padding: 50 }
79821                 );
79822
79823                 context.map().on('move.intro drawn.intro', function() {
79824                     reveal('.edit-menu',
79825                         helpString('intro.lines.multi_delete'),
79826                         { duration: 0, padding: 50 }
79827                     );
79828                 });
79829
79830                 context.on('exit.intro', function() {
79831                     if (context.hasEntity(_washingtonSegmentID) || context.hasEntity(twelfthAvenueID)) {
79832                         return continueTo(multiSelect);  // left select mode but roads still exist
79833                     }
79834                 });
79835
79836                 context.history().on('change.intro', function() {
79837                     if (context.hasEntity(_washingtonSegmentID) || context.hasEntity(twelfthAvenueID)) {
79838                         continueTo(retryDelete);         // changed something but roads still exist
79839                     } else {
79840                         continueTo(play);
79841                     }
79842                 });
79843
79844                 function continueTo(nextStep) {
79845                     context.map().on('move.intro drawn.intro', null);
79846                     context.on('exit.intro', null);
79847                     context.history().on('change.intro', null);
79848                     nextStep();
79849                 }
79850             }
79851
79852
79853             function retryDelete() {
79854                 context.enter(modeBrowse(context));
79855
79856                 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79857                 var box = pad(twelfthAvenue, padding, context);
79858                 reveal(box, helpString('intro.lines.retry_delete'), {
79859                     buttonText: _t('intro.ok'),
79860                     buttonCallback: function() { continueTo(multiSelect); }
79861                 });
79862
79863                 function continueTo(nextStep) {
79864                     nextStep();
79865                 }
79866             }
79867
79868
79869             function play() {
79870                 dispatch$1.call('done');
79871                 reveal('.ideditor',
79872                     helpString('intro.lines.play', { next: _t('intro.buildings.title') }), {
79873                         tooltipBox: '.intro-nav-wrap .chapter-building',
79874                         buttonText: _t('intro.ok'),
79875                         buttonCallback: function() { reveal('.ideditor'); }
79876                     }
79877                 );
79878            }
79879
79880
79881             chapter.enter = function() {
79882                 addLine();
79883             };
79884
79885
79886             chapter.exit = function() {
79887                 timeouts.forEach(window.clearTimeout);
79888                 select(window).on('pointerdown.intro mousedown.intro', null, true);
79889                 context.on('enter.intro exit.intro', null);
79890                 context.map().on('move.intro drawn.intro', null);
79891                 context.history().on('change.intro', null);
79892                 context.container().select('.inspector-wrap').on('wheel.intro', null);
79893                 context.container().select('.preset-list-button').on('click.intro', null);
79894             };
79895
79896
79897             chapter.restart = function() {
79898                 chapter.exit();
79899                 chapter.enter();
79900             };
79901
79902
79903             return utilRebind(chapter, dispatch$1, 'on');
79904         }
79905
79906         function uiIntroBuilding(context, reveal) {
79907             var dispatch$1 = dispatch('done');
79908             var house = [-85.62815, 41.95638];
79909             var tank = [-85.62732, 41.95347];
79910             var buildingCatetory = _mainPresetIndex.item('category-building');
79911             var housePreset = _mainPresetIndex.item('building/house');
79912             var tankPreset = _mainPresetIndex.item('man_made/storage_tank');
79913             var timeouts = [];
79914             var _houseID = null;
79915             var _tankID = null;
79916
79917
79918             var chapter = {
79919                 title: 'intro.buildings.title'
79920             };
79921
79922
79923             function timeout(f, t) {
79924                 timeouts.push(window.setTimeout(f, t));
79925             }
79926
79927
79928             function eventCancel() {
79929                 event.stopPropagation();
79930                 event.preventDefault();
79931             }
79932
79933
79934             function revealHouse(center, text, options) {
79935                 var padding = 160 * Math.pow(2, context.map().zoom() - 20);
79936                 var box = pad(center, padding, context);
79937                 reveal(box, text, options);
79938             }
79939
79940
79941             function revealTank(center, text, options) {
79942                 var padding = 190 * Math.pow(2, context.map().zoom() - 19.5);
79943                 var box = pad(center, padding, context);
79944                 reveal(box, text, options);
79945             }
79946
79947
79948             function addHouse() {
79949                 context.enter(modeBrowse(context));
79950                 context.history().reset('initial');
79951                 _houseID = null;
79952
79953                 var msec = transitionTime(house, context.map().center());
79954                 if (msec) { reveal(null, null, { duration: 0 }); }
79955                 context.map().centerZoomEase(house, 19, msec);
79956
79957                 timeout(function() {
79958                     var tooltip = reveal('button.add-area',
79959                         helpString('intro.buildings.add_building'));
79960
79961                     tooltip.selectAll('.popover-inner')
79962                         .insert('svg', 'span')
79963                         .attr('class', 'tooltip-illustration')
79964                         .append('use')
79965                         .attr('xlink:href', '#iD-graphic-buildings');
79966
79967                     context.on('enter.intro', function(mode) {
79968                         if (mode.id !== 'add-area') return;
79969                         continueTo(startHouse);
79970                     });
79971                 }, msec + 100);
79972
79973                 function continueTo(nextStep) {
79974                     context.on('enter.intro', null);
79975                     nextStep();
79976                 }
79977             }
79978
79979
79980             function startHouse() {
79981                 if (context.mode().id !== 'add-area') {
79982                     return continueTo(addHouse);
79983                 }
79984
79985                 _houseID = null;
79986                 context.map().zoomEase(20, 500);
79987
79988                 timeout(function() {
79989                     var startString = helpString('intro.buildings.start_building') +
79990                         helpString('intro.buildings.building_corner_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap'));
79991                     revealHouse(house, startString);
79992
79993                     context.map().on('move.intro drawn.intro', function() {
79994                         revealHouse(house, startString, { duration: 0 });
79995                     });
79996
79997                     context.on('enter.intro', function(mode) {
79998                         if (mode.id !== 'draw-area') return chapter.restart();
79999                         continueTo(continueHouse);
80000                     });
80001
80002                 }, 550);  // after easing
80003
80004                 function continueTo(nextStep) {
80005                     context.map().on('move.intro drawn.intro', null);
80006                     context.on('enter.intro', null);
80007                     nextStep();
80008                 }
80009             }
80010
80011
80012             function continueHouse() {
80013                 if (context.mode().id !== 'draw-area') {
80014                     return continueTo(addHouse);
80015                 }
80016
80017                 _houseID = null;
80018
80019                 var continueString = helpString('intro.buildings.continue_building') + '{br}' +
80020                     helpString('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) +
80021                     helpString('intro.buildings.finish_building');
80022
80023                 revealHouse(house, continueString);
80024
80025                 context.map().on('move.intro drawn.intro', function() {
80026                     revealHouse(house, continueString, { duration: 0 });
80027                 });
80028
80029                 context.on('enter.intro', function(mode) {
80030                     if (mode.id === 'draw-area') {
80031                         return;
80032                     } else if (mode.id === 'select') {
80033                         var graph = context.graph();
80034                         var way = context.entity(context.selectedIDs()[0]);
80035                         var nodes = graph.childNodes(way);
80036                         var points = utilArrayUniq(nodes)
80037                             .map(function(n) { return context.projection(n.loc); });
80038
80039                         if (isMostlySquare(points)) {
80040                             _houseID = way.id;
80041                             return continueTo(chooseCategoryBuilding);
80042                         } else {
80043                             return continueTo(retryHouse);
80044                         }
80045
80046                     } else {
80047                         return chapter.restart();
80048                     }
80049                 });
80050
80051                 function continueTo(nextStep) {
80052                     context.map().on('move.intro drawn.intro', null);
80053                     context.on('enter.intro', null);
80054                     nextStep();
80055                 }
80056             }
80057
80058
80059             function retryHouse() {
80060                 var onClick = function() { continueTo(addHouse); };
80061
80062                 revealHouse(house, helpString('intro.buildings.retry_building'),
80063                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
80064                 );
80065
80066                 context.map().on('move.intro drawn.intro', function() {
80067                     revealHouse(house, helpString('intro.buildings.retry_building'),
80068                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick }
80069                     );
80070                 });
80071
80072                 function continueTo(nextStep) {
80073                     context.map().on('move.intro drawn.intro', null);
80074                     nextStep();
80075                 }
80076             }
80077
80078
80079             function chooseCategoryBuilding() {
80080                 if (!_houseID || !context.hasEntity(_houseID)) {
80081                     return addHouse();
80082                 }
80083                 var ids = context.selectedIDs();
80084                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) {
80085                     context.enter(modeSelect(context, [_houseID]));
80086                 }
80087
80088                 // disallow scrolling
80089                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80090
80091                 timeout(function() {
80092                     // reset pane, in case user somehow happened to change it..
80093                     context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
80094
80095                     var button = context.container().select('.preset-category-building .preset-list-button');
80096
80097                     reveal(button.node(),
80098                         helpString('intro.buildings.choose_category_building', { category: buildingCatetory.name() })
80099                     );
80100
80101                     button.on('click.intro', function() {
80102                         button.on('click.intro', null);
80103                         continueTo(choosePresetHouse);
80104                     });
80105
80106                 }, 400);  // after preset list pane visible..
80107
80108
80109                 context.on('enter.intro', function(mode) {
80110                     if (!_houseID || !context.hasEntity(_houseID)) {
80111                         return continueTo(addHouse);
80112                     }
80113                     var ids = context.selectedIDs();
80114                     if (mode.id !== 'select' || !ids.length || ids[0] !== _houseID) {
80115                         return continueTo(chooseCategoryBuilding);
80116                     }
80117                 });
80118
80119                 function continueTo(nextStep) {
80120                     context.container().select('.inspector-wrap').on('wheel.intro', null);
80121                     context.container().select('.preset-list-button').on('click.intro', null);
80122                     context.on('enter.intro', null);
80123                     nextStep();
80124                 }
80125             }
80126
80127
80128             function choosePresetHouse() {
80129                 if (!_houseID || !context.hasEntity(_houseID)) {
80130                     return addHouse();
80131                 }
80132                 var ids = context.selectedIDs();
80133                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) {
80134                     context.enter(modeSelect(context, [_houseID]));
80135                 }
80136
80137                 // disallow scrolling
80138                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80139
80140                 timeout(function() {
80141                     // reset pane, in case user somehow happened to change it..
80142                     context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
80143
80144                     var button = context.container().select('.preset-building-house .preset-list-button');
80145
80146                     reveal(button.node(),
80147                         helpString('intro.buildings.choose_preset_house', { preset: housePreset.name() }),
80148                         { duration: 300 }
80149                     );
80150
80151                     button.on('click.intro', function() {
80152                         button.on('click.intro', null);
80153                         continueTo(closeEditorHouse);
80154                     });
80155
80156                 }, 400);  // after preset list pane visible..
80157
80158                 context.on('enter.intro', function(mode) {
80159                     if (!_houseID || !context.hasEntity(_houseID)) {
80160                         return continueTo(addHouse);
80161                     }
80162                     var ids = context.selectedIDs();
80163                     if (mode.id !== 'select' || !ids.length || ids[0] !== _houseID) {
80164                         return continueTo(chooseCategoryBuilding);
80165                     }
80166                 });
80167
80168                 function continueTo(nextStep) {
80169                     context.container().select('.inspector-wrap').on('wheel.intro', null);
80170                     context.container().select('.preset-list-button').on('click.intro', null);
80171                     context.on('enter.intro', null);
80172                     nextStep();
80173                 }
80174             }
80175
80176
80177             function closeEditorHouse() {
80178                 if (!_houseID || !context.hasEntity(_houseID)) {
80179                     return addHouse();
80180                 }
80181                 var ids = context.selectedIDs();
80182                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) {
80183                     context.enter(modeSelect(context, [_houseID]));
80184                 }
80185
80186                 context.history().checkpoint('hasHouse');
80187
80188                 context.on('exit.intro', function() {
80189                     continueTo(rightClickHouse);
80190                 });
80191
80192                 timeout(function() {
80193                     reveal('.entity-editor-pane',
80194                         helpString('intro.buildings.close', { button: icon('#iD-icon-close', 'pre-text') })
80195                     );
80196                 }, 500);
80197
80198                 function continueTo(nextStep) {
80199                     context.on('exit.intro', null);
80200                     nextStep();
80201                 }
80202             }
80203
80204
80205             function rightClickHouse() {
80206                 if (!_houseID) return chapter.restart();
80207
80208                 context.enter(modeBrowse(context));
80209                 context.history().reset('hasHouse');
80210                 var zoom = context.map().zoom();
80211                 if (zoom < 20) {
80212                     zoom = 20;
80213                 }
80214                 context.map().centerZoomEase(house, zoom, 500);
80215
80216                 context.on('enter.intro', function(mode) {
80217                     if (mode.id !== 'select') return;
80218                     var ids = context.selectedIDs();
80219                     if (ids.length !== 1 || ids[0] !== _houseID) return;
80220
80221                     timeout(function() {
80222                         var node = selectMenuItem(context, 'orthogonalize').node();
80223                         if (!node) return;
80224                         continueTo(clickSquare);
80225                     }, 50);  // after menu visible
80226                 });
80227
80228                 context.map().on('move.intro drawn.intro', function() {
80229                     var rightclickString = helpString('intro.buildings.' + (context.lastPointerType() === 'mouse' ? 'rightclick_building' : 'edit_menu_building_touch'));
80230                     revealHouse(house, rightclickString, { duration: 0 });
80231                 });
80232
80233                 context.history().on('change.intro', function() {
80234                     continueTo(rightClickHouse);
80235                 });
80236
80237                 function continueTo(nextStep) {
80238                     context.on('enter.intro', null);
80239                     context.map().on('move.intro drawn.intro', null);
80240                     context.history().on('change.intro', null);
80241                     nextStep();
80242                 }
80243             }
80244
80245
80246             function clickSquare() {
80247                 if (!_houseID) return chapter.restart();
80248                 var entity = context.hasEntity(_houseID);
80249                 if (!entity) return continueTo(rightClickHouse);
80250
80251                 var node = selectMenuItem(context, 'orthogonalize').node();
80252                 if (!node) { return continueTo(rightClickHouse); }
80253
80254                 var wasChanged = false;
80255
80256                 reveal('.edit-menu',
80257                     helpString('intro.buildings.square_building'),
80258                     { padding: 50 }
80259                 );
80260
80261                 context.on('enter.intro', function(mode) {
80262                     if (mode.id === 'browse') {
80263                         continueTo(rightClickHouse);
80264                     } else if (mode.id === 'move' || mode.id === 'rotate') {
80265                         continueTo(retryClickSquare);
80266                     }
80267                 });
80268
80269                 context.map().on('move.intro', function() {
80270                     var node = selectMenuItem(context, 'orthogonalize').node();
80271                     if (!wasChanged && !node) { return continueTo(rightClickHouse); }
80272
80273                     reveal('.edit-menu',
80274                         helpString('intro.buildings.square_building'),
80275                         { duration: 0, padding: 50 }
80276                     );
80277                 });
80278
80279                 context.history().on('change.intro', function() {
80280                     wasChanged = true;
80281                     context.history().on('change.intro', null);
80282
80283                     // Something changed.  Wait for transition to complete and check undo annotation.
80284                     timeout(function() {
80285                         if (context.history().undoAnnotation() === _t('operations.orthogonalize.annotation.feature.single')) {
80286                             continueTo(doneSquare);
80287                         } else {
80288                             continueTo(retryClickSquare);
80289                         }
80290                     }, 500);  // after transitioned actions
80291                 });
80292
80293                 function continueTo(nextStep) {
80294                     context.on('enter.intro', null);
80295                     context.map().on('move.intro', null);
80296                     context.history().on('change.intro', null);
80297                     nextStep();
80298                 }
80299             }
80300
80301
80302             function retryClickSquare() {
80303                 context.enter(modeBrowse(context));
80304
80305                 revealHouse(house, helpString('intro.buildings.retry_square'), {
80306                     buttonText: _t('intro.ok'),
80307                     buttonCallback: function() { continueTo(rightClickHouse); }
80308                 });
80309
80310                 function continueTo(nextStep) {
80311                     nextStep();
80312                 }
80313             }
80314
80315
80316             function doneSquare() {
80317                 context.history().checkpoint('doneSquare');
80318
80319                 revealHouse(house, helpString('intro.buildings.done_square'), {
80320                     buttonText: _t('intro.ok'),
80321                     buttonCallback: function() { continueTo(addTank); }
80322                 });
80323
80324                 function continueTo(nextStep) {
80325                     nextStep();
80326                 }
80327             }
80328
80329
80330             function addTank() {
80331                 context.enter(modeBrowse(context));
80332                 context.history().reset('doneSquare');
80333                 _tankID = null;
80334
80335                 var msec = transitionTime(tank, context.map().center());
80336                 if (msec) { reveal(null, null, { duration: 0 }); }
80337                 context.map().centerZoomEase(tank, 19.5, msec);
80338
80339                 timeout(function() {
80340                     reveal('button.add-area',
80341                         helpString('intro.buildings.add_tank')
80342                     );
80343
80344                     context.on('enter.intro', function(mode) {
80345                         if (mode.id !== 'add-area') return;
80346                         continueTo(startTank);
80347                     });
80348                 }, msec + 100);
80349
80350                 function continueTo(nextStep) {
80351                     context.on('enter.intro', null);
80352                     nextStep();
80353                 }
80354             }
80355
80356
80357             function startTank() {
80358                 if (context.mode().id !== 'add-area') {
80359                     return continueTo(addTank);
80360                 }
80361
80362                 _tankID = null;
80363
80364                 timeout(function() {
80365                     var startString = helpString('intro.buildings.start_tank') +
80366                         helpString('intro.buildings.tank_edge_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap'));
80367                     revealTank(tank, startString);
80368
80369                     context.map().on('move.intro drawn.intro', function() {
80370                         revealTank(tank, startString, { duration: 0 });
80371                     });
80372
80373                     context.on('enter.intro', function(mode) {
80374                         if (mode.id !== 'draw-area') return chapter.restart();
80375                         continueTo(continueTank);
80376                     });
80377
80378                 }, 550);  // after easing
80379
80380                 function continueTo(nextStep) {
80381                     context.map().on('move.intro drawn.intro', null);
80382                     context.on('enter.intro', null);
80383                     nextStep();
80384                 }
80385             }
80386
80387
80388             function continueTank() {
80389                 if (context.mode().id !== 'draw-area') {
80390                     return continueTo(addTank);
80391                 }
80392
80393                 _tankID = null;
80394
80395                 var continueString = helpString('intro.buildings.continue_tank') + '{br}' +
80396                     helpString('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) +
80397                     helpString('intro.buildings.finish_tank');
80398
80399                 revealTank(tank, continueString);
80400
80401                 context.map().on('move.intro drawn.intro', function() {
80402                     revealTank(tank, continueString, { duration: 0 });
80403                 });
80404
80405                 context.on('enter.intro', function(mode) {
80406                     if (mode.id === 'draw-area') {
80407                         return;
80408                     } else if (mode.id === 'select') {
80409                         _tankID = context.selectedIDs()[0];
80410                         return continueTo(searchPresetTank);
80411                     } else {
80412                         return continueTo(addTank);
80413                     }
80414                 });
80415
80416                 function continueTo(nextStep) {
80417                     context.map().on('move.intro drawn.intro', null);
80418                     context.on('enter.intro', null);
80419                     nextStep();
80420                 }
80421             }
80422
80423
80424             function searchPresetTank() {
80425                 if (!_tankID || !context.hasEntity(_tankID)) {
80426                     return addTank();
80427                 }
80428                 var ids = context.selectedIDs();
80429                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _tankID) {
80430                     context.enter(modeSelect(context, [_tankID]));
80431                 }
80432
80433                 // disallow scrolling
80434                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80435
80436                 timeout(function() {
80437                     // reset pane, in case user somehow happened to change it..
80438                     context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
80439
80440                     context.container().select('.preset-search-input')
80441                         .on('keydown.intro', null)
80442                         .on('keyup.intro', checkPresetSearch);
80443
80444                     reveal('.preset-search-input',
80445                         helpString('intro.buildings.search_tank', { preset: tankPreset.name() })
80446                     );
80447                 }, 400);  // after preset list pane visible..
80448
80449                 context.on('enter.intro', function(mode) {
80450                     if (!_tankID || !context.hasEntity(_tankID)) {
80451                         return continueTo(addTank);
80452                     }
80453
80454                     var ids = context.selectedIDs();
80455                     if (mode.id !== 'select' || !ids.length || ids[0] !== _tankID) {
80456                         // keep the user's area selected..
80457                         context.enter(modeSelect(context, [_tankID]));
80458
80459                         // reset pane, in case user somehow happened to change it..
80460                         context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
80461                         // disallow scrolling
80462                         context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80463
80464                         context.container().select('.preset-search-input')
80465                             .on('keydown.intro', null)
80466                             .on('keyup.intro', checkPresetSearch);
80467
80468                         reveal('.preset-search-input',
80469                             helpString('intro.buildings.search_tank', { preset: tankPreset.name() })
80470                         );
80471
80472                         context.history().on('change.intro', null);
80473                     }
80474                 });
80475
80476                 function checkPresetSearch() {
80477                     var first = context.container().select('.preset-list-item:first-child');
80478
80479                     if (first.classed('preset-man_made-storage_tank')) {
80480                         reveal(first.select('.preset-list-button').node(),
80481                             helpString('intro.buildings.choose_tank', { preset: tankPreset.name() }),
80482                             { duration: 300 }
80483                         );
80484
80485                         context.container().select('.preset-search-input')
80486                             .on('keydown.intro', eventCancel, true)
80487                             .on('keyup.intro', null);
80488
80489                         context.history().on('change.intro', function() {
80490                             continueTo(closeEditorTank);
80491                         });
80492                     }
80493                 }
80494
80495                 function continueTo(nextStep) {
80496                     context.container().select('.inspector-wrap').on('wheel.intro', null);
80497                     context.on('enter.intro', null);
80498                     context.history().on('change.intro', null);
80499                     context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
80500                     nextStep();
80501                 }
80502             }
80503
80504
80505             function closeEditorTank() {
80506                 if (!_tankID || !context.hasEntity(_tankID)) {
80507                     return addTank();
80508                 }
80509                 var ids = context.selectedIDs();
80510                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _tankID) {
80511                     context.enter(modeSelect(context, [_tankID]));
80512                 }
80513
80514                 context.history().checkpoint('hasTank');
80515
80516                 context.on('exit.intro', function() {
80517                     continueTo(rightClickTank);
80518                 });
80519
80520                 timeout(function() {
80521                     reveal('.entity-editor-pane',
80522                         helpString('intro.buildings.close', { button: icon('#iD-icon-close', 'pre-text') })
80523                     );
80524                 }, 500);
80525
80526                 function continueTo(nextStep) {
80527                     context.on('exit.intro', null);
80528                     nextStep();
80529                 }
80530             }
80531
80532
80533             function rightClickTank() {
80534                 if (!_tankID) return continueTo(addTank);
80535
80536                 context.enter(modeBrowse(context));
80537                 context.history().reset('hasTank');
80538                 context.map().centerEase(tank, 500);
80539
80540                 timeout(function() {
80541                     context.on('enter.intro', function(mode) {
80542                         if (mode.id !== 'select') return;
80543                         var ids = context.selectedIDs();
80544                         if (ids.length !== 1 || ids[0] !== _tankID) return;
80545
80546                         timeout(function() {
80547                             var node = selectMenuItem(context, 'circularize').node();
80548                             if (!node) return;
80549                             continueTo(clickCircle);
80550                         }, 50);  // after menu visible
80551                     });
80552
80553                     var rightclickString = helpString('intro.buildings.' + (context.lastPointerType() === 'mouse' ? 'rightclick_tank' : 'edit_menu_tank_touch'));
80554
80555                     revealTank(tank, rightclickString);
80556
80557                     context.map().on('move.intro drawn.intro', function() {
80558                         revealTank(tank, rightclickString, { duration: 0 });
80559                     });
80560
80561                     context.history().on('change.intro', function() {
80562                         continueTo(rightClickTank);
80563                     });
80564
80565                 }, 600);
80566
80567                 function continueTo(nextStep) {
80568                     context.on('enter.intro', null);
80569                     context.map().on('move.intro drawn.intro', null);
80570                     context.history().on('change.intro', null);
80571                     nextStep();
80572                 }
80573             }
80574
80575
80576             function clickCircle() {
80577                 if (!_tankID) return chapter.restart();
80578                 var entity = context.hasEntity(_tankID);
80579                 if (!entity) return continueTo(rightClickTank);
80580
80581                 var node = selectMenuItem(context, 'circularize').node();
80582                 if (!node) { return continueTo(rightClickTank); }
80583
80584                 var wasChanged = false;
80585
80586                 reveal('.edit-menu',
80587                     helpString('intro.buildings.circle_tank'),
80588                     { padding: 50 }
80589                 );
80590
80591                 context.on('enter.intro', function(mode) {
80592                     if (mode.id === 'browse') {
80593                         continueTo(rightClickTank);
80594                     } else if (mode.id === 'move' || mode.id === 'rotate') {
80595                         continueTo(retryClickCircle);
80596                     }
80597                 });
80598
80599                 context.map().on('move.intro', function() {
80600                     var node = selectMenuItem(context, 'circularize').node();
80601                     if (!wasChanged && !node) { return continueTo(rightClickTank); }
80602
80603                     reveal('.edit-menu',
80604                         helpString('intro.buildings.circle_tank'),
80605                         { duration: 0, padding: 50 }
80606                     );
80607                 });
80608
80609                 context.history().on('change.intro', function() {
80610                     wasChanged = true;
80611                     context.history().on('change.intro', null);
80612
80613                     // Something changed.  Wait for transition to complete and check undo annotation.
80614                     timeout(function() {
80615                         if (context.history().undoAnnotation() === _t('operations.circularize.annotation.single')) {
80616                             continueTo(play);
80617                         } else {
80618                             continueTo(retryClickCircle);
80619                         }
80620                     }, 500);  // after transitioned actions
80621                 });
80622
80623                 function continueTo(nextStep) {
80624                     context.on('enter.intro', null);
80625                     context.map().on('move.intro', null);
80626                     context.history().on('change.intro', null);
80627                     nextStep();
80628                 }
80629             }
80630
80631
80632             function retryClickCircle() {
80633                 context.enter(modeBrowse(context));
80634
80635                 revealTank(tank, helpString('intro.buildings.retry_circle'), {
80636                     buttonText: _t('intro.ok'),
80637                     buttonCallback: function() { continueTo(rightClickTank); }
80638                 });
80639
80640                 function continueTo(nextStep) {
80641                     nextStep();
80642                 }
80643             }
80644
80645
80646             function play() {
80647                 dispatch$1.call('done');
80648                 reveal('.ideditor',
80649                     helpString('intro.buildings.play', { next: _t('intro.startediting.title') }), {
80650                         tooltipBox: '.intro-nav-wrap .chapter-startEditing',
80651                         buttonText: _t('intro.ok'),
80652                         buttonCallback: function() { reveal('.ideditor'); }
80653                     }
80654                 );
80655             }
80656
80657
80658             chapter.enter = function() {
80659                 addHouse();
80660             };
80661
80662
80663             chapter.exit = function() {
80664                 timeouts.forEach(window.clearTimeout);
80665                 context.on('enter.intro exit.intro', null);
80666                 context.map().on('move.intro drawn.intro', null);
80667                 context.history().on('change.intro', null);
80668                 context.container().select('.inspector-wrap').on('wheel.intro', null);
80669                 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
80670                 context.container().select('.more-fields .combobox-input').on('click.intro', null);
80671             };
80672
80673
80674             chapter.restart = function() {
80675                 chapter.exit();
80676                 chapter.enter();
80677             };
80678
80679
80680             return utilRebind(chapter, dispatch$1, 'on');
80681         }
80682
80683         function uiIntroStartEditing(context, reveal) {
80684             var dispatch$1 = dispatch('done', 'startEditing');
80685             var modalSelection = select(null);
80686
80687
80688             var chapter = {
80689                 title: 'intro.startediting.title'
80690             };
80691
80692             function showHelp() {
80693                 reveal('.map-control.help-control',
80694                     helpString('intro.startediting.help'), {
80695                         buttonText: _t('intro.ok'),
80696                         buttonCallback: function() { shortcuts(); }
80697                     }
80698                 );
80699             }
80700
80701             function shortcuts() {
80702                 reveal('.map-control.help-control',
80703                     helpString('intro.startediting.shortcuts'), {
80704                         buttonText: _t('intro.ok'),
80705                         buttonCallback: function() { showSave(); }
80706                     }
80707                 );
80708             }
80709
80710             function showSave() {
80711                 context.container().selectAll('.shaded').remove();  // in case user opened keyboard shortcuts
80712                 reveal('.top-toolbar button.save',
80713                     helpString('intro.startediting.save'), {
80714                         buttonText: _t('intro.ok'),
80715                         buttonCallback: function() { showStart(); }
80716                     }
80717                 );
80718             }
80719
80720             function showStart() {
80721                 context.container().selectAll('.shaded').remove();  // in case user opened keyboard shortcuts
80722
80723                 modalSelection = uiModal(context.container());
80724
80725                 modalSelection.select('.modal')
80726                     .attr('class', 'modal-splash modal');
80727
80728                 modalSelection.selectAll('.close').remove();
80729
80730                 var startbutton = modalSelection.select('.content')
80731                     .attr('class', 'fillL')
80732                     .append('button')
80733                         .attr('class', 'modal-section huge-modal-button')
80734                         .on('click', function() {
80735                             modalSelection.remove();
80736                         });
80737
80738                     startbutton
80739                         .append('svg')
80740                         .attr('class', 'illustration')
80741                         .append('use')
80742                         .attr('xlink:href', '#iD-logo-walkthrough');
80743
80744                     startbutton
80745                         .append('h2')
80746                         .text(_t('intro.startediting.start'));
80747
80748                 dispatch$1.call('startEditing');
80749             }
80750
80751
80752             chapter.enter = function() {
80753                 showHelp();
80754             };
80755
80756
80757             chapter.exit = function() {
80758                 modalSelection.remove();
80759                 context.container().selectAll('.shaded').remove();  // in case user opened keyboard shortcuts
80760             };
80761
80762
80763             return utilRebind(chapter, dispatch$1, 'on');
80764         }
80765
80766         const chapterUi = {
80767           welcome: uiIntroWelcome,
80768           navigation: uiIntroNavigation,
80769           point: uiIntroPoint,
80770           area: uiIntroArea,
80771           line: uiIntroLine,
80772           building: uiIntroBuilding,
80773           startEditing: uiIntroStartEditing
80774         };
80775
80776         const chapterFlow = [
80777           'welcome',
80778           'navigation',
80779           'point',
80780           'area',
80781           'line',
80782           'building',
80783           'startEditing'
80784         ];
80785
80786
80787         function uiIntro(context) {
80788           const INTRO_IMAGERY = 'EsriWorldImageryClarity';
80789           let _introGraph = {};
80790           let _currChapter;
80791
80792
80793           function intro(selection) {
80794             _mainFileFetcher.get('intro_graph')
80795               .then(dataIntroGraph => {
80796                 // create entities for intro graph and localize names
80797                 for (let id in dataIntroGraph) {
80798                   if (!_introGraph[id]) {
80799                     _introGraph[id] = osmEntity(localize(dataIntroGraph[id]));
80800                   }
80801                 }
80802                 selection.call(startIntro);
80803               })
80804               .catch(function() { /* ignore */ });
80805           }
80806
80807
80808           function startIntro(selection) {
80809             context.enter(modeBrowse(context));
80810
80811             // Save current map state
80812             let osm = context.connection();
80813             let history = context.history().toJSON();
80814             let hash = window.location.hash;
80815             let center = context.map().center();
80816             let zoom = context.map().zoom();
80817             let background = context.background().baseLayerSource();
80818             let overlays = context.background().overlayLayerSources();
80819             let opacity = context.container().selectAll('.main-map .layer-background').style('opacity');
80820             let caches = osm && osm.caches();
80821             let baseEntities = context.history().graph().base().entities;
80822
80823             // Show sidebar and disable the sidebar resizing button
80824             // (this needs to be before `context.inIntro(true)`)
80825             context.ui().sidebar.expand();
80826             context.container().selectAll('button.sidebar-toggle').classed('disabled', true);
80827
80828             // Block saving
80829             context.inIntro(true);
80830
80831             // Load semi-real data used in intro
80832             if (osm) { osm.toggle(false).reset(); }
80833             context.history().reset();
80834             context.history().merge(Object.values(coreGraph().load(_introGraph).entities));
80835             context.history().checkpoint('initial');
80836
80837             // Setup imagery
80838             let imagery = context.background().findSource(INTRO_IMAGERY);
80839             if (imagery) {
80840               context.background().baseLayerSource(imagery);
80841             } else {
80842               context.background().bing();
80843             }
80844             overlays.forEach(d => context.background().toggleOverlayLayer(d));
80845
80846             // Setup data layers (only OSM)
80847             let layers = context.layers();
80848             layers.all().forEach(item => {
80849               // if the layer has the function `enabled`
80850               if (typeof item.layer.enabled === 'function') {
80851                 item.layer.enabled(item.id === 'osm');
80852               }
80853             });
80854
80855
80856             context.container().selectAll('.main-map .layer-background').style('opacity', 1);
80857
80858             let curtain = uiCurtain(context.container().node());
80859             selection.call(curtain);
80860
80861             // Store that the user started the walkthrough..
80862             corePreferences('walkthrough_started', 'yes');
80863
80864             // Restore previous walkthrough progress..
80865             let storedProgress = corePreferences('walkthrough_progress') || '';
80866             let progress = storedProgress.split(';').filter(Boolean);
80867
80868             let chapters = chapterFlow.map((chapter, i) => {
80869               let s = chapterUi[chapter](context, curtain.reveal)
80870                 .on('done', () => {
80871
80872                   buttons
80873                     .filter(d => d.title === s.title)
80874                     .classed('finished', true);
80875
80876                   if (i < chapterFlow.length - 1) {
80877                     const next = chapterFlow[i + 1];
80878                     context.container().select(`button.chapter-${next}`)
80879                       .classed('next', true);
80880                   }
80881
80882                   // Store walkthrough progress..
80883                   progress.push(chapter);
80884                   corePreferences('walkthrough_progress', utilArrayUniq(progress).join(';'));
80885                 });
80886               return s;
80887             });
80888
80889             chapters[chapters.length - 1].on('startEditing', () => {
80890               // Store walkthrough progress..
80891               progress.push('startEditing');
80892               corePreferences('walkthrough_progress', utilArrayUniq(progress).join(';'));
80893
80894               // Store if walkthrough is completed..
80895               let incomplete = utilArrayDifference(chapterFlow, progress);
80896               if (!incomplete.length) {
80897                 corePreferences('walkthrough_completed', 'yes');
80898               }
80899
80900               curtain.remove();
80901               navwrap.remove();
80902               context.container().selectAll('.main-map .layer-background').style('opacity', opacity);
80903               context.container().selectAll('button.sidebar-toggle').classed('disabled', false);
80904               if (osm) { osm.toggle(true).reset().caches(caches); }
80905               context.history().reset().merge(Object.values(baseEntities));
80906               context.background().baseLayerSource(background);
80907               overlays.forEach(d => context.background().toggleOverlayLayer(d));
80908               if (history) { context.history().fromJSON(history, false); }
80909               context.map().centerZoom(center, zoom);
80910               window.location.replace(hash);
80911               context.inIntro(false);
80912             });
80913
80914             let navwrap = selection
80915               .append('div')
80916               .attr('class', 'intro-nav-wrap fillD');
80917
80918             navwrap
80919               .append('svg')
80920               .attr('class', 'intro-nav-wrap-logo')
80921               .append('use')
80922               .attr('xlink:href', '#iD-logo-walkthrough');
80923
80924             let buttonwrap = navwrap
80925               .append('div')
80926               .attr('class', 'joined')
80927               .selectAll('button.chapter');
80928
80929             let buttons = buttonwrap
80930               .data(chapters)
80931               .enter()
80932               .append('button')
80933               .attr('class', (d, i) => `chapter chapter-${chapterFlow[i]}`)
80934               .on('click', enterChapter);
80935
80936             buttons
80937               .append('span')
80938               .text(d => _t(d.title));
80939
80940             buttons
80941               .append('span')
80942               .attr('class', 'status')
80943               .call(svgIcon((_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward'), 'inline'));
80944
80945             enterChapter(chapters[0]);
80946
80947
80948             function enterChapter(newChapter) {
80949               if (_currChapter) { _currChapter.exit(); }
80950               context.enter(modeBrowse(context));
80951
80952               _currChapter = newChapter;
80953               _currChapter.enter();
80954
80955               buttons
80956                 .classed('next', false)
80957                 .classed('active', d => d.title === _currChapter.title);
80958             }
80959           }
80960
80961
80962           return intro;
80963         }
80964
80965         function uiIssuesInfo(context) {
80966
80967             var warningsItem = {
80968                 id: 'warnings',
80969                 count: 0,
80970                 iconID: 'iD-icon-alert',
80971                 descriptionID: 'issues.warnings_and_errors'
80972             };
80973
80974             var resolvedItem = {
80975                 id: 'resolved',
80976                 count: 0,
80977                 iconID: 'iD-icon-apply',
80978                 descriptionID: 'issues.user_resolved_issues'
80979             };
80980
80981             function update(selection) {
80982
80983                 var shownItems = [];
80984
80985                 var liveIssues = context.validator().getIssues({
80986                     what: corePreferences('validate-what') || 'edited',
80987                     where: corePreferences('validate-where') || 'all'
80988                 });
80989                 if (liveIssues.length) {
80990                     warningsItem.count = liveIssues.length;
80991                     shownItems.push(warningsItem);
80992                 }
80993
80994                 if (corePreferences('validate-what') === 'all') {
80995                     var resolvedIssues = context.validator().getResolvedIssues();
80996                     if (resolvedIssues.length) {
80997                         resolvedItem.count = resolvedIssues.length;
80998                         shownItems.push(resolvedItem);
80999                     }
81000                 }
81001
81002                 var chips = selection.selectAll('.chip')
81003                     .data(shownItems, function(d) {
81004                         return d.id;
81005                     });
81006
81007                 chips.exit().remove();
81008
81009                 var enter = chips.enter()
81010                     .append('a')
81011                     .attr('class', function(d) {
81012                         return 'chip ' + d.id + '-count';
81013                     })
81014                     .attr('href', '#')
81015                     .attr('tabindex', -1)
81016                     .each(function(d) {
81017
81018                         var chipSelection = select(this);
81019
81020                         var tooltipBehavior = uiTooltip()
81021                             .placement('top')
81022                             .title(_t(d.descriptionID));
81023
81024                         chipSelection
81025                             .call(tooltipBehavior)
81026                             .on('click', function() {
81027                                 event.preventDefault();
81028
81029                                 tooltipBehavior.hide(select(this));
81030                                 // open the Issues pane
81031                                 context.ui().togglePanes(context.container().select('.map-panes .issues-pane'));
81032                             });
81033
81034                         chipSelection.call(svgIcon('#' + d.iconID));
81035
81036                     });
81037
81038                 enter.append('span')
81039                     .attr('class', 'count');
81040
81041                 enter.merge(chips)
81042                     .selectAll('span.count')
81043                     .text(function(d) {
81044                         return d.count.toString();
81045                     });
81046             }
81047
81048
81049             return function(selection) {
81050                 update(selection);
81051
81052                 context.validator().on('validated.infobox', function() {
81053                     update(selection);
81054                 });
81055             };
81056         }
81057
81058         // import { utilGetDimensions } from '../util/dimensions';
81059
81060
81061         function uiMapInMap(context) {
81062
81063             function mapInMap(selection) {
81064                 var backgroundLayer = rendererTileLayer(context);
81065                 var overlayLayers = {};
81066                 var projection = geoRawMercator();
81067                 var dataLayer = svgData(projection, context).showLabels(false);
81068                 var debugLayer = svgDebug(projection, context);
81069                 var zoom = d3_zoom()
81070                     .scaleExtent([geoZoomToScale(0.5), geoZoomToScale(24)])
81071                     .on('start', zoomStarted)
81072                     .on('zoom', zoomed)
81073                     .on('end', zoomEnded);
81074
81075                 var wrap = select(null);
81076                 var tiles = select(null);
81077                 var viewport = select(null);
81078
81079                 var _isTransformed = false;
81080                 var _isHidden = true;
81081                 var _skipEvents = false;
81082                 var _gesture = null;
81083                 var _zDiff = 6;    // by default, minimap renders at (main zoom - 6)
81084                 var _dMini;        // dimensions of minimap
81085                 var _cMini;        // center pixel of minimap
81086                 var _tStart;       // transform at start of gesture
81087                 var _tCurr;        // transform at most recent event
81088                 var _timeoutID;
81089
81090
81091                 function zoomStarted() {
81092                     if (_skipEvents) return;
81093                     _tStart = _tCurr = projection.transform();
81094                     _gesture = null;
81095                 }
81096
81097
81098                 function zoomed() {
81099                     if (_skipEvents) return;
81100
81101                     var x = event.transform.x;
81102                     var y = event.transform.y;
81103                     var k = event.transform.k;
81104                     var isZooming = (k !== _tStart.k);
81105                     var isPanning = (x !== _tStart.x || y !== _tStart.y);
81106
81107                     if (!isZooming && !isPanning) {
81108                         return;  // no change
81109                     }
81110
81111                     // lock in either zooming or panning, don't allow both in minimap.
81112                     if (!_gesture) {
81113                         _gesture = isZooming ? 'zoom' : 'pan';
81114                     }
81115
81116                     var tMini = projection.transform();
81117                     var tX, tY, scale;
81118
81119                     if (_gesture === 'zoom') {
81120                         scale = k / tMini.k;
81121                         tX = (_cMini[0] / scale - _cMini[0]) * scale;
81122                         tY = (_cMini[1] / scale - _cMini[1]) * scale;
81123                     } else {
81124                         k = tMini.k;
81125                         scale = 1;
81126                         tX = x - tMini.x;
81127                         tY = y - tMini.y;
81128                     }
81129
81130                     utilSetTransform(tiles, tX, tY, scale);
81131                     utilSetTransform(viewport, 0, 0, scale);
81132                     _isTransformed = true;
81133                     _tCurr = identity$2.translate(x, y).scale(k);
81134
81135                     var zMain = geoScaleToZoom(context.projection.scale());
81136                     var zMini = geoScaleToZoom(k);
81137
81138                     _zDiff = zMain - zMini;
81139
81140                     queueRedraw();
81141                 }
81142
81143
81144                 function zoomEnded() {
81145                     if (_skipEvents) return;
81146                     if (_gesture !== 'pan') return;
81147
81148                     updateProjection();
81149                     _gesture = null;
81150                     context.map().center(projection.invert(_cMini));   // recenter main map..
81151                 }
81152
81153
81154                 function updateProjection() {
81155                     var loc = context.map().center();
81156                     var tMain = context.projection.transform();
81157                     var zMain = geoScaleToZoom(tMain.k);
81158                     var zMini = Math.max(zMain - _zDiff, 0.5);
81159                     var kMini = geoZoomToScale(zMini);
81160
81161                     projection
81162                         .translate([tMain.x, tMain.y])
81163                         .scale(kMini);
81164
81165                     var point = projection(loc);
81166                     var mouse = (_gesture === 'pan') ? geoVecSubtract([_tCurr.x, _tCurr.y], [_tStart.x, _tStart.y]) : [0, 0];
81167                     var xMini = _cMini[0] - point[0] + tMain.x + mouse[0];
81168                     var yMini = _cMini[1] - point[1] + tMain.y + mouse[1];
81169
81170                     projection
81171                         .translate([xMini, yMini])
81172                         .clipExtent([[0, 0], _dMini]);
81173
81174                     _tCurr = projection.transform();
81175
81176                     if (_isTransformed) {
81177                         utilSetTransform(tiles, 0, 0);
81178                         utilSetTransform(viewport, 0, 0);
81179                         _isTransformed = false;
81180                     }
81181
81182                     zoom
81183                         .scaleExtent([geoZoomToScale(0.5), geoZoomToScale(zMain - 3)]);
81184
81185                     _skipEvents = true;
81186                     wrap.call(zoom.transform, _tCurr);
81187                     _skipEvents = false;
81188                 }
81189
81190
81191                 function redraw() {
81192                     clearTimeout(_timeoutID);
81193                     if (_isHidden) return;
81194
81195                     updateProjection();
81196                     var zMini = geoScaleToZoom(projection.scale());
81197
81198                     // setup tile container
81199                     tiles = wrap
81200                         .selectAll('.map-in-map-tiles')
81201                         .data([0]);
81202
81203                     tiles = tiles.enter()
81204                         .append('div')
81205                         .attr('class', 'map-in-map-tiles')
81206                         .merge(tiles);
81207
81208                     // redraw background
81209                     backgroundLayer
81210                         .source(context.background().baseLayerSource())
81211                         .projection(projection)
81212                         .dimensions(_dMini);
81213
81214                     var background = tiles
81215                         .selectAll('.map-in-map-background')
81216                         .data([0]);
81217
81218                     background.enter()
81219                         .append('div')
81220                         .attr('class', 'map-in-map-background')
81221                         .merge(background)
81222                         .call(backgroundLayer);
81223
81224
81225                     // redraw overlay
81226                     var overlaySources = context.background().overlayLayerSources();
81227                     var activeOverlayLayers = [];
81228                     for (var i = 0; i < overlaySources.length; i++) {
81229                         if (overlaySources[i].validZoom(zMini)) {
81230                             if (!overlayLayers[i]) overlayLayers[i] = rendererTileLayer(context);
81231                             activeOverlayLayers.push(overlayLayers[i]
81232                                 .source(overlaySources[i])
81233                                 .projection(projection)
81234                                 .dimensions(_dMini));
81235                         }
81236                     }
81237
81238                     var overlay = tiles
81239                         .selectAll('.map-in-map-overlay')
81240                         .data([0]);
81241
81242                     overlay = overlay.enter()
81243                         .append('div')
81244                         .attr('class', 'map-in-map-overlay')
81245                         .merge(overlay);
81246
81247
81248                     var overlays = overlay
81249                         .selectAll('div')
81250                         .data(activeOverlayLayers, function(d) { return d.source().name(); });
81251
81252                     overlays.exit()
81253                         .remove();
81254
81255                     overlays = overlays.enter()
81256                         .append('div')
81257                         .merge(overlays)
81258                         .each(function(layer) { select(this).call(layer); });
81259
81260
81261                     var dataLayers = tiles
81262                         .selectAll('.map-in-map-data')
81263                         .data([0]);
81264
81265                     dataLayers.exit()
81266                         .remove();
81267
81268                     dataLayers = dataLayers.enter()
81269                         .append('svg')
81270                         .attr('class', 'map-in-map-data')
81271                         .merge(dataLayers)
81272                         .call(dataLayer)
81273                         .call(debugLayer);
81274
81275
81276                     // redraw viewport bounding box
81277                     if (_gesture !== 'pan') {
81278                         var getPath = d3_geoPath(projection);
81279                         var bbox = { type: 'Polygon', coordinates: [context.map().extent().polygon()] };
81280
81281                         viewport = wrap.selectAll('.map-in-map-viewport')
81282                             .data([0]);
81283
81284                         viewport = viewport.enter()
81285                             .append('svg')
81286                             .attr('class', 'map-in-map-viewport')
81287                             .merge(viewport);
81288
81289
81290                         var path = viewport.selectAll('.map-in-map-bbox')
81291                             .data([bbox]);
81292
81293                         path.enter()
81294                             .append('path')
81295                             .attr('class', 'map-in-map-bbox')
81296                             .merge(path)
81297                             .attr('d', getPath)
81298                             .classed('thick', function(d) { return getPath.area(d) < 30; });
81299                     }
81300                 }
81301
81302
81303                 function queueRedraw() {
81304                     clearTimeout(_timeoutID);
81305                     _timeoutID = setTimeout(function() { redraw(); }, 750);
81306                 }
81307
81308
81309                 function toggle() {
81310                     if (event) event.preventDefault();
81311
81312                     _isHidden = !_isHidden;
81313
81314                     context.container().select('.minimap-toggle-item')
81315                         .classed('active', !_isHidden)
81316                         .select('input')
81317                         .property('checked', !_isHidden);
81318
81319                     if (_isHidden) {
81320                         wrap
81321                             .style('display', 'block')
81322                             .style('opacity', '1')
81323                             .transition()
81324                             .duration(200)
81325                             .style('opacity', '0')
81326                             .on('end', function() {
81327                                 selection.selectAll('.map-in-map')
81328                                     .style('display', 'none');
81329                             });
81330                     } else {
81331                         wrap
81332                             .style('display', 'block')
81333                             .style('opacity', '0')
81334                             .transition()
81335                             .duration(200)
81336                             .style('opacity', '1')
81337                             .on('end', function() {
81338                                 redraw();
81339                             });
81340                     }
81341                 }
81342
81343
81344                 uiMapInMap.toggle = toggle;
81345
81346                 wrap = selection.selectAll('.map-in-map')
81347                     .data([0]);
81348
81349                 wrap = wrap.enter()
81350                     .append('div')
81351                     .attr('class', 'map-in-map')
81352                     .style('display', (_isHidden ? 'none' : 'block'))
81353                     .call(zoom)
81354                     .on('dblclick.zoom', null)
81355                     .merge(wrap);
81356
81357                 // reflow warning: Hardcode dimensions - currently can't resize it anyway..
81358                 _dMini = [200,150]; //utilGetDimensions(wrap);
81359                 _cMini = geoVecScale(_dMini, 0.5);
81360
81361                 context.map()
81362                     .on('drawn.map-in-map', function(drawn) {
81363                         if (drawn.full === true) {
81364                             redraw();
81365                         }
81366                     });
81367
81368                 redraw();
81369
81370                 context.keybinding()
81371                     .on(_t('background.minimap.key'), toggle);
81372             }
81373
81374             return mapInMap;
81375         }
81376
81377         function uiNotice(context) {
81378
81379             return function(selection) {
81380                 var div = selection
81381                     .append('div')
81382                     .attr('class', 'notice');
81383
81384                 var button = div
81385                     .append('button')
81386                     .attr('class', 'zoom-to notice fillD')
81387                     .on('click', function() {
81388                         context.map().zoomEase(context.minEditableZoom());
81389                     })
81390                     .on('wheel', function() {   // let wheel events pass through #4482
81391                         var e2 = new WheelEvent(event.type, event);
81392                         context.surface().node().dispatchEvent(e2);
81393                     });
81394
81395                 button
81396                     .call(svgIcon('#iD-icon-plus', 'pre-text'))
81397                     .append('span')
81398                     .attr('class', 'label')
81399                     .text(_t('zoom_in_edit'));
81400
81401
81402                 function disableTooHigh() {
81403                     var canEdit = context.map().zoom() >= context.minEditableZoom();
81404                     div.style('display', canEdit ? 'none' : 'block');
81405                 }
81406
81407                 context.map()
81408                     .on('move.notice', debounce(disableTooHigh, 500));
81409
81410                 disableTooHigh();
81411             };
81412         }
81413
81414         function uiPhotoviewer(context) {
81415
81416             var dispatch$1 = dispatch('resize');
81417
81418             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
81419
81420             function photoviewer(selection) {
81421                 selection
81422                     .append('button')
81423                     .attr('class', 'thumb-hide')
81424                     .on('click', function () {
81425                         if (services.streetside) { services.streetside.hideViewer(context); }
81426                         if (services.mapillary) { services.mapillary.hideViewer(context); }
81427                         if (services.openstreetcam) { services.openstreetcam.hideViewer(context); }
81428                     })
81429                     .append('div')
81430                     .call(svgIcon('#iD-icon-close'));
81431
81432                 function preventDefault() {
81433                     event.preventDefault();
81434                 }
81435
81436                 selection
81437                     .append('button')
81438                     .attr('class', 'resize-handle-xy')
81439                     .on('touchstart touchdown touchend', preventDefault)
81440                     .on(
81441                         _pointerPrefix + 'down',
81442                         buildResizeListener(selection, 'resize', dispatch$1, { resizeOnX: true, resizeOnY: true })
81443                     );
81444
81445                 selection
81446                     .append('button')
81447                     .attr('class', 'resize-handle-x')
81448                     .on('touchstart touchdown touchend', preventDefault)
81449                     .on(
81450                         _pointerPrefix + 'down',
81451                         buildResizeListener(selection, 'resize', dispatch$1, { resizeOnX: true })
81452                     );
81453
81454                 selection
81455                     .append('button')
81456                     .attr('class', 'resize-handle-y')
81457                     .on('touchstart touchdown touchend', preventDefault)
81458                     .on(
81459                         _pointerPrefix + 'down',
81460                         buildResizeListener(selection, 'resize', dispatch$1, { resizeOnY: true })
81461                     );
81462
81463                 services.streetside.loadViewer(context);
81464                 services.mapillary.loadViewer(context);
81465                 services.openstreetcam.loadViewer(context);
81466
81467                 function buildResizeListener(target, eventName, dispatch, options) {
81468
81469                     var resizeOnX = !!options.resizeOnX;
81470                     var resizeOnY = !!options.resizeOnY;
81471                     var minHeight = options.minHeight || 240;
81472                     var minWidth = options.minWidth || 320;
81473                     var pointerId;
81474                     var startX;
81475                     var startY;
81476                     var startWidth;
81477                     var startHeight;
81478
81479                     function startResize() {
81480                         if (pointerId !== (event.pointerId || 'mouse')) return;
81481
81482                         event.preventDefault();
81483                         event.stopPropagation();
81484
81485                         var mapSize = context.map().dimensions();
81486
81487                         if (resizeOnX) {
81488                             var maxWidth = mapSize[0];
81489                             var newWidth = clamp((startWidth + event.clientX - startX), minWidth, maxWidth);
81490                             target.style('width', newWidth + 'px');
81491                         }
81492
81493                         if (resizeOnY) {
81494                             var maxHeight = mapSize[1] - 90;  // preserve space at top/bottom of map
81495                             var newHeight = clamp((startHeight + startY - event.clientY), minHeight, maxHeight);
81496                             target.style('height', newHeight + 'px');
81497                         }
81498
81499                         dispatch.call(eventName, target, utilGetDimensions(target, true));
81500                     }
81501
81502                     function clamp(num, min, max) {
81503                         return Math.max(min, Math.min(num, max));
81504                     }
81505
81506                     function stopResize() {
81507                         if (pointerId !== (event.pointerId || 'mouse')) return;
81508
81509                         event.preventDefault();
81510                         event.stopPropagation();
81511
81512                         // remove all the listeners we added
81513                         select(window)
81514                             .on('.' + eventName, null);
81515                     }
81516
81517                     return function initResize() {
81518                         event.preventDefault();
81519                         event.stopPropagation();
81520
81521                         pointerId = event.pointerId || 'mouse';
81522
81523                         startX = event.clientX;
81524                         startY = event.clientY;
81525                         var targetRect = target.node().getBoundingClientRect();
81526                         startWidth = targetRect.width;
81527                         startHeight = targetRect.height;
81528
81529                         select(window)
81530                             .on(_pointerPrefix + 'move.' + eventName, startResize, false)
81531                             .on(_pointerPrefix + 'up.' + eventName, stopResize, false);
81532
81533                         if (_pointerPrefix === 'pointer') {
81534                             select(window)
81535                                 .on('pointercancel.' + eventName, stopResize, false);
81536                         }
81537                     };
81538                 }
81539             }
81540
81541             photoviewer.onMapResize = function() {
81542                 var photoviewer = context.container().select('.photoviewer');
81543                 var content = context.container().select('.main-content');
81544                 var mapDimensions = utilGetDimensions(content, true);
81545                 // shrink photo viewer if it is too big
81546                 // (-90 preserves space at top and bottom of map used by menus)
81547                 var photoDimensions = utilGetDimensions(photoviewer, true);
81548                 if (photoDimensions[0] > mapDimensions[0] || photoDimensions[1] > (mapDimensions[1] - 90)) {
81549                     var setPhotoDimensions = [
81550                         Math.min(photoDimensions[0], mapDimensions[0]),
81551                         Math.min(photoDimensions[1], mapDimensions[1] - 90),
81552                     ];
81553
81554                     photoviewer
81555                         .style('width', setPhotoDimensions[0] + 'px')
81556                         .style('height', setPhotoDimensions[1] + 'px');
81557
81558                     dispatch$1.call('resize', photoviewer, setPhotoDimensions);
81559                 }
81560             };
81561
81562             return utilRebind(photoviewer, dispatch$1, 'on');
81563         }
81564
81565         function uiRestore(context) {
81566           return function(selection) {
81567             if (!context.history().hasRestorableChanges()) return;
81568
81569             let modalSelection = uiModal(selection, true);
81570
81571             modalSelection.select('.modal')
81572               .attr('class', 'modal fillL');
81573
81574             let introModal = modalSelection.select('.content');
81575
81576             introModal
81577               .append('div')
81578               .attr('class', 'modal-section')
81579               .append('h3')
81580               .text(_t('restore.heading'));
81581
81582             introModal
81583               .append('div')
81584               .attr('class','modal-section')
81585               .append('p')
81586               .text(_t('restore.description'));
81587
81588             let buttonWrap = introModal
81589               .append('div')
81590               .attr('class', 'modal-actions');
81591
81592             let restore = buttonWrap
81593               .append('button')
81594               .attr('class', 'restore')
81595               .on('click', () => {
81596                 context.history().restore();
81597                 modalSelection.remove();
81598               });
81599
81600             restore
81601               .append('svg')
81602               .attr('class', 'logo logo-restore')
81603               .append('use')
81604               .attr('xlink:href', '#iD-logo-restore');
81605
81606             restore
81607               .append('div')
81608               .text(_t('restore.restore'));
81609
81610             let reset = buttonWrap
81611               .append('button')
81612               .attr('class', 'reset')
81613               .on('click', () => {
81614                 context.history().clearSaved();
81615                 modalSelection.remove();
81616               });
81617
81618             reset
81619               .append('svg')
81620               .attr('class', 'logo logo-reset')
81621               .append('use')
81622               .attr('xlink:href', '#iD-logo-reset');
81623
81624             reset
81625               .append('div')
81626               .text(_t('restore.reset'));
81627
81628             restore.node().focus();
81629           };
81630         }
81631
81632         function uiScale(context) {
81633             var projection = context.projection,
81634                 isImperial = !_mainLocalizer.usesMetric(),
81635                 maxLength = 180,
81636                 tickHeight = 8;
81637
81638
81639             function scaleDefs(loc1, loc2) {
81640                 var lat = (loc2[1] + loc1[1]) / 2,
81641                     conversion = (isImperial ? 3.28084 : 1),
81642                     dist = geoLonToMeters(loc2[0] - loc1[0], lat) * conversion,
81643                     scale = { dist: 0, px: 0, text: '' },
81644                     buckets, i, val, dLon;
81645
81646                 if (isImperial) {
81647                     buckets = [5280000, 528000, 52800, 5280, 500, 50, 5, 1];
81648                 } else {
81649                     buckets = [5000000, 500000, 50000, 5000, 500, 50, 5, 1];
81650                 }
81651
81652                 // determine a user-friendly endpoint for the scale
81653                 for (i = 0; i < buckets.length; i++) {
81654                     val = buckets[i];
81655                     if (dist >= val) {
81656                         scale.dist = Math.floor(dist / val) * val;
81657                         break;
81658                     } else {
81659                         scale.dist = +dist.toFixed(2);
81660                     }
81661                 }
81662
81663                 dLon = geoMetersToLon(scale.dist / conversion, lat);
81664                 scale.px = Math.round(projection([loc1[0] + dLon, loc1[1]])[0]);
81665
81666                 scale.text = displayLength(scale.dist / conversion, isImperial);
81667
81668                 return scale;
81669             }
81670
81671
81672             function update(selection) {
81673                 // choose loc1, loc2 along bottom of viewport (near where the scale will be drawn)
81674                 var dims = context.map().dimensions(),
81675                     loc1 = projection.invert([0, dims[1]]),
81676                     loc2 = projection.invert([maxLength, dims[1]]),
81677                     scale = scaleDefs(loc1, loc2);
81678
81679                 selection.select('.scale-path')
81680                     .attr('d', 'M0.5,0.5v' + tickHeight + 'h' + scale.px + 'v-' + tickHeight);
81681
81682                 selection.select('.scale-textgroup')
81683                     .attr('transform', 'translate(' + (scale.px + 8) + ',' + tickHeight + ')');
81684
81685                 selection.select('.scale-text')
81686                     .text(scale.text);
81687             }
81688
81689
81690             return function(selection) {
81691                 function switchUnits() {
81692                     isImperial = !isImperial;
81693                     selection.call(update);
81694                 }
81695
81696                 var scalegroup = selection.append('svg')
81697                     .attr('class', 'scale')
81698                     .on('click', switchUnits)
81699                     .append('g')
81700                     .attr('transform', 'translate(10,11)');
81701
81702                 scalegroup
81703                     .append('path')
81704                     .attr('class', 'scale-path');
81705
81706                 scalegroup
81707                     .append('g')
81708                     .attr('class', 'scale-textgroup')
81709                     .append('text')
81710                     .attr('class', 'scale-text');
81711
81712                 selection.call(update);
81713
81714                 context.map().on('move.scale', function() {
81715                     update(selection);
81716                 });
81717             };
81718         }
81719
81720         function uiShortcuts(context) {
81721             var detected = utilDetect();
81722             var _activeTab = 0;
81723             var _modalSelection;
81724             var _selection = select(null);
81725
81726
81727             context.keybinding()
81728                 .on([_t('shortcuts.toggle.key'), '?'], function () {
81729                     if (context.container().selectAll('.modal-shortcuts').size()) {  // already showing
81730                         if (_modalSelection) {
81731                             _modalSelection.close();
81732                             _modalSelection = null;
81733                         }
81734                     } else {
81735                         _modalSelection = uiModal(_selection);
81736                         _modalSelection.call(shortcutsModal);
81737                     }
81738                 });
81739
81740
81741             function shortcutsModal(_modalSelection) {
81742                 _modalSelection.select('.modal')
81743                     .classed('modal-shortcuts', true);
81744
81745                 var content = _modalSelection.select('.content');
81746
81747                 content
81748                     .append('div')
81749                     .attr('class', 'modal-section')
81750                     .append('h3')
81751                     .text(_t('shortcuts.title'));
81752
81753                 _mainFileFetcher.get('shortcuts')
81754                     .then(function(data) { content.call(render, data); })
81755                     .catch(function() { /* ignore */ });
81756             }
81757
81758
81759             function render(selection, dataShortcuts) {
81760                 var wrapper = selection
81761                     .selectAll('.wrapper')
81762                     .data([0]);
81763
81764                 var wrapperEnter = wrapper
81765                     .enter()
81766                     .append('div')
81767                     .attr('class', 'wrapper modal-section');
81768
81769                 var tabsBar = wrapperEnter
81770                     .append('div')
81771                     .attr('class', 'tabs-bar');
81772
81773                 var shortcutsList = wrapperEnter
81774                     .append('div')
81775                     .attr('class', 'shortcuts-list');
81776
81777                 wrapper = wrapper.merge(wrapperEnter);
81778
81779                 var tabs = tabsBar
81780                     .selectAll('.tab')
81781                     .data(dataShortcuts);
81782
81783                 var tabsEnter = tabs
81784                     .enter()
81785                     .append('div')
81786                     .attr('class', 'tab')
81787                     .on('click', function (d, i) {
81788                         _activeTab = i;
81789                         render(selection, dataShortcuts);
81790                     });
81791
81792                 tabsEnter
81793                     .append('span')
81794                     .text(function (d) { return _t(d.text); });
81795
81796                 tabs = tabs
81797                     .merge(tabsEnter);
81798
81799                 // Update
81800                 wrapper.selectAll('.tab')
81801                     .classed('active', function (d, i) {
81802                         return i === _activeTab;
81803                     });
81804
81805
81806                 var shortcuts = shortcutsList
81807                     .selectAll('.shortcut-tab')
81808                     .data(dataShortcuts);
81809
81810                 var shortcutsEnter = shortcuts
81811                     .enter()
81812                     .append('div')
81813                     .attr('class', function(d) { return 'shortcut-tab shortcut-tab-' + d.tab; });
81814
81815                 var columnsEnter = shortcutsEnter
81816                     .selectAll('.shortcut-column')
81817                     .data(function (d) { return d.columns; })
81818                     .enter()
81819                     .append('table')
81820                     .attr('class', 'shortcut-column');
81821
81822                 var rowsEnter = columnsEnter
81823                     .selectAll('.shortcut-row')
81824                     .data(function (d) { return d.rows; })
81825                     .enter()
81826                     .append('tr')
81827                     .attr('class', 'shortcut-row');
81828
81829
81830                 var sectionRows = rowsEnter
81831                     .filter(function (d) { return !d.shortcuts; });
81832
81833                 sectionRows
81834                     .append('td');
81835
81836                 sectionRows
81837                     .append('td')
81838                     .attr('class', 'shortcut-section')
81839                     .append('h3')
81840                     .text(function (d) { return _t(d.text); });
81841
81842
81843                 var shortcutRows = rowsEnter
81844                     .filter(function (d) { return d.shortcuts; });
81845
81846                 var shortcutKeys = shortcutRows
81847                     .append('td')
81848                     .attr('class', 'shortcut-keys');
81849
81850                 var modifierKeys = shortcutKeys
81851                     .filter(function (d) { return d.modifiers; });
81852
81853                 modifierKeys
81854                     .selectAll('kbd.modifier')
81855                     .data(function (d) {
81856                         if (detected.os === 'win' && d.text === 'shortcuts.editing.commands.redo') {
81857                             return ['⌘'];
81858                         } else if (detected.os !== 'mac' && d.text === 'shortcuts.browsing.display_options.fullscreen') {
81859                             return [];
81860                         } else {
81861                             return d.modifiers;
81862                         }
81863                     })
81864                     .enter()
81865                     .each(function () {
81866                         var selection = select(this);
81867
81868                         selection
81869                             .append('kbd')
81870                             .attr('class', 'modifier')
81871                             .text(function (d) { return uiCmd.display(d); });
81872
81873                         selection
81874                             .append('span')
81875                             .text('+');
81876                     });
81877
81878
81879                 shortcutKeys
81880                     .selectAll('kbd.shortcut')
81881                     .data(function (d) {
81882                         var arr = d.shortcuts;
81883                         if (detected.os === 'win' && d.text === 'shortcuts.editing.commands.redo') {
81884                             arr = ['Y'];
81885                         } else if (detected.os !== 'mac' && d.text === 'shortcuts.browsing.display_options.fullscreen') {
81886                             arr = ['F11'];
81887                         }
81888
81889                         // replace translations
81890                         arr = arr.map(function(s) {
81891                             return uiCmd.display(s.indexOf('.') !== -1 ? _t(s) : s);
81892                         });
81893
81894                         return utilArrayUniq(arr).map(function(s) {
81895                             return {
81896                                 shortcut: s,
81897                                 separator: d.separator,
81898                                 suffix: d.suffix
81899                             };
81900                         });
81901                     })
81902                     .enter()
81903                     .each(function (d, i, nodes) {
81904                         var selection = select(this);
81905                         var click = d.shortcut.toLowerCase().match(/(.*).click/);
81906
81907                         if (click && click[1]) {   // replace "left_click", "right_click" with mouse icon
81908                             selection
81909                                 .call(svgIcon('#iD-walkthrough-mouse-' + click[1], 'operation'));
81910                         } else if (d.shortcut.toLowerCase() === 'long-press') {
81911                             selection
81912                                 .call(svgIcon('#iD-walkthrough-longpress', 'longpress operation'));
81913                         } else if (d.shortcut.toLowerCase() === 'tap') {
81914                             selection
81915                                 .call(svgIcon('#iD-walkthrough-tap', 'tap operation'));
81916                         } else {
81917                             selection
81918                                 .append('kbd')
81919                                 .attr('class', 'shortcut')
81920                                 .text(function (d) { return d.shortcut; });
81921                         }
81922
81923                         if (i < nodes.length - 1) {
81924                             selection
81925                                 .append('span')
81926                                 .text(d.separator || '\u00a0' + _t('shortcuts.or') + '\u00a0');
81927                         } else if (i === nodes.length - 1 && d.suffix) {
81928                             selection
81929                                 .append('span')
81930                                 .text(d.suffix);
81931                         }
81932                     });
81933
81934
81935                 shortcutKeys
81936                     .filter(function(d) { return d.gesture; })
81937                     .each(function () {
81938                         var selection = select(this);
81939
81940                         selection
81941                             .append('span')
81942                             .text('+');
81943
81944                         selection
81945                             .append('span')
81946                             .attr('class', 'gesture')
81947                             .text(function (d) { return _t(d.gesture); });
81948                     });
81949
81950
81951                 shortcutRows
81952                     .append('td')
81953                     .attr('class', 'shortcut-desc')
81954                     .text(function (d) { return d.text ? _t(d.text) : '\u00a0'; });
81955
81956
81957                 shortcuts = shortcuts
81958                     .merge(shortcutsEnter);
81959
81960                 // Update
81961                 wrapper.selectAll('.shortcut-tab')
81962                     .style('display', function (d, i) {
81963                         return i === _activeTab ? 'flex' : 'none';
81964                     });
81965             }
81966
81967
81968             return function(selection, show) {
81969                 _selection = selection;
81970                 if (show) {
81971                     _modalSelection = uiModal(selection);
81972                     _modalSelection.call(shortcutsModal);
81973                 }
81974             };
81975         }
81976
81977         var pair_1 = pair;
81978
81979
81980         function search(input, dims) {
81981           if (!dims) dims = 'NSEW';
81982           if (typeof input !== 'string') return null;
81983
81984           input = input.toUpperCase();
81985           var regex = /^[\s\,]*([NSEW])?\s*([\-|\—|\―]?[0-9.]+)[°º˚]?\s*(?:([0-9.]+)['’′‘]\s*)?(?:([0-9.]+)(?:''|"|”|″)\s*)?([NSEW])?/;
81986
81987           var m = input.match(regex);
81988           if (!m) return null;  // no match
81989
81990           var matched = m[0];
81991
81992           // extract dimension.. m[1] = leading, m[5] = trailing
81993           var dim;
81994           if (m[1] && m[5]) {                 // if matched both..
81995             dim = m[1];                       // keep leading
81996             matched = matched.slice(0, -1);   // remove trailing dimension from match
81997           } else {
81998             dim = m[1] || m[5];
81999           }
82000
82001           // if unrecognized dimension
82002           if (dim && dims.indexOf(dim) === -1) return null;
82003
82004           // extract DMS
82005           var deg = m[2] ? parseFloat(m[2]) : 0;
82006           var min = m[3] ? parseFloat(m[3]) / 60 : 0;
82007           var sec = m[4] ? parseFloat(m[4]) / 3600 : 0;
82008           var sign = (deg < 0) ? -1 : 1;
82009           if (dim === 'S' || dim === 'W') sign *= -1;
82010
82011           return {
82012             val: (Math.abs(deg) + min + sec) * sign,
82013             dim: dim,
82014             matched: matched,
82015             remain: input.slice(matched.length)
82016           };
82017         }
82018
82019
82020         function pair(input, dims) {
82021           input = input.trim();
82022           var one = search(input, dims);
82023           if (!one) return null;
82024
82025           input = one.remain.trim();
82026           var two = search(input, dims);
82027           if (!two || two.remain) return null;
82028
82029           if (one.dim) {
82030             return swapdim(one.val, two.val, one.dim);
82031           } else {
82032             return [one.val, two.val];
82033           }
82034         }
82035
82036
82037         function swapdim(a, b, dim) {
82038           if (dim === 'N' || dim === 'S') return [a, b];
82039           if (dim === 'W' || dim === 'E') return [b, a];
82040         }
82041
82042         function uiFeatureList(context) {
82043             var _geocodeResults;
82044
82045
82046             function featureList(selection) {
82047                 var header = selection
82048                     .append('div')
82049                     .attr('class', 'header fillL cf');
82050
82051                 header
82052                     .append('h3')
82053                     .text(_t('inspector.feature_list'));
82054
82055                 var searchWrap = selection
82056                     .append('div')
82057                     .attr('class', 'search-header');
82058
82059                 var search = searchWrap
82060                     .append('input')
82061                     .attr('placeholder', _t('inspector.search'))
82062                     .attr('type', 'search')
82063                     .call(utilNoAuto)
82064                     .on('keypress', keypress)
82065                     .on('keydown', keydown)
82066                     .on('input', inputevent);
82067
82068                 searchWrap
82069                     .call(svgIcon('#iD-icon-search', 'pre-text'));
82070
82071                 var listWrap = selection
82072                     .append('div')
82073                     .attr('class', 'inspector-body');
82074
82075                 var list = listWrap
82076                     .append('div')
82077                     .attr('class', 'feature-list cf');
82078
82079                 context
82080                     .on('exit.feature-list', clearSearch);
82081                 context.map()
82082                     .on('drawn.feature-list', mapDrawn);
82083
82084                 context.keybinding()
82085                     .on(uiCmd('⌘F'), focusSearch);
82086
82087
82088                 function focusSearch() {
82089                     var mode = context.mode() && context.mode().id;
82090                     if (mode !== 'browse') return;
82091
82092                     event.preventDefault();
82093                     search.node().focus();
82094                 }
82095
82096
82097                 function keydown() {
82098                     if (event.keyCode === 27) {  // escape
82099                         search.node().blur();
82100                     }
82101                 }
82102
82103
82104                 function keypress() {
82105                     var q = search.property('value'),
82106                         items = list.selectAll('.feature-list-item');
82107                     if (event.keyCode === 13 && q.length && items.size()) {  // return
82108                         click(items.datum());
82109                     }
82110                 }
82111
82112
82113                 function inputevent() {
82114                     _geocodeResults = undefined;
82115                     drawList();
82116                 }
82117
82118
82119                 function clearSearch() {
82120                     search.property('value', '');
82121                     drawList();
82122                 }
82123
82124
82125                 function mapDrawn(e) {
82126                     if (e.full) {
82127                         drawList();
82128                     }
82129                 }
82130
82131
82132                 function features() {
82133                     var result = [];
82134                     var graph = context.graph();
82135                     var visibleCenter = context.map().extent().center();
82136                     var q = search.property('value').toLowerCase();
82137
82138                     if (!q) return result;
82139
82140                     var idMatch = q.match(/(?:^|\W)(node|way|relation|[nwr])\W?0*([1-9]\d*)(?:\W|$)/i);
82141
82142                     if (idMatch) {
82143                         var elemType = idMatch[1].charAt(0);
82144                         var elemId = idMatch[2];
82145                         result.push({
82146                             id: elemType + elemId,
82147                             geometry: elemType === 'n' ? 'point' : elemType === 'w' ? 'line' : 'relation',
82148                             type: elemType === 'n' ? _t('inspector.node') : elemType === 'w' ? _t('inspector.way') : _t('inspector.relation'),
82149                             name: elemId
82150                         });
82151                     }
82152
82153                     var locationMatch = pair_1(q.toUpperCase()) || q.match(/^(-?\d+\.?\d*)\s+(-?\d+\.?\d*)$/);
82154
82155                     if (locationMatch) {
82156                         var loc = [parseFloat(locationMatch[0]), parseFloat(locationMatch[1])];
82157                         result.push({
82158                             id: -1,
82159                             geometry: 'point',
82160                             type: _t('inspector.location'),
82161                             name: dmsCoordinatePair([loc[1], loc[0]]),
82162                             location: loc
82163                         });
82164                     }
82165
82166                     var allEntities = graph.entities;
82167                     var localResults = [];
82168                     for (var id in allEntities) {
82169                         var entity = allEntities[id];
82170                         if (!entity) continue;
82171
82172                         var name = utilDisplayName(entity) || '';
82173                         if (name.toLowerCase().indexOf(q) < 0) continue;
82174
82175                         var matched = _mainPresetIndex.match(entity, graph);
82176                         var type = (matched && matched.name()) || utilDisplayType(entity.id);
82177                         var extent = entity.extent(graph);
82178                         var distance = extent ? geoSphericalDistance(visibleCenter, extent.center()) : 0;
82179
82180                         localResults.push({
82181                             id: entity.id,
82182                             entity: entity,
82183                             geometry: entity.geometry(graph),
82184                             type: type,
82185                             name: name,
82186                             distance: distance
82187                         });
82188
82189                         if (localResults.length > 100) break;
82190                     }
82191                     localResults = localResults.sort(function byDistance(a, b) {
82192                         return a.distance - b.distance;
82193                     });
82194                     result = result.concat(localResults);
82195
82196                     (_geocodeResults || []).forEach(function(d) {
82197                         if (d.osm_type && d.osm_id) {    // some results may be missing these - #1890
82198
82199                             // Make a temporary osmEntity so we can preset match
82200                             // and better localize the search result - #4725
82201                             var id = osmEntity.id.fromOSM(d.osm_type, d.osm_id);
82202                             var tags = {};
82203                             tags[d.class] = d.type;
82204
82205                             var attrs = { id: id, type: d.osm_type, tags: tags };
82206                             if (d.osm_type === 'way') {   // for ways, add some fake closed nodes
82207                                 attrs.nodes = ['a','a'];  // so that geometry area is possible
82208                             }
82209
82210                             var tempEntity = osmEntity(attrs);
82211                             var tempGraph = coreGraph([tempEntity]);
82212                             var matched = _mainPresetIndex.match(tempEntity, tempGraph);
82213                             var type = (matched && matched.name()) || utilDisplayType(id);
82214
82215                             result.push({
82216                                 id: tempEntity.id,
82217                                 geometry: tempEntity.geometry(tempGraph),
82218                                 type: type,
82219                                 name: d.display_name,
82220                                 extent: new geoExtent(
82221                                     [parseFloat(d.boundingbox[3]), parseFloat(d.boundingbox[0])],
82222                                     [parseFloat(d.boundingbox[2]), parseFloat(d.boundingbox[1])])
82223                             });
82224                         }
82225                     });
82226
82227                     if (q.match(/^[0-9]+$/)) {
82228                         // if query is just a number, possibly an OSM ID without a prefix
82229                         result.push({
82230                             id: 'n' + q,
82231                             geometry: 'point',
82232                             type: _t('inspector.node'),
82233                             name: q
82234                         });
82235                         result.push({
82236                             id: 'w' + q,
82237                             geometry: 'line',
82238                             type: _t('inspector.way'),
82239                             name: q
82240                         });
82241                         result.push({
82242                             id: 'r' + q,
82243                             geometry: 'relation',
82244                             type: _t('inspector.relation'),
82245                             name: q
82246                         });
82247                     }
82248
82249                     return result;
82250                 }
82251
82252
82253                 function drawList() {
82254                     var value = search.property('value');
82255                     var results = features();
82256
82257                     list.classed('filtered', value.length);
82258
82259                     var resultsIndicator = list.selectAll('.no-results-item')
82260                         .data([0])
82261                         .enter()
82262                         .append('button')
82263                         .property('disabled', true)
82264                         .attr('class', 'no-results-item')
82265                         .call(svgIcon('#iD-icon-alert', 'pre-text'));
82266
82267                     resultsIndicator.append('span')
82268                         .attr('class', 'entity-name');
82269
82270                     list.selectAll('.no-results-item .entity-name')
82271                         .text(_t('geocoder.no_results_worldwide'));
82272
82273                     if (services.geocoder) {
82274                       list.selectAll('.geocode-item')
82275                           .data([0])
82276                           .enter()
82277                           .append('button')
82278                           .attr('class', 'geocode-item')
82279                           .on('click', geocoderSearch)
82280                           .append('div')
82281                           .attr('class', 'label')
82282                           .append('span')
82283                           .attr('class', 'entity-name')
82284                           .text(_t('geocoder.search'));
82285                     }
82286
82287                     list.selectAll('.no-results-item')
82288                         .style('display', (value.length && !results.length) ? 'block' : 'none');
82289
82290                     list.selectAll('.geocode-item')
82291                         .style('display', (value && _geocodeResults === undefined) ? 'block' : 'none');
82292
82293                     list.selectAll('.feature-list-item')
82294                         .data([-1])
82295                         .remove();
82296
82297                     var items = list.selectAll('.feature-list-item')
82298                         .data(results, function(d) { return d.id; });
82299
82300                     var enter = items.enter()
82301                         .insert('button', '.geocode-item')
82302                         .attr('class', 'feature-list-item')
82303                         .on('mouseover', mouseover)
82304                         .on('mouseout', mouseout)
82305                         .on('click', click);
82306
82307                     var label = enter
82308                         .append('div')
82309                         .attr('class', 'label');
82310
82311                     label
82312                         .each(function(d) {
82313                             select(this)
82314                                 .call(svgIcon('#iD-icon-' + d.geometry, 'pre-text'));
82315                         });
82316
82317                     label
82318                         .append('span')
82319                         .attr('class', 'entity-type')
82320                         .text(function(d) { return d.type; });
82321
82322                     label
82323                         .append('span')
82324                         .attr('class', 'entity-name')
82325                         .text(function(d) { return d.name; });
82326
82327                     enter
82328                         .style('opacity', 0)
82329                         .transition()
82330                         .style('opacity', 1);
82331
82332                     items.order();
82333
82334                     items.exit()
82335                         .remove();
82336                 }
82337
82338
82339                 function mouseover(d) {
82340                     if (d.id === -1) return;
82341
82342                     utilHighlightEntities([d.id], true, context);
82343                 }
82344
82345
82346                 function mouseout(d) {
82347                     if (d.id === -1) return;
82348
82349                     utilHighlightEntities([d.id], false, context);
82350                 }
82351
82352
82353                 function click(d) {
82354                     event.preventDefault();
82355
82356                     if (d.location) {
82357                         context.map().centerZoomEase([d.location[1], d.location[0]], 19);
82358
82359                     } else if (d.entity) {
82360                         utilHighlightEntities([d.id], false, context);
82361
82362                         context.enter(modeSelect(context, [d.entity.id]));
82363                         context.map().zoomToEase(d.entity);
82364
82365                     } else {
82366                         // download, zoom to, and select the entity with the given ID
82367                         context.zoomToEntity(d.id);
82368                     }
82369                 }
82370
82371
82372                 function geocoderSearch() {
82373                     services.geocoder.search(search.property('value'), function (err, resp) {
82374                         _geocodeResults = resp || [];
82375                         drawList();
82376                     });
82377                 }
82378             }
82379
82380
82381             return featureList;
82382         }
82383
82384         function uiSectionEntityIssues(context) {
82385
82386             var _entityIDs = [];
82387             var _issues = [];
82388             var _activeIssueID;
82389
82390             var section = uiSection('entity-issues', context)
82391                 .shouldDisplay(function() {
82392                     return _issues.length > 0;
82393                 })
82394                 .title(function() {
82395                     return _t('issues.list_title', { count: _issues.length });
82396                 })
82397                 .disclosureContent(renderDisclosureContent);
82398
82399             context.validator()
82400                 .on('validated.entity_issues', function() {
82401                     // Refresh on validated events
82402                     reloadIssues();
82403                     section.reRender();
82404                 })
82405                 .on('focusedIssue.entity_issues', function(issue) {
82406                      makeActiveIssue(issue.id);
82407                 });
82408
82409             function reloadIssues() {
82410                 _issues = context.validator().getSharedEntityIssues(_entityIDs, { includeDisabledRules: true });
82411             }
82412
82413             function makeActiveIssue(issueID) {
82414                 _activeIssueID = issueID;
82415                 section.selection().selectAll('.issue-container')
82416                     .classed('active', function(d) { return d.id === _activeIssueID; });
82417             }
82418
82419             function renderDisclosureContent(selection) {
82420
82421                 selection.classed('grouped-items-area', true);
82422
82423                 _activeIssueID = _issues.length > 0 ? _issues[0].id : null;
82424
82425                 var containers = selection.selectAll('.issue-container')
82426                     .data(_issues, function(d) { return d.id; });
82427
82428                 // Exit
82429                 containers.exit()
82430                     .remove();
82431
82432                 // Enter
82433                 var containersEnter = containers.enter()
82434                     .append('div')
82435                     .attr('class', 'issue-container');
82436
82437
82438                 var itemsEnter = containersEnter
82439                     .append('div')
82440                     .attr('class', function(d) { return 'issue severity-' + d.severity; })
82441                     .on('mouseover.highlight', function(d) {
82442                         // don't hover-highlight the selected entity
82443                         var ids = d.entityIds
82444                             .filter(function(e) { return _entityIDs.indexOf(e) === -1; });
82445
82446                         utilHighlightEntities(ids, true, context);
82447                     })
82448                     .on('mouseout.highlight', function(d) {
82449                         var ids = d.entityIds
82450                             .filter(function(e) { return _entityIDs.indexOf(e) === -1; });
82451
82452                         utilHighlightEntities(ids, false, context);
82453                     });
82454
82455                 var labelsEnter = itemsEnter
82456                     .append('div')
82457                     .attr('class', 'issue-label')
82458                     .on('click', function(d) {
82459
82460                         makeActiveIssue(d.id); // expand only the clicked item
82461
82462                         var extent = d.extent(context.graph());
82463                         if (extent) {
82464                             var setZoom = Math.max(context.map().zoom(), 19);
82465                             context.map().unobscuredCenterZoomEase(extent.center(), setZoom);
82466                         }
82467                     });
82468
82469                 var textEnter = labelsEnter
82470                     .append('span')
82471                     .attr('class', 'issue-text');
82472
82473                 textEnter
82474                     .append('span')
82475                     .attr('class', 'issue-icon')
82476                     .each(function(d) {
82477                         var iconName = '#iD-icon-' + (d.severity === 'warning' ? 'alert' : 'error');
82478                         select(this)
82479                             .call(svgIcon(iconName));
82480                     });
82481
82482                 textEnter
82483                     .append('span')
82484                     .attr('class', 'issue-message');
82485
82486
82487                 var infoButton = labelsEnter
82488                     .append('button')
82489                     .attr('class', 'issue-info-button')
82490                     .attr('title', _t('icons.information'))
82491                     .attr('tabindex', -1)
82492                     .call(svgIcon('#iD-icon-inspect'));
82493
82494                 infoButton
82495                     .on('click', function () {
82496                         event.stopPropagation();
82497                         event.preventDefault();
82498                         this.blur();    // avoid keeping focus on the button - #4641
82499
82500                         var container = select(this.parentNode.parentNode.parentNode);
82501                         var info = container.selectAll('.issue-info');
82502                         var isExpanded = info.classed('expanded');
82503
82504                         if (isExpanded) {
82505                             info
82506                                 .transition()
82507                                 .duration(200)
82508                                 .style('max-height', '0px')
82509                                 .style('opacity', '0')
82510                                 .on('end', function () {
82511                                     info.classed('expanded', false);
82512                                 });
82513                         } else {
82514                             info
82515                                 .classed('expanded', true)
82516                                 .transition()
82517                                 .duration(200)
82518                                 .style('max-height', '200px')
82519                                 .style('opacity', '1')
82520                                 .on('end', function () {
82521                                     info.style('max-height', null);
82522                                 });
82523                         }
82524                     });
82525
82526                 itemsEnter
82527                     .append('ul')
82528                     .attr('class', 'issue-fix-list');
82529
82530                 containersEnter
82531                     .append('div')
82532                     .attr('class', 'issue-info')
82533                     .style('max-height', '0')
82534                     .style('opacity', '0')
82535                     .each(function(d) {
82536                         if (typeof d.reference === 'function') {
82537                             select(this)
82538                                 .call(d.reference);
82539                         } else {
82540                             select(this)
82541                                 .text(_t('inspector.no_documentation_key'));
82542                         }
82543                     });
82544
82545
82546                 // Update
82547                 containers = containers
82548                     .merge(containersEnter)
82549                     .classed('active', function(d) { return d.id === _activeIssueID; });
82550
82551                 containers.selectAll('.issue-message')
82552                     .text(function(d) {
82553                         return d.message(context);
82554                     });
82555
82556                 // fixes
82557                 var fixLists = containers.selectAll('.issue-fix-list');
82558
82559                 var fixes = fixLists.selectAll('.issue-fix-item')
82560                     .data(function(d) { return d.fixes ? d.fixes(context) : []; }, function(fix) { return fix.id; });
82561
82562                 fixes.exit()
82563                     .remove();
82564
82565                 var fixesEnter = fixes.enter()
82566                     .append('li')
82567                     .attr('class', 'issue-fix-item')
82568                     .on('click', function(d) {
82569                         // not all fixes are actionable
82570                         if (!select(this).classed('actionable') || !d.onClick) return;
82571
82572                         // Don't run another fix for this issue within a second of running one
82573                         // (Necessary for "Select a feature type" fix. Most fixes should only ever run once)
82574                         if (d.issue.dateLastRanFix && new Date() - d.issue.dateLastRanFix < 1000) return;
82575                         d.issue.dateLastRanFix = new Date();
82576
82577                         // remove hover-highlighting
82578                         utilHighlightEntities(d.issue.entityIds.concat(d.entityIds), false, context);
82579
82580                         new Promise(function(resolve, reject) {
82581                             d.onClick(context, resolve, reject);
82582                             if (d.onClick.length <= 1) {
82583                                 // if the fix doesn't take any completion parameters then consider it resolved
82584                                 resolve();
82585                             }
82586                         })
82587                         .then(function() {
82588                             // revalidate whenever the fix has finished running successfully
82589                             context.validator().validate();
82590                         });
82591                     })
82592                     .on('mouseover.highlight', function(d) {
82593                         utilHighlightEntities(d.entityIds, true, context);
82594                     })
82595                     .on('mouseout.highlight', function(d) {
82596                         utilHighlightEntities(d.entityIds, false, context);
82597                     });
82598
82599                 fixesEnter
82600                     .append('span')
82601                     .attr('class', 'fix-icon')
82602                     .each(function(d) {
82603                         var iconName = d.icon || 'iD-icon-wrench';
82604                         if (iconName.startsWith('maki')) {
82605                             iconName += '-15';
82606                         }
82607                         select(this).call(svgIcon('#' + iconName));
82608                     });
82609
82610                 fixesEnter
82611                     .append('span')
82612                     .attr('class', 'fix-message')
82613                     .text(function(d) { return d.title; });
82614
82615                 fixesEnter.merge(fixes)
82616                     .classed('actionable', function(d) {
82617                         return d.onClick;
82618                     })
82619                     .attr('title', function(d) {
82620                         if (d.disabledReason) {
82621                             return d.disabledReason;
82622                         }
82623                         return null;
82624                     });
82625             }
82626
82627             section.entityIDs = function(val) {
82628                 if (!arguments.length) return _entityIDs;
82629                 if (!_entityIDs || !val || !utilArrayIdentical(_entityIDs, val)) {
82630                     _entityIDs = val;
82631                     _activeIssueID = null;
82632                     reloadIssues();
82633                 }
82634                 return section;
82635             };
82636
82637             return section;
82638         }
82639
82640         function uiPresetIcon() {
82641           let _preset;
82642           let _geometry;
82643           let _sizeClass = 'medium';
82644
82645
82646           function isSmall() {
82647             return _sizeClass === 'small';
82648           }
82649
82650
82651           function presetIcon(selection) {
82652             selection.each(render);
82653           }
82654
82655
82656           function getIcon(p, geom) {
82657             if (isSmall() && p.isFallback && p.isFallback())
82658               return 'iD-icon-' + p.id;
82659             else if (p.icon)
82660               return p.icon;
82661             else if (geom === 'line')
82662               return 'iD-other-line';
82663             else if (geom === 'vertex')
82664               return p.isFallback() ? '' : 'temaki-vertex';
82665             else if (isSmall() && geom === 'point')
82666               return '';
82667             else
82668               return 'maki-marker-stroked';
82669           }
82670
82671
82672           function renderPointBorder(enter) {
82673             const w = 40;
82674             const h = 40;
82675
82676             enter
82677               .append('svg')
82678               .attr('class', 'preset-icon-fill preset-icon-point-border')
82679               .attr('width', w)
82680               .attr('height', h)
82681               .attr('viewBox', `0 0 ${w} ${h}`)
82682               .append('path')
82683               .attr('transform', 'translate(11.5, 8)')
82684               .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');
82685           }
82686
82687
82688           function renderCircleFill(fillEnter) {
82689             const w = 60;
82690             const h = 60;
82691             const d = 40;
82692
82693             fillEnter
82694               .append('svg')
82695               .attr('class', 'preset-icon-fill preset-icon-fill-vertex')
82696               .attr('width', w)
82697               .attr('height', h)
82698               .attr('viewBox', `0 0 ${w} ${h}`)
82699               .append('circle')
82700               .attr('cx', w / 2)
82701               .attr('cy', h / 2)
82702               .attr('r', d / 2);
82703           }
82704
82705
82706           function renderSquareFill(fillEnter) {
82707             const d = isSmall() ? 40 : 60;
82708             const w = d;
82709             const h = d;
82710             const l = d * 2/3;
82711             const c1 = (w-l) / 2;
82712             const c2 = c1 + l;
82713
82714             fillEnter = fillEnter
82715               .append('svg')
82716               .attr('class', 'preset-icon-fill preset-icon-fill-area')
82717               .attr('width', w)
82718               .attr('height', h)
82719               .attr('viewBox', `0 0 ${w} ${h}`);
82720
82721             ['fill', 'stroke'].forEach(klass => {
82722               fillEnter
82723                 .append('path')
82724                 .attr('d', `M${c1} ${c1} L${c1} ${c2} L${c2} ${c2} L${c2} ${c1} Z`)
82725                 .attr('class', `line area ${klass}`);
82726             });
82727
82728             const rVertex = 2.5;
82729             [[c1, c1], [c1, c2], [c2, c2], [c2, c1]].forEach(point => {
82730               fillEnter
82731                 .append('circle')
82732                 .attr('class', 'vertex')
82733                 .attr('cx', point[0])
82734                 .attr('cy', point[1])
82735                 .attr('r', rVertex);
82736             });
82737
82738             if (!isSmall()) {
82739               const rMidpoint = 1.25;
82740               [[c1, w/2], [c2, w/2], [h/2, c1], [h/2, c2]].forEach(point => {
82741                 fillEnter
82742                   .append('circle')
82743                   .attr('class', 'midpoint')
82744                   .attr('cx', point[0])
82745                   .attr('cy', point[1])
82746                   .attr('r', rMidpoint);
82747               });
82748             }
82749           }
82750
82751
82752           function renderLine(lineEnter) {
82753             const d = isSmall() ? 40 : 60;
82754             // draw the line parametrically
82755             const w = d;
82756             const h = d;
82757             const y = Math.round(d * 0.72);
82758             const l = Math.round(d * 0.6);
82759             const r = 2.5;
82760             const x1 = (w - l) / 2;
82761             const x2 = x1 + l;
82762
82763             lineEnter = lineEnter
82764               .append('svg')
82765               .attr('class', 'preset-icon-line')
82766               .attr('width', w)
82767               .attr('height', h)
82768               .attr('viewBox', `0 0 ${w} ${h}`);
82769
82770             ['casing', 'stroke'].forEach(klass => {
82771               lineEnter
82772                 .append('path')
82773                 .attr('d', `M${x1} ${y} L${x2} ${y}`)
82774                 .attr('class', `line ${klass}`);
82775             });
82776
82777             [[x1-1, y], [x2+1, y]].forEach(point => {
82778               lineEnter
82779                 .append('circle')
82780                 .attr('class', 'vertex')
82781                 .attr('cx', point[0])
82782                 .attr('cy', point[1])
82783                 .attr('r', r);
82784             });
82785           }
82786
82787
82788           function renderRoute(routeEnter) {
82789             const d = isSmall() ? 40 : 60;
82790             // draw the route parametrically
82791             const w = d;
82792             const h = d;
82793             const y1 = Math.round(d * 0.80);
82794             const y2 = Math.round(d * 0.68);
82795             const l = Math.round(d * 0.6);
82796             const r = 2;
82797             const x1 = (w - l) / 2;
82798             const x2 = x1 + l / 3;
82799             const x3 = x2 + l / 3;
82800             const x4 = x3 + l / 3;
82801
82802             routeEnter = routeEnter
82803               .append('svg')
82804               .attr('class', 'preset-icon-route')
82805               .attr('width', w)
82806               .attr('height', h)
82807               .attr('viewBox', `0 0 ${w} ${h}`);
82808
82809             ['casing', 'stroke'].forEach(klass => {
82810               routeEnter
82811                 .append('path')
82812                 .attr('d', `M${x1} ${y1} L${x2} ${y2}`)
82813                 .attr('class', `segment0 line ${klass}`);
82814               routeEnter
82815                 .append('path')
82816                 .attr('d', `M${x2} ${y2} L${x3} ${y1}`)
82817                 .attr('class', `segment1 line ${klass}`);
82818               routeEnter
82819                 .append('path')
82820                 .attr('d', `M${x3} ${y1} L${x4} ${y2}`)
82821                 .attr('class', `segment2 line ${klass}`);
82822             });
82823
82824             [[x1, y1], [x2, y2], [x3, y1], [x4, y2]].forEach(point => {
82825               routeEnter
82826                 .append('circle')
82827                 .attr('class', 'vertex')
82828                 .attr('cx', point[0])
82829                 .attr('cy', point[1])
82830                 .attr('r', r);
82831             });
82832           }
82833
82834
82835           // Route icons are drawn with a zigzag annotation underneath:
82836           //     o   o
82837           //    / \ /
82838           //   o   o
82839           // This dataset defines the styles that are used to draw the zigzag segments.
82840           const routeSegments = {
82841             bicycle: ['highway/cycleway', 'highway/cycleway', 'highway/cycleway'],
82842             bus: ['highway/unclassified', 'highway/secondary', 'highway/primary'],
82843             trolleybus: ['highway/unclassified', 'highway/secondary', 'highway/primary'],
82844             detour: ['highway/tertiary', 'highway/residential', 'highway/unclassified'],
82845             ferry: ['route/ferry', 'route/ferry', 'route/ferry'],
82846             foot: ['highway/footway', 'highway/footway', 'highway/footway'],
82847             hiking: ['highway/path', 'highway/path', 'highway/path'],
82848             horse: ['highway/bridleway', 'highway/bridleway', 'highway/bridleway'],
82849             light_rail: ['railway/light_rail', 'railway/light_rail', 'railway/light_rail'],
82850             monorail: ['railway/monorail', 'railway/monorail', 'railway/monorail'],
82851             pipeline: ['man_made/pipeline', 'man_made/pipeline', 'man_made/pipeline'],
82852             piste: ['piste/downhill', 'piste/hike', 'piste/nordic'],
82853             power: ['power/line', 'power/line', 'power/line'],
82854             road: ['highway/secondary', 'highway/primary', 'highway/trunk'],
82855             subway: ['railway/subway', 'railway/subway', 'railway/subway'],
82856             train: ['railway/rail', 'railway/rail', 'railway/rail'],
82857             tram: ['railway/tram', 'railway/tram', 'railway/tram'],
82858             waterway: ['waterway/stream', 'waterway/stream', 'waterway/stream']
82859           };
82860
82861
82862           function render() {
82863             let p = _preset.apply(this, arguments);
82864             let geom = _geometry ? _geometry.apply(this, arguments) : null;
82865             if (geom === 'relation' && p.tags && ((p.tags.type === 'route' && p.tags.route && routeSegments[p.tags.route]) || p.tags.type === 'waterway')) {
82866               geom = 'route';
82867             }
82868
82869             const showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true';
82870             const isFallback = isSmall() && p.isFallback && p.isFallback();
82871             const imageURL = (showThirdPartyIcons === 'true') && p.imageURL;
82872             const picon = getIcon(p, geom);
82873             const isMaki = picon && /^maki-/.test(picon);
82874             const isTemaki = picon && /^temaki-/.test(picon);
82875             const isFa = picon && /^fa[srb]-/.test(picon);
82876             const isTnp = picon && /^tnp-/.test(picon);
82877             const isiDIcon = picon && !(isMaki || isTemaki || isFa || isTnp);
82878             const isCategory = !p.setTags;
82879             const drawPoint = picon && geom === 'point' && isSmall() && !isFallback;
82880             const drawVertex = picon !== null && geom === 'vertex' && (!isSmall() || !isFallback);
82881             const drawLine = picon && geom === 'line' && !isFallback && !isCategory;
82882             const drawArea = picon && geom === 'area' && !isFallback;
82883             const drawRoute = picon && geom === 'route';
82884             const isFramed = (drawVertex || drawArea || drawLine || drawRoute);
82885
82886             let tags = !isCategory ? p.setTags({}, geom) : {};
82887             for (let k in tags) {
82888               if (tags[k] === '*') {
82889                 tags[k] = 'yes';
82890               }
82891             }
82892
82893             let tagClasses = svgTagClasses().getClassesString(tags, '');
82894             let selection = select(this);
82895
82896             let container = selection.selectAll('.preset-icon-container')
82897               .data([0]);
82898
82899             container = container.enter()
82900               .append('div')
82901               .attr('class', `preset-icon-container ${_sizeClass}`)
82902               .merge(container);
82903
82904             container
82905               .classed('showing-img', !!imageURL)
82906               .classed('fallback', isFallback);
82907
82908
82909             let pointBorder = container.selectAll('.preset-icon-point-border')
82910               .data(drawPoint ? [0] : []);
82911
82912             pointBorder.exit()
82913               .remove();
82914
82915             let pointBorderEnter = pointBorder.enter();
82916             renderPointBorder(pointBorderEnter);
82917             pointBorder = pointBorderEnter.merge(pointBorder);
82918
82919
82920             let vertexFill = container.selectAll('.preset-icon-fill-vertex')
82921               .data(drawVertex ? [0] : []);
82922
82923             vertexFill.exit()
82924               .remove();
82925
82926             let vertexFillEnter = vertexFill.enter();
82927             renderCircleFill(vertexFillEnter);
82928             vertexFill = vertexFillEnter.merge(vertexFill);
82929
82930
82931             let fill = container.selectAll('.preset-icon-fill-area')
82932               .data(drawArea ? [0] : []);
82933
82934             fill.exit()
82935               .remove();
82936
82937             let fillEnter = fill.enter();
82938             renderSquareFill(fillEnter);
82939             fill = fillEnter.merge(fill);
82940
82941             fill.selectAll('path.stroke')
82942               .attr('class', `area stroke ${tagClasses}`);
82943             fill.selectAll('path.fill')
82944               .attr('class', `area fill ${tagClasses}`);
82945
82946
82947             let line = container.selectAll('.preset-icon-line')
82948               .data(drawLine ? [0] : []);
82949
82950             line.exit()
82951               .remove();
82952
82953             let lineEnter = line.enter();
82954             renderLine(lineEnter);
82955             line = lineEnter.merge(line);
82956
82957             line.selectAll('path.stroke')
82958               .attr('class', `line stroke ${tagClasses}`);
82959             line.selectAll('path.casing')
82960               .attr('class', `line casing ${tagClasses}`);
82961
82962
82963             let route = container.selectAll('.preset-icon-route')
82964               .data(drawRoute ? [0] : []);
82965
82966             route.exit()
82967               .remove();
82968
82969             let routeEnter = route.enter();
82970             renderRoute(routeEnter);
82971             route = routeEnter.merge(route);
82972
82973             if (drawRoute) {
82974               let routeType = p.tags.type === 'waterway' ? 'waterway' : p.tags.route;
82975               const segmentPresetIDs = routeSegments[routeType];
82976               for (let i in segmentPresetIDs) {
82977                 const segmentPreset = _mainPresetIndex.item(segmentPresetIDs[i]);
82978                 const segmentTagClasses = svgTagClasses().getClassesString(segmentPreset.tags, '');
82979                 route.selectAll(`path.stroke.segment${i}`)
82980                   .attr('class', `segment${i} line stroke ${segmentTagClasses}`);
82981                 route.selectAll(`path.casing.segment${i}`)
82982                   .attr('class', `segment${i} line casing ${segmentTagClasses}`);
82983               }
82984             }
82985
82986
82987             let icon = container.selectAll('.preset-icon')
82988               .data(picon ? [0] : []);
82989
82990             icon.exit()
82991               .remove();
82992
82993             icon = icon.enter()
82994               .append('div')
82995               .attr('class', 'preset-icon')
82996               .call(svgIcon(''))
82997               .merge(icon);
82998
82999             icon
83000               .attr('class', 'preset-icon ' + (geom ? geom + '-geom' : ''))
83001               .classed('framed', isFramed)
83002               .classed('preset-icon-iD', isiDIcon);
83003
83004             icon.selectAll('svg')
83005               .attr('class', 'icon ' + picon + ' ' + (!isiDIcon && geom !== 'line'  ? '' : tagClasses));
83006
83007             icon.selectAll('use')
83008               .attr('href', '#' + picon + (isMaki ? (isSmall() && geom === 'point' ? '-11' : '-15') : ''));
83009
83010             let imageIcon = container.selectAll('img.image-icon')
83011               .data(imageURL ? [0] : []);
83012
83013             imageIcon.exit()
83014               .remove();
83015
83016             imageIcon = imageIcon.enter()
83017               .append('img')
83018               .attr('class', 'image-icon')
83019               .on('load', () => container.classed('showing-img', true) )
83020               .on('error', () => container.classed('showing-img', false) )
83021               .merge(imageIcon);
83022
83023             imageIcon
83024               .attr('src', imageURL);
83025           }
83026
83027
83028           presetIcon.preset = function(val) {
83029             if (!arguments.length) return _preset;
83030             _preset = utilFunctor(val);
83031             return presetIcon;
83032           };
83033
83034
83035           presetIcon.geometry = function(val) {
83036             if (!arguments.length) return _geometry;
83037             _geometry = utilFunctor(val);
83038             return presetIcon;
83039           };
83040
83041
83042           presetIcon.sizeClass = function(val) {
83043             if (!arguments.length) return _sizeClass;
83044             _sizeClass = val;
83045             return presetIcon;
83046           };
83047
83048           return presetIcon;
83049         }
83050
83051         function uiSectionFeatureType(context) {
83052
83053             var dispatch$1 = dispatch('choose');
83054
83055             var _entityIDs = [];
83056             var _presets = [];
83057
83058             var _tagReference;
83059
83060             var section = uiSection('feature-type', context)
83061                 .title(_t('inspector.feature_type'))
83062                 .disclosureContent(renderDisclosureContent);
83063
83064             function renderDisclosureContent(selection) {
83065
83066                 selection.classed('preset-list-item', true);
83067                 selection.classed('mixed-types', _presets.length > 1);
83068
83069                 var presetButtonWrap = selection
83070                     .selectAll('.preset-list-button-wrap')
83071                     .data([0])
83072                     .enter()
83073                     .append('div')
83074                     .attr('class', 'preset-list-button-wrap');
83075
83076                 var presetButton = presetButtonWrap
83077                     .append('button')
83078                     .attr('class', 'preset-list-button preset-reset')
83079                     .call(uiTooltip()
83080                         .title(_t('inspector.back_tooltip'))
83081                         .placement('bottom')
83082                     );
83083
83084                 presetButton.append('div')
83085                     .attr('class', 'preset-icon-container');
83086
83087                 presetButton
83088                     .append('div')
83089                     .attr('class', 'label')
83090                     .append('div')
83091                     .attr('class', 'label-inner');
83092
83093                 presetButtonWrap.append('div')
83094                     .attr('class', 'accessory-buttons');
83095
83096                 var tagReferenceBodyWrap = selection
83097                     .selectAll('.tag-reference-body-wrap')
83098                     .data([0]);
83099
83100                 tagReferenceBodyWrap = tagReferenceBodyWrap
83101                     .enter()
83102                     .append('div')
83103                     .attr('class', 'tag-reference-body-wrap')
83104                     .merge(tagReferenceBodyWrap);
83105
83106                 // update header
83107                 if (_tagReference) {
83108                     selection.selectAll('.preset-list-button-wrap .accessory-buttons')
83109                         .style('display', _presets.length === 1 ? null : 'none')
83110                         .call(_tagReference.button);
83111
83112                     tagReferenceBodyWrap
83113                         .style('display', _presets.length === 1 ? null : 'none')
83114                         .call(_tagReference.body);
83115                 }
83116
83117                 selection.selectAll('.preset-reset')
83118                     .on('click', function() {
83119                          dispatch$1.call('choose', this, _presets);
83120                     })
83121                     .on('pointerdown pointerup mousedown mouseup', function() {
83122                         event.preventDefault();
83123                         event.stopPropagation();
83124                     });
83125
83126                 var geometries = entityGeometries();
83127                 selection.select('.preset-list-item button')
83128                     .call(uiPresetIcon()
83129                         .geometry(_presets.length === 1 ? (geometries.length === 1 && geometries[0]) : null)
83130                         .preset(_presets.length === 1 ? _presets[0] : _mainPresetIndex.item('point'))
83131                     );
83132
83133                 // NOTE: split on en-dash, not a hypen (to avoid conflict with hyphenated names)
83134                 var names = _presets.length === 1 ? _presets[0].name().split(' – ') : [_t('inspector.multiple_types')];
83135
83136                 var label = selection.select('.label-inner');
83137                 var nameparts = label.selectAll('.namepart')
83138                     .data(names, function(d) { return d; });
83139
83140                 nameparts.exit()
83141                     .remove();
83142
83143                 nameparts
83144                     .enter()
83145                     .append('div')
83146                     .attr('class', 'namepart')
83147                     .text(function(d) { return d; });
83148             }
83149
83150             section.entityIDs = function(val) {
83151                 if (!arguments.length) return _entityIDs;
83152                 _entityIDs = val;
83153                 return section;
83154             };
83155
83156             section.presets = function(val) {
83157                 if (!arguments.length) return _presets;
83158
83159                 // don't reload the same preset
83160                 if (!utilArrayIdentical(val, _presets)) {
83161                     _presets = val;
83162
83163                     var geometries = entityGeometries();
83164                     if (_presets.length === 1 && geometries.length) {
83165                         _tagReference = uiTagReference(_presets[0].reference(geometries[0]))
83166                             .showing(false);
83167                     }
83168                 }
83169
83170                 return section;
83171             };
83172
83173             function entityGeometries() {
83174
83175                 var counts = {};
83176
83177                 for (var i in _entityIDs) {
83178                     var geometry = context.graph().geometry(_entityIDs[i]);
83179                     if (!counts[geometry]) counts[geometry] = 0;
83180                     counts[geometry] += 1;
83181                 }
83182
83183                 return Object.keys(counts).sort(function(geom1, geom2) {
83184                     return counts[geom2] - counts[geom1];
83185                 });
83186             }
83187
83188             return utilRebind(section, dispatch$1, 'on');
83189         }
83190
83191         // This currently only works with the 'restrictions' field
83192         // It borrows some code from uiHelp
83193
83194         function uiFieldHelp(context, fieldName) {
83195             var fieldHelp = {};
83196             var _inspector = select(null);
83197             var _wrap = select(null);
83198             var _body = select(null);
83199
83200             var fieldHelpKeys = {
83201                 restrictions: [
83202                     ['about',[
83203                         'about',
83204                         'from_via_to',
83205                         'maxdist',
83206                         'maxvia'
83207                     ]],
83208                     ['inspecting',[
83209                         'about',
83210                         'from_shadow',
83211                         'allow_shadow',
83212                         'restrict_shadow',
83213                         'only_shadow',
83214                         'restricted',
83215                         'only'
83216                     ]],
83217                     ['modifying',[
83218                         'about',
83219                         'indicators',
83220                         'allow_turn',
83221                         'restrict_turn',
83222                         'only_turn'
83223                     ]],
83224                     ['tips',[
83225                         'simple',
83226                         'simple_example',
83227                         'indirect',
83228                         'indirect_example',
83229                         'indirect_noedit'
83230                     ]]
83231                 ]
83232             };
83233
83234             var fieldHelpHeadings = {};
83235
83236             var replacements = {
83237                 distField: _t('restriction.controls.distance'),
83238                 viaField: _t('restriction.controls.via'),
83239                 fromShadow: icon('#iD-turn-shadow', 'pre-text shadow from'),
83240                 allowShadow: icon('#iD-turn-shadow', 'pre-text shadow allow'),
83241                 restrictShadow: icon('#iD-turn-shadow', 'pre-text shadow restrict'),
83242                 onlyShadow: icon('#iD-turn-shadow', 'pre-text shadow only'),
83243                 allowTurn: icon('#iD-turn-yes', 'pre-text turn'),
83244                 restrictTurn: icon('#iD-turn-no', 'pre-text turn'),
83245                 onlyTurn: icon('#iD-turn-only', 'pre-text turn')
83246             };
83247
83248
83249             // For each section, squash all the texts into a single markdown document
83250             var docs = fieldHelpKeys[fieldName].map(function(key) {
83251                 var helpkey = 'help.field.' + fieldName + '.' + key[0];
83252                 var text = key[1].reduce(function(all, part) {
83253                     var subkey = helpkey + '.' + part;
83254                     var depth = fieldHelpHeadings[subkey];                     // is this subkey a heading?
83255                     var hhh = depth ? Array(depth + 1).join('#') + ' ' : '';   // if so, prepend with some ##'s
83256                     return all + hhh + _t(subkey, replacements) + '\n\n';
83257                 }, '');
83258
83259                 return {
83260                     key: helpkey,
83261                     title: _t(helpkey + '.title'),
83262                     html: marked_1(text.trim())
83263                 };
83264             });
83265
83266
83267             function show() {
83268                 updatePosition();
83269
83270                 _body
83271                     .classed('hide', false)
83272                     .style('opacity', '0')
83273                     .transition()
83274                     .duration(200)
83275                     .style('opacity', '1');
83276             }
83277
83278
83279             function hide() {
83280                 _body
83281                     .classed('hide', true)
83282                     .transition()
83283                     .duration(200)
83284                     .style('opacity', '0')
83285                     .on('end', function () {
83286                         _body.classed('hide', true);
83287                     });
83288             }
83289
83290
83291             function clickHelp(index) {
83292                 var d = docs[index];
83293                 var tkeys = fieldHelpKeys[fieldName][index][1];
83294
83295                 _body.selectAll('.field-help-nav-item')
83296                     .classed('active', function(d, i) { return i === index; });
83297
83298                 var content = _body.selectAll('.field-help-content')
83299                     .html(d.html);
83300
83301                 // class the paragraphs so we can find and style them
83302                 content.selectAll('p')
83303                     .attr('class', function(d, i) { return tkeys[i]; });
83304
83305                 // insert special content for certain help sections
83306                 if (d.key === 'help.field.restrictions.inspecting') {
83307                     content
83308                         .insert('img', 'p.from_shadow')
83309                         .attr('class', 'field-help-image cf')
83310                         .attr('src', context.imagePath('tr_inspect.gif'));
83311
83312                 } else if (d.key === 'help.field.restrictions.modifying') {
83313                     content
83314                         .insert('img', 'p.allow_turn')
83315                         .attr('class', 'field-help-image cf')
83316                         .attr('src', context.imagePath('tr_modify.gif'));
83317                 }
83318             }
83319
83320
83321             fieldHelp.button = function(selection) {
83322                 if (_body.empty()) return;
83323
83324                 var button = selection.selectAll('.field-help-button')
83325                     .data([0]);
83326
83327                 // enter/update
83328                 button.enter()
83329                     .append('button')
83330                     .attr('class', 'field-help-button')
83331                     .attr('tabindex', -1)
83332                     .call(svgIcon('#iD-icon-help'))
83333                     .merge(button)
83334                     .on('click', function () {
83335                         event.stopPropagation();
83336                         event.preventDefault();
83337                         if (_body.classed('hide')) {
83338                             show();
83339                         } else {
83340                             hide();
83341                         }
83342                     });
83343             };
83344
83345
83346             function updatePosition() {
83347                 var wrap = _wrap.node();
83348                 var inspector = _inspector.node();
83349                 var wRect = wrap.getBoundingClientRect();
83350                 var iRect = inspector.getBoundingClientRect();
83351
83352                 _body
83353                     .style('top', wRect.top + inspector.scrollTop - iRect.top + 'px');
83354             }
83355
83356
83357             fieldHelp.body = function(selection) {
83358                 // This control expects the field to have a form-field-input-wrap div
83359                 _wrap = selection.selectAll('.form-field-input-wrap');
83360                 if (_wrap.empty()) return;
83361
83362                 // absolute position relative to the inspector, so it "floats" above the fields
83363                 _inspector = context.container().select('.sidebar .entity-editor-pane .inspector-body');
83364                 if (_inspector.empty()) return;
83365
83366                 _body = _inspector.selectAll('.field-help-body')
83367                     .data([0]);
83368
83369                 var enter = _body.enter()
83370                     .append('div')
83371                     .attr('class', 'field-help-body hide');   // initially hidden
83372
83373                 var titleEnter = enter
83374                     .append('div')
83375                     .attr('class', 'field-help-title cf');
83376
83377                 titleEnter
83378                     .append('h2')
83379                     .attr('class', ((_mainLocalizer.textDirection() === 'rtl') ? 'fr' : 'fl'))
83380                     .text(_t('help.field.' + fieldName + '.title'));
83381
83382                 titleEnter
83383                     .append('button')
83384                     .attr('class', 'fr close')
83385                     .on('click', function() {
83386                         event.stopPropagation();
83387                         event.preventDefault();
83388                         hide();
83389                     })
83390                     .call(svgIcon('#iD-icon-close'));
83391
83392                 var navEnter = enter
83393                     .append('div')
83394                     .attr('class', 'field-help-nav cf');
83395
83396                 var titles = docs.map(function(d) { return d.title; });
83397                 navEnter.selectAll('.field-help-nav-item')
83398                     .data(titles)
83399                     .enter()
83400                     .append('div')
83401                     .attr('class', 'field-help-nav-item')
83402                     .text(function(d) { return d; })
83403                     .on('click', function(d, i) {
83404                         event.stopPropagation();
83405                         event.preventDefault();
83406                         clickHelp(i);
83407                     });
83408
83409                 enter
83410                     .append('div')
83411                     .attr('class', 'field-help-content');
83412
83413                 _body = _body
83414                     .merge(enter);
83415
83416                 clickHelp(0);
83417             };
83418
83419
83420             return fieldHelp;
83421         }
83422
83423         function uiFieldCheck(field, context) {
83424             var dispatch$1 = dispatch('change');
83425             var options = field.strings && field.strings.options;
83426             var values = [];
83427             var texts = [];
83428
83429             var _tags;
83430
83431             var input = select(null);
83432             var text = select(null);
83433             var label = select(null);
83434             var reverser = select(null);
83435
83436             var _impliedYes;
83437             var _entityIDs = [];
83438             var _value;
83439
83440
83441             if (options) {
83442                 for (var k in options) {
83443                     values.push(k === 'undefined' ? undefined : k);
83444                     texts.push(field.t('options.' + k, { 'default': options[k] }));
83445                 }
83446             } else {
83447                 values = [undefined, 'yes'];
83448                 texts = [_t('inspector.unknown'), _t('inspector.check.yes')];
83449                 if (field.type !== 'defaultCheck') {
83450                     values.push('no');
83451                     texts.push(_t('inspector.check.no'));
83452                 }
83453             }
83454
83455
83456             // Checks tags to see whether an undefined value is "Assumed to be Yes"
83457             function checkImpliedYes() {
83458                 _impliedYes = (field.id === 'oneway_yes');
83459
83460                 // hack: pretend `oneway` field is a `oneway_yes` field
83461                 // where implied oneway tag exists (e.g. `junction=roundabout`) #2220, #1841
83462                 if (field.id === 'oneway') {
83463                     var entity = context.entity(_entityIDs[0]);
83464                     for (var key in entity.tags) {
83465                         if (key in osmOneWayTags && (entity.tags[key] in osmOneWayTags[key])) {
83466                             _impliedYes = true;
83467                             texts[0] = _t('presets.fields.oneway_yes.options.undefined');
83468                             break;
83469                         }
83470                     }
83471                 }
83472             }
83473
83474
83475             function reverserHidden() {
83476                 if (!context.container().select('div.inspector-hover').empty()) return true;
83477                 return !(_value === 'yes' || (_impliedYes && !_value));
83478             }
83479
83480
83481             function reverserSetText(selection) {
83482                 var entity = _entityIDs.length && context.hasEntity(_entityIDs[0]);
83483                 if (reverserHidden() || !entity) return selection;
83484
83485                 var first = entity.first();
83486                 var last = entity.isClosed() ? entity.nodes[entity.nodes.length - 2] : entity.last();
83487                 var pseudoDirection = first < last;
83488                 var icon = pseudoDirection ? '#iD-icon-forward' : '#iD-icon-backward';
83489
83490                 selection.selectAll('.reverser-span')
83491                     .text(_t('inspector.check.reverser'))
83492                     .call(svgIcon(icon, 'inline'));
83493
83494                 return selection;
83495             }
83496
83497
83498             var check = function(selection) {
83499                 checkImpliedYes();
83500
83501                 label = selection.selectAll('.form-field-input-wrap')
83502                     .data([0]);
83503
83504                 var enter = label.enter()
83505                     .append('label')
83506                     .attr('class', 'form-field-input-wrap form-field-input-check');
83507
83508                 enter
83509                     .append('input')
83510                     .property('indeterminate', field.type !== 'defaultCheck')
83511                     .attr('type', 'checkbox')
83512                     .attr('id', field.domId);
83513
83514                 enter
83515                     .append('span')
83516                     .text(texts[0])
83517                     .attr('class', 'value');
83518
83519                 if (field.type === 'onewayCheck') {
83520                     enter
83521                         .append('a')
83522                         .attr('class', 'reverser button' + (reverserHidden() ? ' hide' : ''))
83523                         .attr('href', '#')
83524                         .append('span')
83525                         .attr('class', 'reverser-span');
83526                 }
83527
83528                 label = label.merge(enter);
83529                 input = label.selectAll('input');
83530                 text = label.selectAll('span.value');
83531
83532                 input
83533                     .on('click', function() {
83534                         event.stopPropagation();
83535                         var t = {};
83536
83537                         if (Array.isArray(_tags[field.key])) {
83538                             if (values.indexOf('yes') !== -1) {
83539                                 t[field.key] = 'yes';
83540                             } else {
83541                                 t[field.key] = values[0];
83542                             }
83543                         } else {
83544                             t[field.key] = values[(values.indexOf(_value) + 1) % values.length];
83545                         }
83546
83547                         // Don't cycle through `alternating` or `reversible` states - #4970
83548                         // (They are supported as translated strings, but should not toggle with clicks)
83549                         if (t[field.key] === 'reversible' || t[field.key] === 'alternating') {
83550                             t[field.key] = values[0];
83551                         }
83552
83553                         dispatch$1.call('change', this, t);
83554                     });
83555
83556                 if (field.type === 'onewayCheck') {
83557                     reverser = label.selectAll('.reverser');
83558
83559                     reverser
83560                         .call(reverserSetText)
83561                         .on('click', function() {
83562                             event.preventDefault();
83563                             event.stopPropagation();
83564                             context.perform(
83565                                 function(graph) {
83566                                     for (var i in _entityIDs) {
83567                                         graph = actionReverse(_entityIDs[i])(graph);
83568                                     }
83569                                     return graph;
83570                                 },
83571                                 _t('operations.reverse.annotation')
83572                             );
83573
83574                             // must manually revalidate since no 'change' event was called
83575                             context.validator().validate();
83576
83577                             select(this)
83578                                 .call(reverserSetText);
83579                         });
83580                 }
83581             };
83582
83583
83584             check.entityIDs = function(val) {
83585                 if (!arguments.length) return _entityIDs;
83586                 _entityIDs = val;
83587                 return check;
83588             };
83589
83590
83591             check.tags = function(tags) {
83592
83593                 _tags = tags;
83594
83595                 function isChecked(val) {
83596                     return val !== 'no' && val !== '' && val !== undefined && val !== null;
83597                 }
83598
83599                 function textFor(val) {
83600                     if (val === '') val = undefined;
83601                     var index = values.indexOf(val);
83602                     return (index !== -1 ? texts[index] : ('"' + val + '"'));
83603                 }
83604
83605                 checkImpliedYes();
83606
83607                 var isMixed = Array.isArray(tags[field.key]);
83608
83609                 _value = !isMixed && tags[field.key] && tags[field.key].toLowerCase();
83610
83611                 if (field.type === 'onewayCheck' && (_value === '1' || _value === '-1')) {
83612                     _value = 'yes';
83613                 }
83614
83615                 input
83616                     .property('indeterminate', isMixed || (field.type !== 'defaultCheck' && !_value))
83617                     .property('checked', isChecked(_value));
83618
83619                 text
83620                     .text(isMixed ? _t('inspector.multiple_values') : textFor(_value))
83621                     .classed('mixed', isMixed);
83622
83623                 label
83624                     .classed('set', !!_value);
83625
83626                 if (field.type === 'onewayCheck') {
83627                     reverser
83628                         .classed('hide', reverserHidden())
83629                         .call(reverserSetText);
83630                 }
83631             };
83632
83633
83634             check.focus = function() {
83635                 input.node().focus();
83636             };
83637
83638             return utilRebind(check, dispatch$1, 'on');
83639         }
83640
83641         function uiFieldCombo(field, context) {
83642             var dispatch$1 = dispatch('change');
83643             var taginfo = services.taginfo;
83644             var isMulti = (field.type === 'multiCombo');
83645             var isNetwork = (field.type === 'networkCombo');
83646             var isSemi = (field.type === 'semiCombo');
83647             var optstrings = field.strings && field.strings.options;
83648             var optarray = field.options;
83649             var snake_case = (field.snake_case || (field.snake_case === undefined));
83650             var caseSensitive = field.caseSensitive;
83651             var combobox = uiCombobox(context, 'combo-' + field.safeid)
83652                 .caseSensitive(caseSensitive)
83653                 .minItems(isMulti || isSemi ? 1 : 2);
83654             var container = select(null);
83655             var inputWrap = select(null);
83656             var input = select(null);
83657             var _comboData = [];
83658             var _multiData = [];
83659             var _entityIDs = [];
83660             var _tags;
83661             var _countryCode;
83662             var _staticPlaceholder;
83663
83664             // initialize deprecated tags array
83665             var _dataDeprecated = [];
83666             _mainFileFetcher.get('deprecated')
83667                 .then(function(d) { _dataDeprecated = d; })
83668                 .catch(function() { /* ignore */ });
83669
83670
83671             // ensure multiCombo field.key ends with a ':'
83672             if (isMulti && /[^:]$/.test(field.key)) {
83673                 field.key += ':';
83674             }
83675
83676
83677             function snake(s) {
83678                 return s.replace(/\s+/g, '_');
83679             }
83680
83681             function unsnake(s) {
83682                 return s.replace(/_+/g, ' ');
83683             }
83684
83685             function clean(s) {
83686                 return s.split(';')
83687                     .map(function(s) { return s.trim(); })
83688                     .join(';');
83689             }
83690
83691
83692             // returns the tag value for a display value
83693             // (for multiCombo, dval should be the key suffix, not the entire key)
83694             function tagValue(dval) {
83695                 dval = clean(dval || '');
83696
83697                 if (optstrings) {
83698                     var found = _comboData.find(function(o) {
83699                         return o.key && clean(o.value) === dval;
83700                     });
83701                     if (found) {
83702                         return found.key;
83703                     }
83704                 }
83705
83706                 if (field.type === 'typeCombo' && !dval) {
83707                     return 'yes';
83708                 }
83709
83710                 return (snake_case ? snake(dval) : dval) || undefined;
83711             }
83712
83713
83714             // returns the display value for a tag value
83715             // (for multiCombo, tval should be the key suffix, not the entire key)
83716             function displayValue(tval) {
83717                 tval = tval || '';
83718
83719                 if (optstrings) {
83720                     var found = _comboData.find(function(o) {
83721                         return o.key === tval && o.value;
83722                     });
83723                     if (found) {
83724                         return found.value;
83725                     }
83726                 }
83727
83728                 if (field.type === 'typeCombo' && tval.toLowerCase() === 'yes') {
83729                     return '';
83730                 }
83731
83732                 return snake_case ? unsnake(tval) : tval;
83733             }
83734
83735
83736             // Compute the difference between arrays of objects by `value` property
83737             //
83738             // objectDifference([{value:1}, {value:2}, {value:3}], [{value:2}])
83739             // > [{value:1}, {value:3}]
83740             //
83741             function objectDifference(a, b) {
83742                 return a.filter(function(d1) {
83743                     return !b.some(function(d2) {
83744                         return !d2.isMixed && d1.value === d2.value;
83745                     });
83746                 });
83747             }
83748
83749
83750             function initCombo(selection, attachTo) {
83751                 if (optstrings) {
83752                     selection.attr('readonly', 'readonly');
83753                     selection.call(combobox, attachTo);
83754                     setStaticValues(setPlaceholder);
83755
83756                 } else if (optarray) {
83757                     selection.call(combobox, attachTo);
83758                     setStaticValues(setPlaceholder);
83759
83760                 } else if (taginfo) {
83761                     selection.call(combobox.fetcher(setTaginfoValues), attachTo);
83762                     setTaginfoValues('', setPlaceholder);
83763                 }
83764             }
83765
83766
83767             function setStaticValues(callback) {
83768                 if (!(optstrings || optarray)) return;
83769
83770                 if (optstrings) {
83771                     _comboData = Object.keys(optstrings).map(function(k) {
83772                         var v = field.t('options.' + k, { 'default': optstrings[k] });
83773                         return {
83774                             key: k,
83775                             value: v,
83776                             title: v
83777                         };
83778                     });
83779
83780                 } else if (optarray) {
83781                     _comboData = optarray.map(function(k) {
83782                         var v = snake_case ? unsnake(k) : k;
83783                         return {
83784                             key: k,
83785                             value: v,
83786                             title: v
83787                         };
83788                     });
83789                 }
83790
83791                 combobox.data(objectDifference(_comboData, _multiData));
83792                 if (callback) callback(_comboData);
83793             }
83794
83795
83796             function setTaginfoValues(q, callback) {
83797                 var fn = isMulti ? 'multikeys' : 'values';
83798                 var query = (isMulti ? field.key : '') + q;
83799                 var hasCountryPrefix = isNetwork && _countryCode && _countryCode.indexOf(q.toLowerCase()) === 0;
83800                 if (hasCountryPrefix) {
83801                     query = _countryCode + ':';
83802                 }
83803
83804                 var params = {
83805                     debounce: (q !== ''),
83806                     key: field.key,
83807                     query: query
83808                 };
83809
83810                 if (_entityIDs.length) {
83811                     params.geometry = context.graph().geometry(_entityIDs[0]);
83812                 }
83813
83814                 taginfo[fn](params, function(err, data) {
83815                     if (err) return;
83816
83817                     data = data.filter(function(d) {
83818
83819                         if (field.type === 'typeCombo' && d.value === 'yes') {
83820                             // don't show the fallback value
83821                             return false;
83822                         }
83823
83824                         // don't show values with very low usage
83825                         return !d.count || d.count > 10;
83826                     });
83827
83828                     var deprecatedValues = osmEntity.deprecatedTagValuesByKey(_dataDeprecated)[field.key];
83829                     if (deprecatedValues) {
83830                         // don't suggest deprecated tag values
83831                         data = data.filter(function(d) {
83832                             return deprecatedValues.indexOf(d.value) === -1;
83833                         });
83834                     }
83835
83836                     if (hasCountryPrefix) {
83837                         data = data.filter(function(d) {
83838                             return d.value.toLowerCase().indexOf(_countryCode + ':') === 0;
83839                         });
83840                     }
83841
83842                     // hide the caret if there are no suggestions
83843                     container.classed('empty-combobox', data.length === 0);
83844
83845                     _comboData = data.map(function(d) {
83846                         var k = d.value;
83847                         if (isMulti) k = k.replace(field.key, '');
83848                         var v = snake_case ? unsnake(k) : k;
83849                         return {
83850                             key: k,
83851                             value: v,
83852                             title: isMulti ? v : d.title
83853                         };
83854                     });
83855
83856                     _comboData = objectDifference(_comboData, _multiData);
83857                     if (callback) callback(_comboData);
83858                 });
83859             }
83860
83861
83862             function setPlaceholder(values) {
83863
83864                 if (isMulti || isSemi) {
83865                     _staticPlaceholder = field.placeholder() || _t('inspector.add');
83866                 } else {
83867                     var vals = values
83868                         .map(function(d) { return d.value; })
83869                         .filter(function(s) { return s.length < 20; });
83870
83871                     var placeholders = vals.length > 1 ? vals : values.map(function(d) { return d.key; });
83872                     _staticPlaceholder = field.placeholder() || placeholders.slice(0, 3).join(', ');
83873                 }
83874
83875                 if (!/(…|\.\.\.)$/.test(_staticPlaceholder)) {
83876                     _staticPlaceholder += '…';
83877                 }
83878
83879                 var ph;
83880                 if (!isMulti && !isSemi && _tags && Array.isArray(_tags[field.key])) {
83881                     ph = _t('inspector.multiple_values');
83882                 } else {
83883                     ph =  _staticPlaceholder;
83884                 }
83885
83886                 container.selectAll('input')
83887                     .attr('placeholder', ph);
83888             }
83889
83890
83891             function change() {
83892                 var t = {};
83893                 var val;
83894
83895                 if (isMulti || isSemi) {
83896                     val = tagValue(utilGetSetValue(input).replace(/,/g, ';')) || '';
83897                     container.classed('active', false);
83898                     utilGetSetValue(input, '');
83899
83900                     var vals = val.split(';').filter(Boolean);
83901                     if (!vals.length) return;
83902
83903                     if (isMulti) {
83904                         utilArrayUniq(vals).forEach(function(v) {
83905                             var key = field.key + v;
83906                             if (_tags) {
83907                                 // don't set a multicombo value to 'yes' if it already has a non-'no' value
83908                                 // e.g. `language:de=main`
83909                                 var old = _tags[key];
83910                                 if (typeof old === 'string' && old.toLowerCase() !== 'no') return;
83911                             }
83912                             key = context.cleanTagKey(key);
83913                             field.keys.push(key);
83914                             t[key] = 'yes';
83915                         });
83916
83917                     } else if (isSemi) {
83918                         var arr = _multiData.map(function(d) { return d.key; });
83919                         arr = arr.concat(vals);
83920                         t[field.key] = context.cleanTagValue(utilArrayUniq(arr).filter(Boolean).join(';'));
83921                     }
83922
83923                     window.setTimeout(function() { input.node().focus(); }, 10);
83924
83925                 } else {
83926                     var rawValue = utilGetSetValue(input);
83927
83928                     // don't override multiple values with blank string
83929                     if (!rawValue && Array.isArray(_tags[field.key])) return;
83930
83931                     val = context.cleanTagValue(tagValue(rawValue));
83932                     t[field.key] = val || undefined;
83933                 }
83934
83935                 dispatch$1.call('change', this, t);
83936             }
83937
83938
83939             function removeMultikey(d) {
83940                 event.stopPropagation();
83941                 var t = {};
83942                 if (isMulti) {
83943                     t[d.key] = undefined;
83944                 } else if (isSemi) {
83945                     var arr = _multiData.map(function(md) {
83946                         return md.key === d.key ? null : md.key;
83947                     }).filter(Boolean);
83948
83949                     arr = utilArrayUniq(arr);
83950                     t[field.key] = arr.length ? arr.join(';') : undefined;
83951                 }
83952                 dispatch$1.call('change', this, t);
83953             }
83954
83955
83956             function combo(selection) {
83957                 container = selection.selectAll('.form-field-input-wrap')
83958                     .data([0]);
83959
83960                 var type = (isMulti || isSemi) ? 'multicombo': 'combo';
83961                 container = container.enter()
83962                     .append('div')
83963                     .attr('class', 'form-field-input-wrap form-field-input-' + type)
83964                     .merge(container);
83965
83966                 if (isMulti || isSemi) {
83967                     container = container.selectAll('.chiplist')
83968                         .data([0]);
83969
83970                     var listClass = 'chiplist';
83971
83972                     // Use a separate line for each value in the Destinations field
83973                     // to mimic highway exit signs
83974                     if (field.key === 'destination') {
83975                         listClass += ' full-line-chips';
83976                     }
83977
83978                     container = container.enter()
83979                         .append('ul')
83980                         .attr('class', listClass)
83981                         .on('click', function() {
83982                             window.setTimeout(function() { input.node().focus(); }, 10);
83983                         })
83984                         .merge(container);
83985
83986
83987                     inputWrap = container.selectAll('.input-wrap')
83988                         .data([0]);
83989
83990                     inputWrap = inputWrap.enter()
83991                         .append('li')
83992                         .attr('class', 'input-wrap')
83993                         .merge(inputWrap);
83994
83995                     input = inputWrap.selectAll('input')
83996                         .data([0]);
83997                 } else {
83998                     input = container.selectAll('input')
83999                         .data([0]);
84000                 }
84001
84002                 input = input.enter()
84003                     .append('input')
84004                     .attr('type', 'text')
84005                     .attr('id', field.domId)
84006                     .call(utilNoAuto)
84007                     .call(initCombo, selection)
84008                     .merge(input);
84009
84010                 if (isNetwork) {
84011                     var extent = combinedEntityExtent();
84012                     var countryCode = extent && iso1A2Code(extent.center());
84013                     _countryCode = countryCode && countryCode.toLowerCase();
84014                 }
84015
84016                 input
84017                     .on('change', change)
84018                     .on('blur', change);
84019
84020                 input
84021                     .on('keydown.field', function() {
84022                         switch (event.keyCode) {
84023                             case 13: // ↩ Return
84024                                 input.node().blur(); // blurring also enters the value
84025                                 event.stopPropagation();
84026                                 break;
84027                         }
84028                     });
84029
84030                 if (isMulti || isSemi) {
84031                     combobox
84032                         .on('accept', function() {
84033                             input.node().blur();
84034                             input.node().focus();
84035                         });
84036
84037                     input
84038                         .on('focus', function() { container.classed('active', true); });
84039                 }
84040             }
84041
84042
84043             combo.tags = function(tags) {
84044                 _tags = tags;
84045
84046                 if (isMulti || isSemi) {
84047                     _multiData = [];
84048
84049                     var maxLength;
84050
84051                     if (isMulti) {
84052                         // Build _multiData array containing keys already set..
84053                         for (var k in tags) {
84054                             if (k.indexOf(field.key) !== 0) continue;
84055                             var v = tags[k];
84056                             if (!v || (typeof v === 'string' && v.toLowerCase() === 'no')) continue;
84057
84058                             var suffix = k.substring(field.key.length);
84059                             _multiData.push({
84060                                 key: k,
84061                                 value: displayValue(suffix),
84062                                 isMixed: Array.isArray(v)
84063                             });
84064                         }
84065
84066                         // Set keys for form-field modified (needed for undo and reset buttons)..
84067                         field.keys = _multiData.map(function(d) { return d.key; });
84068
84069                         // limit the input length so it fits after prepending the key prefix
84070                         maxLength = context.maxCharsForTagKey() - utilUnicodeCharsCount(field.key);
84071
84072                     } else if (isSemi) {
84073
84074                         var allValues = [];
84075                         var commonValues;
84076                         if (Array.isArray(tags[field.key])) {
84077
84078                             tags[field.key].forEach(function(tagVal) {
84079                                 var thisVals = utilArrayUniq((tagVal || '').split(';')).filter(Boolean);
84080                                 allValues = allValues.concat(thisVals);
84081                                 if (!commonValues) {
84082                                     commonValues = thisVals;
84083                                 } else {
84084                                     commonValues = commonValues.filter(value => thisVals.includes(value));
84085                                 }
84086                             });
84087                             allValues = utilArrayUniq(allValues).filter(Boolean);
84088
84089                         } else {
84090                             allValues =  utilArrayUniq((tags[field.key] || '').split(';')).filter(Boolean);
84091                             commonValues = allValues;
84092                         }
84093
84094                         _multiData = allValues.map(function(v) {
84095                             return {
84096                                 key: v,
84097                                 value: displayValue(v),
84098                                 isMixed: !commonValues.includes(v)
84099                             };
84100                         });
84101
84102                         var currLength = utilUnicodeCharsCount(commonValues.join(';'));
84103
84104                         // limit the input length to the remaining available characters
84105                         maxLength = context.maxCharsForTagValue() - currLength;
84106
84107                         if (currLength > 0) {
84108                             // account for the separator if a new value will be appended to existing
84109                             maxLength -= 1;
84110                         }
84111                     }
84112                     // a negative maxlength doesn't make sense
84113                     maxLength = Math.max(0, maxLength);
84114
84115                     var allowDragAndDrop = isSemi // only semiCombo values are ordered
84116                         && !Array.isArray(tags[field.key]);
84117
84118                     // Exclude existing multikeys from combo options..
84119                     var available = objectDifference(_comboData, _multiData);
84120                     combobox.data(available);
84121
84122                     // Hide 'Add' button if this field uses fixed set of
84123                     // translateable optstrings and they're all currently used,
84124                     // or if the field is already at its character limit
84125                     var hideAdd = (optstrings && !available.length) || maxLength <= 0;
84126                     container.selectAll('.chiplist .input-wrap')
84127                         .style('display', hideAdd ? 'none' : null);
84128
84129
84130                     // Render chips
84131                     var chips = container.selectAll('.chip')
84132                         .data(_multiData);
84133
84134                     chips.exit()
84135                         .remove();
84136
84137                     var enter = chips.enter()
84138                         .insert('li', '.input-wrap')
84139                         .attr('class', 'chip');
84140
84141                     enter.append('span');
84142                     enter.append('a');
84143
84144                     chips = chips.merge(enter)
84145                         .order()
84146                         .classed('draggable', allowDragAndDrop)
84147                         .classed('mixed', function(d) {
84148                             return d.isMixed;
84149                         })
84150                         .attr('title', function(d) {
84151                             return d.isMixed ? _t('inspector.unshared_value_tooltip') : null;
84152                         });
84153
84154                     if (allowDragAndDrop) {
84155                         registerDragAndDrop(chips);
84156                     }
84157
84158                     chips.select('span')
84159                         .text(function(d) { return d.value; });
84160
84161                     chips.select('a')
84162                         .on('click', removeMultikey)
84163                         .attr('class', 'remove')
84164                         .text('×');
84165
84166                 } else {
84167                     var isMixed = Array.isArray(tags[field.key]);
84168
84169                     var mixedValues = isMixed && tags[field.key].map(function(val) {
84170                         return displayValue(val);
84171                     }).filter(Boolean);
84172
84173                     utilGetSetValue(input, !isMixed ? displayValue(tags[field.key]) : '')
84174                         .attr('title', isMixed ? mixedValues.join('\n') : undefined)
84175                         .attr('placeholder', isMixed ? _t('inspector.multiple_values') : _staticPlaceholder || '')
84176                         .classed('mixed', isMixed);
84177                 }
84178             };
84179
84180             function registerDragAndDrop(selection) {
84181
84182                 // allow drag and drop re-ordering of chips
84183                 var dragOrigin, targetIndex;
84184                 selection.call(d3_drag()
84185                     .on('start', function() {
84186                         dragOrigin = {
84187                             x: event.x,
84188                             y: event.y
84189                         };
84190                         targetIndex = null;
84191                     })
84192                     .on('drag', function(d, index) {
84193                         var x = event.x - dragOrigin.x,
84194                             y = event.y - dragOrigin.y;
84195
84196                         if (!select(this).classed('dragging') &&
84197                             // don't display drag until dragging beyond a distance threshold
84198                             Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) <= 5) return;
84199
84200                         select(this)
84201                             .classed('dragging', true);
84202
84203                         targetIndex = null;
84204                         var targetIndexOffsetTop = null;
84205                         var draggedTagWidth = select(this).node().offsetWidth;
84206
84207                         if (field.key === 'destination') { // meaning tags are full width
84208                             container.selectAll('.chip')
84209                                 .style('transform', function(d2, index2) {
84210                                     var node = select(this).node();
84211
84212                                     if (index === index2) {
84213                                         return 'translate(' + x + 'px, ' + y + 'px)';
84214                                     // move the dragged tag up the order
84215                                     } else if (index2 > index && event.y > node.offsetTop) {
84216                                         if (targetIndex === null || index2 > targetIndex) {
84217                                             targetIndex = index2;
84218                                         }
84219                                         return 'translateY(-100%)';
84220                                     // move the dragged tag down the order
84221                                     } else if (index2 < index && event.y < node.offsetTop + node.offsetHeight) {
84222                                         if (targetIndex === null || index2 < targetIndex) {
84223                                             targetIndex = index2;
84224                                         }
84225                                         return 'translateY(100%)';
84226                                     }
84227                                     return null;
84228                                 });
84229                         } else {
84230                             container.selectAll('.chip')
84231                                 .each(function(d2, index2) {
84232                                     var node = select(this).node();
84233
84234                                     // check the cursor is in the bounding box
84235                                     if (
84236                                         index !== index2 &&
84237                                         event.x < node.offsetLeft + node.offsetWidth + 5 &&
84238                                         event.x > node.offsetLeft &&
84239                                         event.y < node.offsetTop + node.offsetHeight &&
84240                                         event.y > node.offsetTop
84241                                     ) {
84242                                         targetIndex = index2;
84243                                         targetIndexOffsetTop = node.offsetTop;
84244                                     }
84245                                 })
84246                                 .style('transform', function(d2, index2) {
84247                                     var node = select(this).node();
84248
84249                                     if (index === index2) {
84250                                         return 'translate(' + x + 'px, ' + y + 'px)';
84251                                     }
84252
84253                                     // only translate tags in the same row
84254                                     if (node.offsetTop === targetIndexOffsetTop) {
84255                                         if (index2 < index && index2 >= targetIndex) {
84256                                             return 'translateX(' + draggedTagWidth + 'px)';
84257                                         } else if (index2 > index && index2 <= targetIndex) {
84258                                             return 'translateX(-' + draggedTagWidth + 'px)';
84259                                         }
84260                                     }
84261                                     return null;
84262                                 });
84263                             }
84264                     })
84265                     .on('end', function(d, index) {
84266                         if (!select(this).classed('dragging')) {
84267                             return;
84268                         }
84269
84270                         select(this)
84271                             .classed('dragging', false);
84272
84273                         container.selectAll('.chip')
84274                             .style('transform', null);
84275
84276                         if (typeof targetIndex === 'number') {
84277                             var element = _multiData[index];
84278                             _multiData.splice(index, 1);
84279                             _multiData.splice(targetIndex, 0, element);
84280
84281                             var t = {};
84282
84283                             if (_multiData.length) {
84284                                 t[field.key] = _multiData.map(function(element) {
84285                                     return element.key;
84286                                 }).join(';');
84287                             } else {
84288                                 t[field.key] = undefined;
84289                             }
84290
84291                             dispatch$1.call('change', this, t);
84292                         }
84293                         dragOrigin = undefined;
84294                         targetIndex = undefined;
84295                     })
84296                 );
84297             }
84298
84299
84300             combo.focus = function() {
84301                 input.node().focus();
84302             };
84303
84304
84305             combo.entityIDs = function(val) {
84306                 if (!arguments.length) return _entityIDs;
84307                 _entityIDs = val;
84308                 return combo;
84309             };
84310
84311
84312             function combinedEntityExtent() {
84313                 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
84314             }
84315
84316
84317             return utilRebind(combo, dispatch$1, 'on');
84318         }
84319
84320         function uiFieldText(field, context) {
84321             var dispatch$1 = dispatch('change');
84322             var input = select(null);
84323             var outlinkButton = select(null);
84324             var _entityIDs = [];
84325             var _tags;
84326             var _phoneFormats = {};
84327
84328             if (field.type === 'tel') {
84329                 _mainFileFetcher.get('phone_formats')
84330                     .then(function(d) {
84331                         _phoneFormats = d;
84332                         updatePhonePlaceholder();
84333                     })
84334                     .catch(function() { /* ignore */ });
84335             }
84336
84337             function i(selection) {
84338                 var entity = _entityIDs.length && context.hasEntity(_entityIDs[0]);
84339                 var preset = entity && _mainPresetIndex.match(entity, context.graph());
84340                 var isLocked = preset && preset.suggestion && field.id === 'brand';
84341                 field.locked(isLocked);
84342
84343                 var wrap = selection.selectAll('.form-field-input-wrap')
84344                     .data([0]);
84345
84346                 wrap = wrap.enter()
84347                     .append('div')
84348                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
84349                     .merge(wrap);
84350
84351                 input = wrap.selectAll('input')
84352                     .data([0]);
84353
84354                 input = input.enter()
84355                     .append('input')
84356                     .attr('type', field.type === 'identifier' ? 'text' : field.type)
84357                     .attr('id', field.domId)
84358                     .classed(field.type, true)
84359                     .call(utilNoAuto)
84360                     .merge(input);
84361
84362                 input
84363                     .classed('disabled', !!isLocked)
84364                     .attr('readonly', isLocked || null)
84365                     .on('input', change(true))
84366                     .on('blur', change())
84367                     .on('change', change());
84368
84369
84370                 if (field.type === 'tel') {
84371                     updatePhonePlaceholder();
84372
84373                 } else if (field.type === 'number') {
84374                     var rtl = (_mainLocalizer.textDirection() === 'rtl');
84375
84376                     input.attr('type', 'text');
84377
84378                     var buttons = wrap.selectAll('.increment, .decrement')
84379                         .data(rtl ? [1, -1] : [-1, 1]);
84380
84381                     buttons.enter()
84382                         .append('button')
84383                         .attr('tabindex', -1)
84384                         .attr('class', function(d) {
84385                             var which = (d === 1 ? 'increment' : 'decrement');
84386                             return 'form-field-button ' + which;
84387                         })
84388                         .merge(buttons)
84389                         .on('click', function(d) {
84390                             event.preventDefault();
84391                             var raw_vals = input.node().value || '0';
84392                             var vals = raw_vals.split(';');
84393                             vals = vals.map(function(v) {
84394                                 var num = parseFloat(v.trim(), 10);
84395                                 return isFinite(num) ? clamped(num + d) : v.trim();
84396                             });
84397                             input.node().value = vals.join(';');
84398                             change()();
84399                         });
84400                 } else if (field.type === 'identifier' && field.urlFormat && field.pattern) {
84401
84402                     input.attr('type', 'text');
84403
84404                     outlinkButton = wrap.selectAll('.foreign-id-permalink')
84405                         .data([0]);
84406
84407                     outlinkButton.enter()
84408                         .append('button')
84409                         .attr('tabindex', -1)
84410                         .call(svgIcon('#iD-icon-out-link'))
84411                         .attr('class', 'form-field-button foreign-id-permalink')
84412                         .attr('title', function() {
84413                             var domainResults = /^https?:\/\/(.{1,}?)\//.exec(field.urlFormat);
84414                             if (domainResults.length >= 2 && domainResults[1]) {
84415                                 var domain = domainResults[1];
84416                                 return _t('icons.view_on', { domain: domain });
84417                             }
84418                             return '';
84419                         })
84420                         .on('click', function() {
84421                             event.preventDefault();
84422
84423                             var value = validIdentifierValueForLink();
84424                             if (value) {
84425                                 var url = field.urlFormat.replace(/{value}/, encodeURIComponent(value));
84426                                 window.open(url, '_blank');
84427                             }
84428                         })
84429                         .merge(outlinkButton);
84430                 }
84431             }
84432
84433
84434             function updatePhonePlaceholder() {
84435                 if (input.empty() || !Object.keys(_phoneFormats).length) return;
84436
84437                 var extent = combinedEntityExtent();
84438                 var countryCode = extent && iso1A2Code(extent.center());
84439                 var format = countryCode && _phoneFormats[countryCode.toLowerCase()];
84440                 if (format) input.attr('placeholder', format);
84441             }
84442
84443
84444             function validIdentifierValueForLink() {
84445                 if (field.type === 'identifier' && field.pattern) {
84446                     var value = utilGetSetValue(input).trim().split(';')[0];
84447                     return value && value.match(new RegExp(field.pattern));
84448                 }
84449                 return null;
84450             }
84451
84452
84453             // clamp number to min/max
84454             function clamped(num) {
84455                 if (field.minValue !== undefined) {
84456                     num = Math.max(num, field.minValue);
84457                 }
84458                 if (field.maxValue !== undefined) {
84459                     num = Math.min(num, field.maxValue);
84460                 }
84461                 return num;
84462             }
84463
84464
84465             function change(onInput) {
84466                 return function() {
84467                     var t = {};
84468                     var val = utilGetSetValue(input);
84469                     if (!onInput) val = context.cleanTagValue(val);
84470
84471                     // don't override multiple values with blank string
84472                     if (!val && Array.isArray(_tags[field.key])) return;
84473
84474                     if (!onInput) {
84475                         if (field.type === 'number' && val) {
84476                             var vals = val.split(';');
84477                             vals = vals.map(function(v) {
84478                                 var num = parseFloat(v.trim(), 10);
84479                                 return isFinite(num) ? clamped(num) : v.trim();
84480                             });
84481                             val = vals.join(';');
84482                         }
84483                         utilGetSetValue(input, val);
84484                     }
84485                     t[field.key] = val || undefined;
84486                     dispatch$1.call('change', this, t, onInput);
84487                 };
84488             }
84489
84490
84491             i.entityIDs = function(val) {
84492                 if (!arguments.length) return _entityIDs;
84493                 _entityIDs = val;
84494                 return i;
84495             };
84496
84497
84498             i.tags = function(tags) {
84499                 _tags = tags;
84500
84501                 var isMixed = Array.isArray(tags[field.key]);
84502
84503                 utilGetSetValue(input, !isMixed && tags[field.key] ? tags[field.key] : '')
84504                     .attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : undefined)
84505                     .attr('placeholder', isMixed ? _t('inspector.multiple_values') : (field.placeholder() || _t('inspector.unknown')))
84506                     .classed('mixed', isMixed);
84507
84508                 if (outlinkButton && !outlinkButton.empty()) {
84509                     var disabled = !validIdentifierValueForLink();
84510                     outlinkButton.classed('disabled', disabled);
84511                 }
84512             };
84513
84514
84515             i.focus = function() {
84516                 var node = input.node();
84517                 if (node) node.focus();
84518             };
84519
84520             function combinedEntityExtent() {
84521                 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
84522             }
84523
84524             return utilRebind(i, dispatch$1, 'on');
84525         }
84526
84527         function uiFieldAccess(field, context) {
84528             var dispatch$1 = dispatch('change');
84529             var items = select(null);
84530             var _tags;
84531
84532             function access(selection) {
84533                 var wrap = selection.selectAll('.form-field-input-wrap')
84534                     .data([0]);
84535
84536                 wrap = wrap.enter()
84537                     .append('div')
84538                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
84539                     .merge(wrap);
84540
84541                 var list = wrap.selectAll('ul')
84542                     .data([0]);
84543
84544                 list = list.enter()
84545                     .append('ul')
84546                     .attr('class', 'rows')
84547                     .merge(list);
84548
84549
84550                 items = list.selectAll('li')
84551                     .data(field.keys);
84552
84553                 // Enter
84554                 var enter = items.enter()
84555                     .append('li')
84556                     .attr('class', function(d) { return 'labeled-input preset-access-' + d; });
84557
84558                 enter
84559                     .append('span')
84560                     .attr('class', 'label preset-label-access')
84561                     .attr('for', function(d) { return 'preset-input-access-' + d; })
84562                     .text(function(d) { return field.t('types.' + d); });
84563
84564                 enter
84565                     .append('div')
84566                     .attr('class', 'preset-input-access-wrap')
84567                     .append('input')
84568                     .attr('type', 'text')
84569                     .attr('class', function(d) { return 'preset-input-access preset-input-access-' + d; })
84570                     .call(utilNoAuto)
84571                     .each(function(d) {
84572                         select(this)
84573                             .call(uiCombobox(context, 'access-' + d)
84574                                 .data(access.options(d))
84575                             );
84576                     });
84577
84578
84579                 // Update
84580                 items = items.merge(enter);
84581
84582                 wrap.selectAll('.preset-input-access')
84583                     .on('change', change)
84584                     .on('blur', change);
84585             }
84586
84587
84588             function change(d) {
84589                 var tag = {};
84590                 var value = context.cleanTagValue(utilGetSetValue(select(this)));
84591
84592                 // don't override multiple values with blank string
84593                 if (!value && typeof _tags[d] !== 'string') return;
84594
84595                 tag[d] = value || undefined;
84596                 dispatch$1.call('change', this, tag);
84597             }
84598
84599
84600             access.options = function(type) {
84601                 var options = ['no', 'permissive', 'private', 'permit', 'destination'];
84602
84603                 if (type !== 'access') {
84604                     options.unshift('yes');
84605                     options.push('designated');
84606
84607                     if (type === 'bicycle') {
84608                         options.push('dismount');
84609                     }
84610                 }
84611
84612                 return options.map(function(option) {
84613                     return {
84614                         title: field.t('options.' + option + '.description'),
84615                         value: option
84616                     };
84617                 });
84618             };
84619
84620
84621             var placeholdersByHighway = {
84622                 footway: {
84623                     foot: 'designated',
84624                     motor_vehicle: 'no'
84625                 },
84626                 steps: {
84627                     foot: 'yes',
84628                     motor_vehicle: 'no',
84629                     bicycle: 'no',
84630                     horse: 'no'
84631                 },
84632                 pedestrian: {
84633                     foot: 'yes',
84634                     motor_vehicle: 'no'
84635                 },
84636                 cycleway: {
84637                     motor_vehicle: 'no',
84638                     bicycle: 'designated'
84639                 },
84640                 bridleway: {
84641                     motor_vehicle: 'no',
84642                     horse: 'designated'
84643                 },
84644                 path: {
84645                     foot: 'yes',
84646                     motor_vehicle: 'no',
84647                     bicycle: 'yes',
84648                     horse: 'yes'
84649                 },
84650                 motorway: {
84651                     foot: 'no',
84652                     motor_vehicle: 'yes',
84653                     bicycle: 'no',
84654                     horse: 'no'
84655                 },
84656                 trunk: {
84657                     motor_vehicle: 'yes'
84658                 },
84659                 primary: {
84660                     foot: 'yes',
84661                     motor_vehicle: 'yes',
84662                     bicycle: 'yes',
84663                     horse: 'yes'
84664                 },
84665                 secondary: {
84666                     foot: 'yes',
84667                     motor_vehicle: 'yes',
84668                     bicycle: 'yes',
84669                     horse: 'yes'
84670                 },
84671                 tertiary: {
84672                     foot: 'yes',
84673                     motor_vehicle: 'yes',
84674                     bicycle: 'yes',
84675                     horse: 'yes'
84676                 },
84677                 residential: {
84678                     foot: 'yes',
84679                     motor_vehicle: 'yes',
84680                     bicycle: 'yes',
84681                     horse: 'yes'
84682                 },
84683                 unclassified: {
84684                     foot: 'yes',
84685                     motor_vehicle: 'yes',
84686                     bicycle: 'yes',
84687                     horse: 'yes'
84688                 },
84689                 service: {
84690                     foot: 'yes',
84691                     motor_vehicle: 'yes',
84692                     bicycle: 'yes',
84693                     horse: 'yes'
84694                 },
84695                 motorway_link: {
84696                     foot: 'no',
84697                     motor_vehicle: 'yes',
84698                     bicycle: 'no',
84699                     horse: 'no'
84700                 },
84701                 trunk_link: {
84702                     motor_vehicle: 'yes'
84703                 },
84704                 primary_link: {
84705                     foot: 'yes',
84706                     motor_vehicle: 'yes',
84707                     bicycle: 'yes',
84708                     horse: 'yes'
84709                 },
84710                 secondary_link: {
84711                     foot: 'yes',
84712                     motor_vehicle: 'yes',
84713                     bicycle: 'yes',
84714                     horse: 'yes'
84715                 },
84716                 tertiary_link: {
84717                     foot: 'yes',
84718                     motor_vehicle: 'yes',
84719                     bicycle: 'yes',
84720                     horse: 'yes'
84721                 }
84722             };
84723
84724
84725             access.tags = function(tags) {
84726                 _tags = tags;
84727
84728                 utilGetSetValue(items.selectAll('.preset-input-access'), function(d) {
84729                         return typeof tags[d] === 'string' ? tags[d] : '';
84730                     })
84731                     .classed('mixed', function(d) {
84732                         return tags[d] && Array.isArray(tags[d]);
84733                     })
84734                     .attr('title', function(d) {
84735                         return tags[d] && Array.isArray(tags[d]) && tags[d].filter(Boolean).join('\n');
84736                     })
84737                     .attr('placeholder', function(d) {
84738                         if (tags[d] && Array.isArray(tags[d])) {
84739                             return _t('inspector.multiple_values');
84740                         }
84741                         if (d === 'access') {
84742                             return 'yes';
84743                         }
84744                         if (tags.access && typeof tags.access === 'string') {
84745                             return tags.access;
84746                         }
84747                         if (tags.highway) {
84748                             if (typeof tags.highway === 'string') {
84749                                 if (placeholdersByHighway[tags.highway] &&
84750                                     placeholdersByHighway[tags.highway][d]) {
84751
84752                                     return placeholdersByHighway[tags.highway][d];
84753                                 }
84754                             } else {
84755                                 var impliedAccesses = tags.highway.filter(Boolean).map(function(highwayVal) {
84756                                     return placeholdersByHighway[highwayVal] && placeholdersByHighway[highwayVal][d];
84757                                 }).filter(Boolean);
84758
84759                                 if (impliedAccesses.length === tags.highway.length &&
84760                                     new Set(impliedAccesses).size === 1) {
84761                                     // if all the highway values have the same implied access for this type then use that
84762                                     return impliedAccesses[0];
84763                                 }
84764                             }
84765                         }
84766                         return field.placeholder();
84767                     });
84768             };
84769
84770
84771             access.focus = function() {
84772                 items.selectAll('.preset-input-access')
84773                     .node().focus();
84774             };
84775
84776
84777             return utilRebind(access, dispatch$1, 'on');
84778         }
84779
84780         function uiFieldAddress(field, context) {
84781             var dispatch$1 = dispatch('change');
84782             var _selection = select(null);
84783             var _wrap = select(null);
84784             var addrField = _mainPresetIndex.field('address');   // needed for placeholder strings
84785
84786             var _entityIDs = [];
84787             var _tags;
84788             var _countryCode;
84789             var _addressFormats = [{
84790                 format: [
84791                     ['housenumber', 'street'],
84792                     ['city', 'postcode']
84793                 ]
84794               }];
84795
84796             _mainFileFetcher.get('address_formats')
84797                 .then(function(d) {
84798                     _addressFormats = d;
84799                     if (!_selection.empty()) {
84800                         _selection.call(address);
84801                     }
84802                 })
84803                 .catch(function() { /* ignore */ });
84804
84805
84806             function getNearStreets() {
84807                 var extent = combinedEntityExtent();
84808                 var l = extent.center();
84809                 var box = geoExtent(l).padByMeters(200);
84810
84811                 var streets = context.history().intersects(box)
84812                     .filter(isAddressable)
84813                     .map(function(d) {
84814                         var loc = context.projection([
84815                             (extent[0][0] + extent[1][0]) / 2,
84816                             (extent[0][1] + extent[1][1]) / 2
84817                         ]);
84818                         var choice = geoChooseEdge(context.graph().childNodes(d), loc, context.projection);
84819
84820                         return {
84821                             title: d.tags.name,
84822                             value: d.tags.name,
84823                             dist: choice.distance
84824                         };
84825                     })
84826                     .sort(function(a, b) {
84827                         return a.dist - b.dist;
84828                     });
84829
84830                 return utilArrayUniqBy(streets, 'value');
84831
84832                 function isAddressable(d) {
84833                     return d.tags.highway && d.tags.name && d.type === 'way';
84834                 }
84835             }
84836
84837
84838             function getNearCities() {
84839                 var extent = combinedEntityExtent();
84840                 var l = extent.center();
84841                 var box = geoExtent(l).padByMeters(200);
84842
84843                 var cities = context.history().intersects(box)
84844                     .filter(isAddressable)
84845                     .map(function(d) {
84846                         return {
84847                             title: d.tags['addr:city'] || d.tags.name,
84848                             value: d.tags['addr:city'] || d.tags.name,
84849                             dist: geoSphericalDistance(d.extent(context.graph()).center(), l)
84850                         };
84851                     })
84852                     .sort(function(a, b) {
84853                         return a.dist - b.dist;
84854                     });
84855
84856                 return utilArrayUniqBy(cities, 'value');
84857
84858
84859                 function isAddressable(d) {
84860                     if (d.tags.name) {
84861                         if (d.tags.admin_level === '8' && d.tags.boundary === 'administrative')
84862                             return true;
84863                         if (d.tags.border_type === 'city')
84864                             return true;
84865                         if (d.tags.place === 'city' || d.tags.place === 'town' || d.tags.place === 'village')
84866                             return true;
84867                     }
84868
84869                     if (d.tags['addr:city'])
84870                         return true;
84871
84872                     return false;
84873                 }
84874             }
84875
84876             function getNearValues(key) {
84877                 var extent = combinedEntityExtent();
84878                 var l = extent.center();
84879                 var box = geoExtent(l).padByMeters(200);
84880
84881                 var results = context.history().intersects(box)
84882                     .filter(function hasTag(d) { return _entityIDs.indexOf(d.id) === -1 && d.tags[key]; })
84883                     .map(function(d) {
84884                         return {
84885                             title: d.tags[key],
84886                             value: d.tags[key],
84887                             dist: geoSphericalDistance(d.extent(context.graph()).center(), l)
84888                         };
84889                     })
84890                     .sort(function(a, b) {
84891                         return a.dist - b.dist;
84892                     });
84893
84894                 return utilArrayUniqBy(results, 'value');
84895             }
84896
84897
84898             function updateForCountryCode() {
84899
84900                 if (!_countryCode) return;
84901
84902                 var addressFormat;
84903                 for (var i = 0; i < _addressFormats.length; i++) {
84904                     var format = _addressFormats[i];
84905                     if (!format.countryCodes) {
84906                         addressFormat = format;   // choose the default format, keep going
84907                     } else if (format.countryCodes.indexOf(_countryCode) !== -1) {
84908                         addressFormat = format;   // choose the country format, stop here
84909                         break;
84910                     }
84911                 }
84912
84913                 var dropdowns = addressFormat.dropdowns || [
84914                     'city', 'county', 'country', 'district', 'hamlet',
84915                     'neighbourhood', 'place', 'postcode', 'province',
84916                     'quarter', 'state', 'street', 'subdistrict', 'suburb'
84917                 ];
84918
84919                 var widths = addressFormat.widths || {
84920                     housenumber: 1/3, street: 2/3,
84921                     city: 2/3, state: 1/4, postcode: 1/3
84922                 };
84923
84924                 function row(r) {
84925                     // Normalize widths.
84926                     var total = r.reduce(function(sum, key) {
84927                         return sum + (widths[key] || 0.5);
84928                     }, 0);
84929
84930                     return r.map(function(key) {
84931                         return {
84932                             id: key,
84933                             width: (widths[key] || 0.5) / total
84934                         };
84935                     });
84936                 }
84937
84938                 var rows = _wrap.selectAll('.addr-row')
84939                     .data(addressFormat.format, function(d) {
84940                         return d.toString();
84941                     });
84942
84943                 rows.exit()
84944                     .remove();
84945
84946                 rows
84947                     .enter()
84948                     .append('div')
84949                     .attr('class', 'addr-row')
84950                     .selectAll('input')
84951                     .data(row)
84952                     .enter()
84953                     .append('input')
84954                     .property('type', 'text')
84955                     .call(updatePlaceholder)
84956                     .attr('class', function (d) { return 'addr-' + d.id; })
84957                     .call(utilNoAuto)
84958                     .each(addDropdown)
84959                     .style('width', function (d) { return d.width * 100 + '%'; });
84960
84961
84962                 function addDropdown(d) {
84963                     if (dropdowns.indexOf(d.id) === -1) return;  // not a dropdown
84964
84965                     var nearValues = (d.id === 'street') ? getNearStreets
84966                         : (d.id === 'city') ? getNearCities
84967                         : getNearValues;
84968
84969                     select(this)
84970                         .call(uiCombobox(context, 'address-' + d.id)
84971                             .minItems(1)
84972                             .caseSensitive(true)
84973                             .fetcher(function(value, callback) {
84974                                 callback(nearValues('addr:' + d.id));
84975                             })
84976                         );
84977                 }
84978
84979                 _wrap.selectAll('input')
84980                     .on('blur', change())
84981                     .on('change', change());
84982
84983                 _wrap.selectAll('input:not(.combobox-input)')
84984                     .on('input', change(true));
84985
84986                 if (_tags) updateTags(_tags);
84987             }
84988
84989
84990             function address(selection) {
84991                 _selection = selection;
84992
84993                 _wrap = selection.selectAll('.form-field-input-wrap')
84994                     .data([0]);
84995
84996                 _wrap = _wrap.enter()
84997                     .append('div')
84998                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
84999                     .merge(_wrap);
85000
85001                 var extent = combinedEntityExtent();
85002
85003                 if (extent) {
85004                     var countryCode;
85005                     if (context.inIntro()) {
85006                         // localize the address format for the walkthrough
85007                         countryCode = _t('intro.graph.countrycode');
85008                     } else {
85009                         var center = extent.center();
85010                         countryCode = iso1A2Code(center);
85011                     }
85012                     if (countryCode) {
85013                         _countryCode = countryCode.toLowerCase();
85014                         updateForCountryCode();
85015                     }
85016                 }
85017             }
85018
85019
85020             function change(onInput) {
85021                 return function() {
85022                     var tags = {};
85023
85024                     _wrap.selectAll('input')
85025                         .each(function (subfield) {
85026                             var key = field.key + ':' + subfield.id;
85027
85028                             var value = this.value;
85029                             if (!onInput) value = context.cleanTagValue(value);
85030
85031                             // don't override multiple values with blank string
85032                             if (Array.isArray(_tags[key]) && !value) return;
85033
85034                             tags[key] = value || undefined;
85035                         });
85036
85037                     dispatch$1.call('change', this, tags, onInput);
85038                 };
85039             }
85040
85041             function updatePlaceholder(inputSelection) {
85042                 return inputSelection.attr('placeholder', function(subfield) {
85043                     if (_tags && Array.isArray(_tags[field.key + ':' + subfield.id])) {
85044                         return _t('inspector.multiple_values');
85045                     }
85046                     if (_countryCode) {
85047                         var localkey = subfield.id + '!' + _countryCode;
85048                         var tkey = addrField.strings.placeholders[localkey] ? localkey : subfield.id;
85049                         return addrField.t('placeholders.' + tkey);
85050                     }
85051                 });
85052             }
85053
85054
85055             function updateTags(tags) {
85056                 utilGetSetValue(_wrap.selectAll('input'), function (subfield) {
85057                         var val = tags[field.key + ':' + subfield.id];
85058                         return typeof val === 'string' ? val : '';
85059                     })
85060                     .attr('title', function(subfield) {
85061                         var val = tags[field.key + ':' + subfield.id];
85062                         return val && Array.isArray(val) && val.filter(Boolean).join('\n');
85063                     })
85064                     .classed('mixed', function(subfield) {
85065                         return Array.isArray(tags[field.key + ':' + subfield.id]);
85066                     })
85067                     .call(updatePlaceholder);
85068             }
85069
85070
85071             function combinedEntityExtent() {
85072                 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
85073             }
85074
85075
85076             address.entityIDs = function(val) {
85077                 if (!arguments.length) return _entityIDs;
85078                 _entityIDs = val;
85079                 return address;
85080             };
85081
85082
85083             address.tags = function(tags) {
85084                 _tags = tags;
85085                 updateTags(tags);
85086             };
85087
85088
85089             address.focus = function() {
85090                 var node = _wrap.selectAll('input').node();
85091                 if (node) node.focus();
85092             };
85093
85094
85095             return utilRebind(address, dispatch$1, 'on');
85096         }
85097
85098         function uiFieldCycleway(field, context) {
85099             var dispatch$1 = dispatch('change');
85100             var items = select(null);
85101             var wrap = select(null);
85102             var _tags;
85103
85104             function cycleway(selection) {
85105
85106                 function stripcolon(s) {
85107                     return s.replace(':', '');
85108                 }
85109
85110
85111                 wrap = selection.selectAll('.form-field-input-wrap')
85112                     .data([0]);
85113
85114                 wrap = wrap.enter()
85115                     .append('div')
85116                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
85117                     .merge(wrap);
85118
85119
85120                 var div = wrap.selectAll('ul')
85121                     .data([0]);
85122
85123                 div = div.enter()
85124                     .append('ul')
85125                     .attr('class', 'rows')
85126                     .merge(div);
85127
85128                 var keys = ['cycleway:left', 'cycleway:right'];
85129
85130                 items = div.selectAll('li')
85131                     .data(keys);
85132
85133                 var enter = items.enter()
85134                     .append('li')
85135                     .attr('class', function(d) { return 'labeled-input preset-cycleway-' + stripcolon(d); });
85136
85137                 enter
85138                     .append('span')
85139                     .attr('class', 'label preset-label-cycleway')
85140                     .attr('for', function(d) { return 'preset-input-cycleway-' + stripcolon(d); })
85141                     .text(function(d) { return field.t('types.' + d); });
85142
85143                 enter
85144                     .append('div')
85145                     .attr('class', 'preset-input-cycleway-wrap')
85146                     .append('input')
85147                     .attr('type', 'text')
85148                     .attr('class', function(d) { return 'preset-input-cycleway preset-input-' + stripcolon(d); })
85149                     .call(utilNoAuto)
85150                     .each(function(d) {
85151                         select(this)
85152                             .call(uiCombobox(context, 'cycleway-' + stripcolon(d))
85153                                 .data(cycleway.options(d))
85154                             );
85155                     });
85156
85157                 items = items.merge(enter);
85158
85159                 // Update
85160                 wrap.selectAll('.preset-input-cycleway')
85161                     .on('change', change)
85162                     .on('blur', change);
85163             }
85164
85165
85166             function change(key) {
85167
85168                 var newValue = context.cleanTagValue(utilGetSetValue(select(this)));
85169
85170                 // don't override multiple values with blank string
85171                 if (!newValue && (Array.isArray(_tags.cycleway) || Array.isArray(_tags[key]))) return;
85172
85173                 if (newValue === 'none' || newValue === '') { newValue = undefined; }
85174
85175                 var otherKey = key === 'cycleway:left' ? 'cycleway:right' : 'cycleway:left';
85176                 var otherValue = typeof _tags.cycleway === 'string' ? _tags.cycleway : _tags[otherKey];
85177                 if (otherValue && Array.isArray(otherValue)) {
85178                     // we must always have an explicit value for comparison
85179                     otherValue = otherValue[0];
85180                 }
85181                 if (otherValue === 'none' || otherValue === '') { otherValue = undefined; }
85182
85183                 var tag = {};
85184
85185                 // If the left and right tags match, use the cycleway tag to tag both
85186                 // sides the same way
85187                 if (newValue === otherValue) {
85188                     tag = {
85189                         cycleway: newValue,
85190                         'cycleway:left': undefined,
85191                         'cycleway:right': undefined
85192                     };
85193                 } else {
85194                     // Always set both left and right as changing one can affect the other
85195                     tag = {
85196                         cycleway: undefined
85197                     };
85198                     tag[key] = newValue;
85199                     tag[otherKey] = otherValue;
85200                 }
85201
85202                 dispatch$1.call('change', this, tag);
85203             }
85204
85205
85206             cycleway.options = function() {
85207                 return Object.keys(field.strings.options).map(function(option) {
85208                     return {
85209                         title: field.t('options.' + option + '.description'),
85210                         value: option
85211                     };
85212                 });
85213             };
85214
85215
85216             cycleway.tags = function(tags) {
85217                 _tags = tags;
85218
85219                 // If cycleway is set, use that instead of individual values
85220                 var commonValue = typeof tags.cycleway === 'string' && tags.cycleway;
85221
85222                 utilGetSetValue(items.selectAll('.preset-input-cycleway'), function(d) {
85223                         if (commonValue) return commonValue;
85224                         return !tags.cycleway && typeof tags[d] === 'string' ? tags[d] : '';
85225                     })
85226                     .attr('title', function(d) {
85227                         if (Array.isArray(tags.cycleway) || Array.isArray(tags[d])) {
85228                             var vals = [];
85229                             if (Array.isArray(tags.cycleway)) {
85230                                 vals = vals.concat(tags.cycleway);
85231                             }
85232                             if (Array.isArray(tags[d])) {
85233                                 vals = vals.concat(tags[d]);
85234                             }
85235                             return vals.filter(Boolean).join('\n');
85236                         }
85237                         return null;
85238                     })
85239                     .attr('placeholder', function(d) {
85240                         if (Array.isArray(tags.cycleway) || Array.isArray(tags[d])) {
85241                             return _t('inspector.multiple_values');
85242                         }
85243                         return field.placeholder();
85244                     })
85245                     .classed('mixed', function(d) {
85246                         return Array.isArray(tags.cycleway) || Array.isArray(tags[d]);
85247                     });
85248             };
85249
85250
85251             cycleway.focus = function() {
85252                 var node = wrap.selectAll('input').node();
85253                 if (node) node.focus();
85254             };
85255
85256
85257             return utilRebind(cycleway, dispatch$1, 'on');
85258         }
85259
85260         function uiFieldLanes(field, context) {
85261             var dispatch$1 = dispatch('change');
85262             var LANE_WIDTH = 40;
85263             var LANE_HEIGHT = 200;
85264             var _entityIDs = [];
85265
85266             function lanes(selection) {
85267                 var lanesData = context.entity(_entityIDs[0]).lanes();
85268
85269                 if (!context.container().select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode) {
85270                     selection.call(lanes.off);
85271                     return;
85272                 }
85273
85274                 var wrap = selection.selectAll('.form-field-input-wrap')
85275                     .data([0]);
85276
85277                 wrap = wrap.enter()
85278                     .append('div')
85279                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
85280                     .merge(wrap);
85281
85282                 var surface =  wrap.selectAll('.surface')
85283                     .data([0]);
85284
85285                 var d = utilGetDimensions(wrap);
85286                 var freeSpace = d[0] - lanesData.lanes.length * LANE_WIDTH * 1.5 + LANE_WIDTH * 0.5;
85287
85288                 surface = surface.enter()
85289                     .append('svg')
85290                     .attr('width', d[0])
85291                     .attr('height', 300)
85292                     .attr('class', 'surface')
85293                     .merge(surface);
85294
85295
85296                 var lanesSelection = surface.selectAll('.lanes')
85297                     .data([0]);
85298
85299                 lanesSelection = lanesSelection.enter()
85300                     .append('g')
85301                     .attr('class', 'lanes')
85302                     .merge(lanesSelection);
85303
85304                 lanesSelection
85305                     .attr('transform', function () {
85306                         return 'translate(' + (freeSpace / 2) + ', 0)';
85307                     });
85308
85309
85310                 var lane = lanesSelection.selectAll('.lane')
85311                    .data(lanesData.lanes);
85312
85313                 lane.exit()
85314                     .remove();
85315
85316                 var enter = lane.enter()
85317                     .append('g')
85318                     .attr('class', 'lane');
85319
85320                 enter
85321                     .append('g')
85322                     .append('rect')
85323                     .attr('y', 50)
85324                     .attr('width', LANE_WIDTH)
85325                     .attr('height', LANE_HEIGHT);
85326
85327                 enter
85328                     .append('g')
85329                     .attr('class', 'forward')
85330                     .append('text')
85331                     .attr('y', 40)
85332                     .attr('x', 14)
85333                     .text('▲');
85334
85335                 enter
85336                     .append('g')
85337                     .attr('class', 'bothways')
85338                     .append('text')
85339                     .attr('y', 40)
85340                     .attr('x', 14)
85341                     .text('▲▼');
85342
85343                 enter
85344                     .append('g')
85345                     .attr('class', 'backward')
85346                     .append('text')
85347                     .attr('y', 40)
85348                     .attr('x', 14)
85349                     .text('▼');
85350
85351
85352                 lane = lane
85353                     .merge(enter);
85354
85355                 lane
85356                     .attr('transform', function(d) {
85357                         return 'translate(' + (LANE_WIDTH * d.index * 1.5) + ', 0)';
85358                     });
85359
85360                 lane.select('.forward')
85361                     .style('visibility', function(d) {
85362                         return d.direction === 'forward' ? 'visible' : 'hidden';
85363                     });
85364
85365                 lane.select('.bothways')
85366                     .style('visibility', function(d) {
85367                         return d.direction === 'bothways' ? 'visible' : 'hidden';
85368                     });
85369
85370                 lane.select('.backward')
85371                     .style('visibility', function(d) {
85372                         return d.direction === 'backward' ? 'visible' : 'hidden';
85373                     });
85374             }
85375
85376
85377             lanes.entityIDs = function(val) {
85378                 _entityIDs = val;
85379             };
85380
85381             lanes.tags = function() {};
85382             lanes.focus = function() {};
85383             lanes.off = function() {};
85384
85385             return utilRebind(lanes, dispatch$1, 'on');
85386         }
85387
85388         uiFieldLanes.supportsMultiselection = false;
85389
85390         var _languagesArray = [];
85391
85392
85393         function uiFieldLocalized(field, context) {
85394             var dispatch$1 = dispatch('change', 'input');
85395             var wikipedia = services.wikipedia;
85396             var input = select(null);
85397             var localizedInputs = select(null);
85398             var _countryCode;
85399             var _tags;
85400
85401
85402             // A concern here in switching to async data means that _languagesArray will not
85403             // be available the first time through, so things like the fetchers and
85404             // the language() function will not work immediately.
85405             _mainFileFetcher.get('languages')
85406                 .then(loadLanguagesArray)
85407                 .catch(function() { /* ignore */ });
85408
85409             var _territoryLanguages = {};
85410             _mainFileFetcher.get('territory_languages')
85411                 .then(function(d) { _territoryLanguages = d; })
85412                 .catch(function() { /* ignore */ });
85413
85414
85415             var allSuggestions = _mainPresetIndex.collection.filter(function(p) {
85416                 return p.suggestion === true;
85417             });
85418
85419             // reuse these combos
85420             var langCombo = uiCombobox(context, 'localized-lang')
85421                 .fetcher(fetchLanguages)
85422                 .minItems(0);
85423
85424             var brandCombo = uiCombobox(context, 'localized-brand')
85425                 .canAutocomplete(false)
85426                 .minItems(1);
85427
85428             var _selection = select(null);
85429             var _multilingual = [];
85430             var _buttonTip = uiTooltip()
85431                 .title(_t('translate.translate'))
85432                 .placement('left');
85433             var _wikiTitles;
85434             var _entityIDs = [];
85435
85436
85437             function loadLanguagesArray(dataLanguages) {
85438                 if (_languagesArray.length !== 0) return;
85439
85440                 // some conversion is needed to ensure correct OSM tags are used
85441                 var replacements = {
85442                     sr: 'sr-Cyrl',      // in OSM, `sr` implies Cyrillic
85443                     'sr-Cyrl': false    // `sr-Cyrl` isn't used in OSM
85444                 };
85445
85446                 for (var code in dataLanguages) {
85447                     if (replacements[code] === false) continue;
85448                     var metaCode = code;
85449                     if (replacements[code]) metaCode = replacements[code];
85450
85451                     _languagesArray.push({
85452                         localName: _mainLocalizer.languageName(metaCode, { localOnly: true }),
85453                         nativeName: dataLanguages[metaCode].nativeName,
85454                         code: code,
85455                         label: _mainLocalizer.languageName(metaCode)
85456                     });
85457                 }
85458             }
85459
85460
85461             function calcLocked() {
85462
85463                 // only lock the Name field
85464                 var isLocked = field.id === 'name' &&
85465                     _entityIDs.length &&
85466                     // lock the field if any feature needs it
85467                     _entityIDs.some(function(entityID) {
85468
85469                         var entity = context.graph().hasEntity(entityID);
85470                         if (!entity) return false;
85471
85472                         var original = context.graph().base().entities[_entityIDs[0]];
85473                         var hasOriginalName = original && entity.tags.name && entity.tags.name === original.tags.name;
85474                         // if the name was already edited manually then allow further editing
85475                         if (!hasOriginalName) return false;
85476
85477                         // features linked to Wikidata are likely important and should be protected
85478                         if (entity.tags.wikidata) return true;
85479
85480                         // assume the name has already been confirmed if its source has been researched
85481                         if (entity.tags['name:etymology:wikidata']) return true;
85482
85483                         var preset = _mainPresetIndex.match(entity, context.graph());
85484                         var isSuggestion = preset && preset.suggestion;
85485                         var showsBrand = preset && preset.originalFields.filter(function(d) {
85486                             return d.id === 'brand';
85487                         }).length;
85488                         // protect standardized brand names
85489                         return isSuggestion && !showsBrand;
85490                     });
85491
85492                 field.locked(isLocked);
85493             }
85494
85495
85496             // update _multilingual, maintaining the existing order
85497             function calcMultilingual(tags) {
85498                 var existingLangsOrdered = _multilingual.map(function(item) {
85499                     return item.lang;
85500                 });
85501                 var existingLangs = new Set(existingLangsOrdered.filter(Boolean));
85502
85503                 for (var k in tags) {
85504                     var m = k.match(/^(.*):([a-zA-Z_-]+)$/);
85505                     if (m && m[1] === field.key && m[2]) {
85506                         var item = { lang: m[2], value: tags[k] };
85507                         if (existingLangs.has(item.lang)) {
85508                             // update the value
85509                             _multilingual[existingLangsOrdered.indexOf(item.lang)].value = item.value;
85510                             existingLangs.delete(item.lang);
85511                         } else {
85512                             _multilingual.push(item);
85513                         }
85514                     }
85515                 }
85516
85517                 _multilingual = _multilingual.filter(function(item) {
85518                     return !item.lang || !existingLangs.has(item.lang);
85519                 });
85520             }
85521
85522
85523             function localized(selection) {
85524                 _selection = selection;
85525                 calcLocked();
85526                 var isLocked = field.locked();
85527                 var singularEntity = _entityIDs.length === 1 && context.hasEntity(_entityIDs[0]);
85528                 var preset = singularEntity && _mainPresetIndex.match(singularEntity, context.graph());
85529
85530                 var wrap = selection.selectAll('.form-field-input-wrap')
85531                     .data([0]);
85532
85533                 // enter/update
85534                 wrap = wrap.enter()
85535                     .append('div')
85536                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
85537                     .merge(wrap);
85538
85539                 input = wrap.selectAll('.localized-main')
85540                     .data([0]);
85541
85542                 // enter/update
85543                 input = input.enter()
85544                     .append('input')
85545                     .attr('type', 'text')
85546                     .attr('id', field.domId)
85547                     .attr('class', 'localized-main')
85548                     .call(utilNoAuto)
85549                     .merge(input);
85550
85551                 if (preset && field.id === 'name') {
85552                     var pTag = preset.id.split('/', 2);
85553                     var pKey = pTag[0];
85554                     var pValue = pTag[1];
85555
85556                     if (!preset.suggestion) {
85557                         // Not a suggestion preset - Add a suggestions dropdown if it makes sense to.
85558                         // This code attempts to determine if the matched preset is the
85559                         // kind of preset that even can benefit from name suggestions..
85560                         // - true = shops, cafes, hotels, etc. (also generic and fallback presets)
85561                         // - false = churches, parks, hospitals, etc. (things not in the index)
85562                         var isFallback = preset.isFallback();
85563                         var goodSuggestions = allSuggestions.filter(function(s) {
85564                             if (isFallback) return true;
85565                             var sTag = s.id.split('/', 2);
85566                             var sKey = sTag[0];
85567                             var sValue = sTag[1];
85568                             return pKey === sKey && (!pValue || pValue === sValue);
85569                         });
85570
85571                         // Show the suggestions.. If the user picks one, change the tags..
85572                         if (allSuggestions.length && goodSuggestions.length) {
85573                             input
85574                                 .on('blur.localized', checkBrandOnBlur)
85575                                 .call(brandCombo
85576                                     .fetcher(fetchBrandNames(preset, allSuggestions))
85577                                     .on('accept', acceptBrand)
85578                                     .on('cancel', cancelBrand)
85579                                 );
85580                         }
85581                     }
85582                 }
85583
85584                 input
85585                     .classed('disabled', !!isLocked)
85586                     .attr('readonly', isLocked || null)
85587                     .on('input', change(true))
85588                     .on('blur', change())
85589                     .on('change', change());
85590
85591
85592                 var translateButton = wrap.selectAll('.localized-add')
85593                     .data([0]);
85594
85595                 translateButton = translateButton.enter()
85596                     .append('button')
85597                     .attr('class', 'localized-add form-field-button')
85598                     .attr('tabindex', -1)
85599                     .call(svgIcon('#iD-icon-plus'))
85600                     .merge(translateButton);
85601
85602                 translateButton
85603                     .classed('disabled', !!isLocked)
85604                     .call(isLocked ? _buttonTip.destroy : _buttonTip)
85605                     .on('click', addNew);
85606
85607
85608                 if (_tags && !_multilingual.length) {
85609                     calcMultilingual(_tags);
85610                 }
85611
85612                 localizedInputs = selection.selectAll('.localized-multilingual')
85613                     .data([0]);
85614
85615                 localizedInputs = localizedInputs.enter()
85616                     .append('div')
85617                     .attr('class', 'localized-multilingual')
85618                     .merge(localizedInputs);
85619
85620                 localizedInputs
85621                     .call(renderMultilingual);
85622
85623                 localizedInputs.selectAll('button, input')
85624                     .classed('disabled', !!isLocked)
85625                     .attr('readonly', isLocked || null);
85626
85627
85628
85629                 // We are not guaranteed to get an `accept` or `cancel` when blurring the field.
85630                 // (This can happen if the user actives the combo, arrows down, and then clicks off to blur)
85631                 // So compare the current field value against the suggestions one last time.
85632                 function checkBrandOnBlur() {
85633                     var latest = _entityIDs.length === 1 && context.hasEntity(_entityIDs[0]);
85634                     if (!latest) return;   // deleting the entity blurred the field?
85635
85636                     var preset = _mainPresetIndex.match(latest, context.graph());
85637                     if (preset && preset.suggestion) return;   // already accepted
85638
85639                     // note: here we are testing against "decorated" names, i.e. 'Starbucks – Cafe'
85640                     var name = utilGetSetValue(input).trim();
85641                     var matched = allSuggestions.filter(function(s) { return name === s.name(); });
85642
85643                     if (matched.length === 1) {
85644                         acceptBrand({ suggestion: matched[0] });
85645                     } else {
85646                         cancelBrand();
85647                     }
85648                 }
85649
85650
85651                 function acceptBrand(d) {
85652
85653                     var entity = _entityIDs.length === 1 && context.hasEntity(_entityIDs[0]);
85654
85655                     if (!d || !entity) {
85656                         cancelBrand();
85657                         return;
85658                     }
85659
85660                     var tags = entity.tags;
85661                     var geometry = entity.geometry(context.graph());
85662                     var removed = preset.unsetTags(tags, geometry);
85663                     for (var k in tags) {
85664                         tags[k] = removed[k];  // set removed tags to `undefined`
85665                     }
85666                     tags = d.suggestion.setTags(tags, geometry);
85667                     utilGetSetValue(input, tags.name);
85668                     dispatch$1.call('change', this, tags);
85669                 }
85670
85671
85672                 // user hit escape, clean whatever preset name appears after the last ' – '
85673                 function cancelBrand() {
85674                     var name = utilGetSetValue(input);
85675                     var clean = cleanName(name);
85676                     if (clean !== name) {
85677                         utilGetSetValue(input, clean);
85678                         dispatch$1.call('change', this, { name: clean });
85679                     }
85680                 }
85681
85682                 // Remove whatever is after the last ' – '
85683                 // NOTE: split/join on en-dash, not a hypen (to avoid conflict with fr - nl names in Brussels etc)
85684                 function cleanName(name) {
85685                     var parts = name.split(' – ');
85686                     if (parts.length > 1) {
85687                         parts.pop();
85688                         name = parts.join(' – ');
85689                     }
85690                     return name;
85691                 }
85692
85693
85694                 function fetchBrandNames(preset, suggestions) {
85695                     var pTag = preset.id.split('/', 2);
85696                     var pKey = pTag[0];
85697                     var pValue = pTag[1];
85698
85699                     return function(value, callback) {
85700                         var results = [];
85701                         if (value && value.length > 2) {
85702                             for (var i = 0; i < suggestions.length; i++) {
85703                                 var s = suggestions[i];
85704
85705                                 // don't suggest brands from incompatible countries
85706                                 if (_countryCode && s.countryCodes &&
85707                                     s.countryCodes.indexOf(_countryCode) === -1) continue;
85708
85709                                 var sTag = s.id.split('/', 2);
85710                                 var sKey = sTag[0];
85711                                 var sValue = sTag[1];
85712                                 var name = s.name();
85713                                 var dist = utilEditDistance(value, name.substring(0, value.length));
85714                                 var matchesPreset = (pKey === sKey && (!pValue || pValue === sValue));
85715
85716                                 if (dist < 1 || (matchesPreset && dist < 3)) {
85717                                     var obj = {
85718                                         title: name,
85719                                         value: name,
85720                                         suggestion: s,
85721                                         dist: dist + (matchesPreset ? 0 : 1)  // penalize if not matched preset
85722                                     };
85723                                     results.push(obj);
85724                                 }
85725                             }
85726                             results.sort(function(a, b) { return a.dist - b.dist; });
85727                         }
85728                         results = results.slice(0, 10);
85729                         callback(results);
85730                     };
85731                 }
85732
85733
85734                 function addNew() {
85735                     event.preventDefault();
85736                     if (field.locked()) return;
85737
85738                     var defaultLang = _mainLocalizer.languageCode().toLowerCase();
85739                     var langExists = _multilingual.find(function(datum) { return datum.lang === defaultLang; });
85740                     var isLangEn = defaultLang.indexOf('en') > -1;
85741                     if (isLangEn || langExists) {
85742                         defaultLang = '';
85743                         langExists = _multilingual.find(function(datum) { return datum.lang === defaultLang; });
85744                     }
85745
85746                     if (!langExists) {
85747                         // prepend the value so it appears at the top
85748                         _multilingual.unshift({ lang: defaultLang, value: '' });
85749
85750                         localizedInputs
85751                             .call(renderMultilingual);
85752                     }
85753                 }
85754
85755
85756                 function change(onInput) {
85757                     return function() {
85758                         if (field.locked()) {
85759                             event.preventDefault();
85760                             return;
85761                         }
85762
85763                         var val = utilGetSetValue(select(this));
85764                         if (!onInput) val = context.cleanTagValue(val);
85765
85766                         // don't override multiple values with blank string
85767                         if (!val && Array.isArray(_tags[field.key])) return;
85768
85769                         var t = {};
85770
85771                         t[field.key] = val || undefined;
85772                         dispatch$1.call('change', this, t, onInput);
85773                     };
85774                 }
85775             }
85776
85777
85778             function key(lang) {
85779                 return field.key + ':' + lang;
85780             }
85781
85782
85783             function changeLang(d) {
85784                 var tags = {};
85785
85786                 // make sure unrecognized suffixes are lowercase - #7156
85787                 var lang = utilGetSetValue(select(this)).toLowerCase();
85788
85789                 var language = _languagesArray.find(function(d) {
85790                     return d.label.toLowerCase() === lang ||
85791                         (d.localName && d.localName.toLowerCase() === lang) ||
85792                         (d.nativeName && d.nativeName.toLowerCase() === lang);
85793                 });
85794                 if (language) lang = language.code;
85795
85796                 if (d.lang && d.lang !== lang) {
85797                     tags[key(d.lang)] = undefined;
85798                 }
85799
85800                 var newKey = lang && context.cleanTagKey(key(lang));
85801
85802                 var value = utilGetSetValue(select(this.parentNode).selectAll('.localized-value'));
85803
85804                 if (newKey && value) {
85805                     tags[newKey] = value;
85806                 } else if (newKey && _wikiTitles && _wikiTitles[d.lang]) {
85807                     tags[newKey] = _wikiTitles[d.lang];
85808                 }
85809
85810                 d.lang = lang;
85811                 dispatch$1.call('change', this, tags);
85812             }
85813
85814
85815             function changeValue(d) {
85816                 if (!d.lang) return;
85817                 var value = context.cleanTagValue(utilGetSetValue(select(this))) || undefined;
85818
85819                 // don't override multiple values with blank string
85820                 if (!value && Array.isArray(d.value)) return;
85821
85822                 var t = {};
85823                 t[key(d.lang)] = value;
85824                 d.value = value;
85825                 dispatch$1.call('change', this, t);
85826             }
85827
85828
85829             function fetchLanguages(value, cb) {
85830                 var v = value.toLowerCase();
85831
85832                 // show the user's language first
85833                 var langCodes = [_mainLocalizer.localeCode(), _mainLocalizer.languageCode()];
85834
85835                 if (_countryCode && _territoryLanguages[_countryCode]) {
85836                     langCodes = langCodes.concat(_territoryLanguages[_countryCode]);
85837                 }
85838
85839                 var langItems = [];
85840                 langCodes.forEach(function(code) {
85841                     var langItem = _languagesArray.find(function(item) {
85842                         return item.code === code;
85843                     });
85844                     if (langItem) langItems.push(langItem);
85845                 });
85846                 langItems = utilArrayUniq(langItems.concat(_languagesArray));
85847
85848                 cb(langItems.filter(function(d) {
85849                     return d.label.toLowerCase().indexOf(v) >= 0 ||
85850                         (d.localName && d.localName.toLowerCase().indexOf(v) >= 0) ||
85851                         (d.nativeName && d.nativeName.toLowerCase().indexOf(v) >= 0) ||
85852                         d.code.toLowerCase().indexOf(v) >= 0;
85853                 }).map(function(d) {
85854                     return { value: d.label };
85855                 }));
85856             }
85857
85858
85859             function renderMultilingual(selection) {
85860                 var entries = selection.selectAll('div.entry')
85861                     .data(_multilingual, function(d) { return d.lang; });
85862
85863                 entries.exit()
85864                     .style('top', '0')
85865                     .style('max-height', '240px')
85866                     .transition()
85867                     .duration(200)
85868                     .style('opacity', '0')
85869                     .style('max-height', '0px')
85870                     .remove();
85871
85872                 var entriesEnter = entries.enter()
85873                     .append('div')
85874                     .attr('class', 'entry')
85875                     .each(function(_, index) {
85876                         var wrap = select(this);
85877
85878                         var domId = utilUniqueDomId(index);
85879
85880                         var label = wrap
85881                             .append('label')
85882                             .attr('class', 'field-label')
85883                             .attr('for', domId);
85884
85885                         var text = label
85886                             .append('span')
85887                             .attr('class', 'label-text');
85888
85889                         text
85890                             .append('span')
85891                             .attr('class', 'label-textvalue')
85892                             .text(_t('translate.localized_translation_label'));
85893
85894                         text
85895                             .append('span')
85896                             .attr('class', 'label-textannotation');
85897
85898                         label
85899                             .append('button')
85900                             .attr('class', 'remove-icon-multilingual')
85901                             .on('click', function(d, index) {
85902                                 if (field.locked()) return;
85903                                 event.preventDefault();
85904
85905                                 if (!d.lang || !d.value) {
85906                                     _multilingual.splice(index, 1);
85907                                     renderMultilingual(selection);
85908                                 } else {
85909                                     // remove from entity tags
85910                                     var t = {};
85911                                     t[key(d.lang)] = undefined;
85912                                     dispatch$1.call('change', this, t);
85913                                 }
85914
85915                             })
85916                             .call(svgIcon('#iD-operation-delete'));
85917
85918                         wrap
85919                             .append('input')
85920                             .attr('class', 'localized-lang')
85921                             .attr('id', domId)
85922                             .attr('type', 'text')
85923                             .attr('placeholder', _t('translate.localized_translation_language'))
85924                             .on('blur', changeLang)
85925                             .on('change', changeLang)
85926                             .call(langCombo);
85927
85928                         wrap
85929                             .append('input')
85930                             .attr('type', 'text')
85931                             .attr('class', 'localized-value')
85932                             .on('blur', changeValue)
85933                             .on('change', changeValue);
85934                     });
85935
85936                 entriesEnter
85937                     .style('margin-top', '0px')
85938                     .style('max-height', '0px')
85939                     .style('opacity', '0')
85940                     .transition()
85941                     .duration(200)
85942                     .style('margin-top', '10px')
85943                     .style('max-height', '240px')
85944                     .style('opacity', '1')
85945                     .on('end', function() {
85946                         select(this)
85947                             .style('max-height', '')
85948                             .style('overflow', 'visible');
85949                     });
85950
85951                 entries = entries.merge(entriesEnter);
85952
85953                 entries.order();
85954
85955                 entries.classed('present', function(d) {
85956                     return d.lang && d.value;
85957                 });
85958
85959                 utilGetSetValue(entries.select('.localized-lang'), function(d) {
85960                     return _mainLocalizer.languageName(d.lang);
85961                 });
85962
85963                 utilGetSetValue(entries.select('.localized-value'), function(d) {
85964                         return typeof d.value === 'string' ? d.value : '';
85965                     })
85966                     .attr('title', function(d) {
85967                         return Array.isArray(d.value) ? d.value.filter(Boolean).join('\n') : null;
85968                     })
85969                     .attr('placeholder', function(d) {
85970                         return Array.isArray(d.value) ? _t('inspector.multiple_values') : _t('translate.localized_translation_name');
85971                     })
85972                     .classed('mixed', function(d) {
85973                         return Array.isArray(d.value);
85974                     });
85975             }
85976
85977
85978             localized.tags = function(tags) {
85979                 _tags = tags;
85980
85981                 // Fetch translations from wikipedia
85982                 if (typeof tags.wikipedia === 'string' && !_wikiTitles) {
85983                     _wikiTitles = {};
85984                     var wm = tags.wikipedia.match(/([^:]+):(.+)/);
85985                     if (wm && wm[0] && wm[1]) {
85986                         wikipedia.translations(wm[1], wm[2], function(err, d) {
85987                             if (err || !d) return;
85988                             _wikiTitles = d;
85989                         });
85990                     }
85991                 }
85992
85993                 var isMixed = Array.isArray(tags[field.key]);
85994
85995                 utilGetSetValue(input, typeof tags[field.key] === 'string' ? tags[field.key] : '')
85996                     .attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : undefined)
85997                     .attr('placeholder', isMixed ? _t('inspector.multiple_values') : field.placeholder())
85998                     .classed('mixed', isMixed);
85999
86000                 calcMultilingual(tags);
86001
86002                 _selection
86003                     .call(localized);
86004             };
86005
86006
86007             localized.focus = function() {
86008                 input.node().focus();
86009             };
86010
86011
86012             localized.entityIDs = function(val) {
86013                 if (!arguments.length) return _entityIDs;
86014                 _entityIDs = val;
86015                 _multilingual = [];
86016                 loadCountryCode();
86017                 return localized;
86018             };
86019
86020             function loadCountryCode() {
86021                 var extent = combinedEntityExtent();
86022                 var countryCode = extent && iso1A2Code(extent.center());
86023                 _countryCode = countryCode && countryCode.toLowerCase();
86024             }
86025
86026             function combinedEntityExtent() {
86027                 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
86028             }
86029
86030             return utilRebind(localized, dispatch$1, 'on');
86031         }
86032
86033         function uiFieldMaxspeed(field, context) {
86034             var dispatch$1 = dispatch('change');
86035             var unitInput = select(null);
86036             var input = select(null);
86037             var _entityIDs = [];
86038             var _tags;
86039             var _isImperial;
86040
86041             var speedCombo = uiCombobox(context, 'maxspeed');
86042             var unitCombo = uiCombobox(context, 'maxspeed-unit')
86043                     .data(['km/h', 'mph'].map(comboValues));
86044
86045             var metricValues = [20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120];
86046             var imperialValues = [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80];
86047
86048
86049             function maxspeed(selection) {
86050
86051                 var wrap = selection.selectAll('.form-field-input-wrap')
86052                     .data([0]);
86053
86054                 wrap = wrap.enter()
86055                     .append('div')
86056                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
86057                     .merge(wrap);
86058
86059
86060                 input = wrap.selectAll('input.maxspeed-number')
86061                     .data([0]);
86062
86063                 input = input.enter()
86064                     .append('input')
86065                     .attr('type', 'text')
86066                     .attr('class', 'maxspeed-number')
86067                     .attr('id', field.domId)
86068                     .call(utilNoAuto)
86069                     .call(speedCombo)
86070                     .merge(input);
86071
86072                 input
86073                     .on('change', change)
86074                     .on('blur', change);
86075
86076                 var loc = combinedEntityExtent().center();
86077                 _isImperial = roadSpeedUnit(loc) === 'mph';
86078
86079                 unitInput = wrap.selectAll('input.maxspeed-unit')
86080                     .data([0]);
86081
86082                 unitInput = unitInput.enter()
86083                     .append('input')
86084                     .attr('type', 'text')
86085                     .attr('class', 'maxspeed-unit')
86086                     .call(unitCombo)
86087                     .merge(unitInput);
86088
86089                 unitInput
86090                     .on('blur', changeUnits)
86091                     .on('change', changeUnits);
86092
86093
86094                 function changeUnits() {
86095                     _isImperial = utilGetSetValue(unitInput) === 'mph';
86096                     utilGetSetValue(unitInput, _isImperial ? 'mph' : 'km/h');
86097                     setUnitSuggestions();
86098                     change();
86099                 }
86100             }
86101
86102
86103             function setUnitSuggestions() {
86104                 speedCombo.data((_isImperial ? imperialValues : metricValues).map(comboValues));
86105                 utilGetSetValue(unitInput, _isImperial ? 'mph' : 'km/h');
86106             }
86107
86108
86109             function comboValues(d) {
86110                 return {
86111                     value: d.toString(),
86112                     title: d.toString()
86113                 };
86114             }
86115
86116
86117             function change() {
86118                 var tag = {};
86119                 var value = utilGetSetValue(input).trim();
86120
86121                 // don't override multiple values with blank string
86122                 if (!value && Array.isArray(_tags[field.key])) return;
86123
86124                 if (!value) {
86125                     tag[field.key] = undefined;
86126                 } else if (isNaN(value) || !_isImperial) {
86127                     tag[field.key] = context.cleanTagValue(value);
86128                 } else {
86129                     tag[field.key] = context.cleanTagValue(value + ' mph');
86130                 }
86131
86132                 dispatch$1.call('change', this, tag);
86133             }
86134
86135
86136             maxspeed.tags = function(tags) {
86137                 _tags = tags;
86138
86139                 var value = tags[field.key];
86140                 var isMixed = Array.isArray(value);
86141
86142                 if (!isMixed) {
86143                     if (value && value.indexOf('mph') >= 0) {
86144                         value = parseInt(value, 10).toString();
86145                         _isImperial = true;
86146                     } else if (value) {
86147                         _isImperial = false;
86148                     }
86149                 }
86150
86151                 setUnitSuggestions();
86152
86153                 utilGetSetValue(input, typeof value === 'string' ? value : '')
86154                     .attr('title', isMixed ? value.filter(Boolean).join('\n') : null)
86155                     .attr('placeholder', isMixed ? _t('inspector.multiple_values') : field.placeholder())
86156                     .classed('mixed', isMixed);
86157             };
86158
86159
86160             maxspeed.focus = function() {
86161                 input.node().focus();
86162             };
86163
86164
86165             maxspeed.entityIDs = function(val) {
86166                 _entityIDs = val;
86167             };
86168
86169
86170             function combinedEntityExtent() {
86171                 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
86172             }
86173
86174
86175             return utilRebind(maxspeed, dispatch$1, 'on');
86176         }
86177
86178         function uiFieldRadio(field, context) {
86179             var dispatch$1 = dispatch('change');
86180             var placeholder = select(null);
86181             var wrap = select(null);
86182             var labels = select(null);
86183             var radios = select(null);
86184             var radioData = (field.options || (field.strings && field.strings.options && Object.keys(field.strings.options)) || field.keys).slice();  // shallow copy
86185             var typeField;
86186             var layerField;
86187             var _oldType = {};
86188             var _entityIDs = [];
86189
86190
86191             function selectedKey() {
86192                 var node = wrap.selectAll('.form-field-input-radio label.active input');
86193                 return !node.empty() && node.datum();
86194             }
86195
86196
86197             function radio(selection) {
86198                 selection.classed('preset-radio', true);
86199
86200                 wrap = selection.selectAll('.form-field-input-wrap')
86201                     .data([0]);
86202
86203                 var enter = wrap.enter()
86204                     .append('div')
86205                     .attr('class', 'form-field-input-wrap form-field-input-radio');
86206
86207                 enter
86208                     .append('span')
86209                     .attr('class', 'placeholder');
86210
86211                 wrap = wrap
86212                     .merge(enter);
86213
86214
86215                 placeholder = wrap.selectAll('.placeholder');
86216
86217                 labels = wrap.selectAll('label')
86218                     .data(radioData);
86219
86220                 enter = labels.enter()
86221                     .append('label');
86222
86223                 enter
86224                     .append('input')
86225                     .attr('type', 'radio')
86226                     .attr('name', field.id)
86227                     .attr('value', function(d) { return field.t('options.' + d, { 'default': d }); })
86228                     .attr('checked', false);
86229
86230                 enter
86231                     .append('span')
86232                     .text(function(d) { return field.t('options.' + d, { 'default': d }); });
86233
86234                 labels = labels
86235                     .merge(enter);
86236
86237                 radios = labels.selectAll('input')
86238                     .on('change', changeRadio);
86239
86240             }
86241
86242
86243             function structureExtras(selection, tags) {
86244                 var selected = selectedKey() || tags.layer !== undefined;
86245                 var type = _mainPresetIndex.field(selected);
86246                 var layer = _mainPresetIndex.field('layer');
86247                 var showLayer = (selected === 'bridge' || selected === 'tunnel' || tags.layer !== undefined);
86248
86249
86250                 var extrasWrap = selection.selectAll('.structure-extras-wrap')
86251                     .data(selected ? [0] : []);
86252
86253                 extrasWrap.exit()
86254                     .remove();
86255
86256                 extrasWrap = extrasWrap.enter()
86257                     .append('div')
86258                     .attr('class', 'structure-extras-wrap')
86259                     .merge(extrasWrap);
86260
86261                 var list = extrasWrap.selectAll('ul')
86262                     .data([0]);
86263
86264                 list = list.enter()
86265                     .append('ul')
86266                     .attr('class', 'rows')
86267                     .merge(list);
86268
86269
86270                 // Type
86271                 if (type) {
86272                     if (!typeField || typeField.id !== selected) {
86273                         typeField = uiField(context, type, _entityIDs, { wrap: false })
86274                             .on('change', changeType);
86275                     }
86276                     typeField.tags(tags);
86277                 } else {
86278                     typeField = null;
86279                 }
86280
86281                 var typeItem = list.selectAll('.structure-type-item')
86282                     .data(typeField ? [typeField] : [], function(d) { return d.id; });
86283
86284                 // Exit
86285                 typeItem.exit()
86286                     .remove();
86287
86288                 // Enter
86289                 var typeEnter = typeItem.enter()
86290                     .insert('li', ':first-child')
86291                     .attr('class', 'labeled-input structure-type-item');
86292
86293                 typeEnter
86294                     .append('span')
86295                     .attr('class', 'label structure-label-type')
86296                     .attr('for', 'preset-input-' + selected)
86297                     .text(_t('inspector.radio.structure.type'));
86298
86299                 typeEnter
86300                     .append('div')
86301                     .attr('class', 'structure-input-type-wrap');
86302
86303                 // Update
86304                 typeItem = typeItem
86305                     .merge(typeEnter);
86306
86307                 if (typeField) {
86308                     typeItem.selectAll('.structure-input-type-wrap')
86309                         .call(typeField.render);
86310                 }
86311
86312
86313                 // Layer
86314                 if (layer && showLayer) {
86315                     if (!layerField) {
86316                         layerField = uiField(context, layer, _entityIDs, { wrap: false })
86317                             .on('change', changeLayer);
86318                     }
86319                     layerField.tags(tags);
86320                     field.keys = utilArrayUnion(field.keys, ['layer']);
86321                 } else {
86322                     layerField = null;
86323                     field.keys = field.keys.filter(function(k) { return k !== 'layer'; });
86324                 }
86325
86326                 var layerItem = list.selectAll('.structure-layer-item')
86327                     .data(layerField ? [layerField] : []);
86328
86329                 // Exit
86330                 layerItem.exit()
86331                     .remove();
86332
86333                 // Enter
86334                 var layerEnter = layerItem.enter()
86335                     .append('li')
86336                     .attr('class', 'labeled-input structure-layer-item');
86337
86338                 layerEnter
86339                     .append('span')
86340                     .attr('class', 'label structure-label-layer')
86341                     .attr('for', 'preset-input-layer')
86342                     .text(_t('inspector.radio.structure.layer'));
86343
86344                 layerEnter
86345                     .append('div')
86346                     .attr('class', 'structure-input-layer-wrap');
86347
86348                 // Update
86349                 layerItem = layerItem
86350                     .merge(layerEnter);
86351
86352                 if (layerField) {
86353                     layerItem.selectAll('.structure-input-layer-wrap')
86354                         .call(layerField.render);
86355                 }
86356             }
86357
86358
86359             function changeType(t, onInput) {
86360                 var key = selectedKey();
86361                 if (!key) return;
86362
86363                 var val = t[key];
86364                 if (val !== 'no') {
86365                     _oldType[key] = val;
86366                 }
86367
86368                 if (field.type === 'structureRadio') {
86369                     // remove layer if it should not be set
86370                     if (val === 'no' ||
86371                         (key !== 'bridge' && key !== 'tunnel') ||
86372                         (key === 'tunnel' && val === 'building_passage')) {
86373                         t.layer = undefined;
86374                     }
86375                     // add layer if it should be set
86376                     if (t.layer === undefined) {
86377                         if (key === 'bridge' && val !== 'no') {
86378                             t.layer = '1';
86379                         }
86380                         if (key === 'tunnel' && val !== 'no' && val !== 'building_passage') {
86381                             t.layer = '-1';
86382                         }
86383                     }
86384                  }
86385
86386                 dispatch$1.call('change', this, t, onInput);
86387             }
86388
86389
86390             function changeLayer(t, onInput) {
86391                 if (t.layer === '0') {
86392                     t.layer = undefined;
86393                 }
86394                 dispatch$1.call('change', this, t, onInput);
86395             }
86396
86397
86398             function changeRadio() {
86399                 var t = {};
86400                 var activeKey;
86401
86402                 if (field.key) {
86403                     t[field.key] = undefined;
86404                 }
86405
86406                 radios.each(function(d) {
86407                     var active = select(this).property('checked');
86408                     if (active) activeKey = d;
86409
86410                     if (field.key) {
86411                         if (active) t[field.key] = d;
86412                     } else {
86413                         var val = _oldType[activeKey] || 'yes';
86414                         t[d] = active ? val : undefined;
86415                     }
86416                 });
86417
86418                 if (field.type === 'structureRadio') {
86419                     if (activeKey === 'bridge') {
86420                         t.layer = '1';
86421                     } else if (activeKey === 'tunnel' && t.tunnel !== 'building_passage') {
86422                         t.layer = '-1';
86423                     } else {
86424                         t.layer = undefined;
86425                     }
86426                 }
86427
86428                 dispatch$1.call('change', this, t);
86429             }
86430
86431
86432             radio.tags = function(tags) {
86433
86434                 radios.property('checked', function(d) {
86435                     if (field.key) {
86436                         return tags[field.key] === d;
86437                     }
86438                     return !!(typeof tags[d] === 'string' && tags[d].toLowerCase() !== 'no');
86439                 });
86440
86441                 function isMixed(d) {
86442                     if (field.key) {
86443                         return Array.isArray(tags[field.key]) && tags[field.key].includes(d);
86444                     }
86445                     return Array.isArray(tags[d]);
86446                 }
86447
86448                 labels
86449                     .classed('active', function(d) {
86450                         if (field.key) {
86451                             return (Array.isArray(tags[field.key]) && tags[field.key].includes(d))
86452                                 || tags[field.key] === d;
86453                         }
86454                         return Array.isArray(tags[d]) || !!(tags[d] && tags[d].toLowerCase() !== 'no');
86455                     })
86456                     .classed('mixed', isMixed)
86457                     .attr('title', function(d) {
86458                         return isMixed(d) ? _t('inspector.unshared_value_tooltip') : null;
86459                     });
86460
86461
86462                 var selection = radios.filter(function() { return this.checked; });
86463
86464                 if (selection.empty()) {
86465                     placeholder.text(_t('inspector.none'));
86466                 } else {
86467                     placeholder.text(selection.attr('value'));
86468                     _oldType[selection.datum()] = tags[selection.datum()];
86469                 }
86470
86471                 if (field.type === 'structureRadio') {
86472                     // For waterways without a tunnel tag, set 'culvert' as
86473                     // the _oldType to default to if the user picks 'tunnel'
86474                     if (!!tags.waterway && !_oldType.tunnel) {
86475                         _oldType.tunnel = 'culvert';
86476                     }
86477
86478                     wrap.call(structureExtras, tags);
86479                 }
86480             };
86481
86482
86483             radio.focus = function() {
86484                 radios.node().focus();
86485             };
86486
86487
86488             radio.entityIDs = function(val) {
86489                 if (!arguments.length) return _entityIDs;
86490                 _entityIDs = val;
86491                 _oldType = {};
86492                 return radio;
86493             };
86494
86495
86496             radio.isAllowed = function() {
86497                 return _entityIDs.length === 1;
86498             };
86499
86500
86501             return utilRebind(radio, dispatch$1, 'on');
86502         }
86503
86504         function uiFieldRestrictions(field, context) {
86505             var dispatch$1 = dispatch('change');
86506             var breathe = behaviorBreathe();
86507
86508             corePreferences('turn-restriction-via-way', null);                 // remove old key
86509             var storedViaWay = corePreferences('turn-restriction-via-way0');   // use new key #6922
86510             var storedDistance = corePreferences('turn-restriction-distance');
86511
86512             var _maxViaWay = storedViaWay !== null ? (+storedViaWay) : 0;
86513             var _maxDistance = storedDistance ? (+storedDistance) : 30;
86514             var _initialized = false;
86515             var _parent = select(null);       // the entire field
86516             var _container = select(null);    // just the map
86517             var _oldTurns;
86518             var _graph;
86519             var _vertexID;
86520             var _intersection;
86521             var _fromWayID;
86522
86523             var _lastXPos;
86524
86525
86526             function restrictions(selection) {
86527                 _parent = selection;
86528
86529                 // try to reuse the intersection, but always rebuild it if the graph has changed
86530                 if (_vertexID && (context.graph() !== _graph || !_intersection)) {
86531                     _graph = context.graph();
86532                     _intersection = osmIntersection(_graph, _vertexID, _maxDistance);
86533                 }
86534
86535                 // It's possible for there to be no actual intersection here.
86536                 // for example, a vertex of two `highway=path`
86537                 // In this case, hide the field.
86538                 var isOK = (
86539                     _intersection &&
86540                     _intersection.vertices.length &&           // has vertices
86541                     _intersection.vertices                     // has the vertex that the user selected
86542                         .filter(function(vertex) { return vertex.id === _vertexID; }).length &&
86543                     _intersection.ways.length > 2 &&           // has more than 2 ways
86544                     _intersection.ways                         // has more than 1 TO way
86545                         .filter(function(way) { return way.__to; }).length > 1
86546                 );
86547
86548                 // Also hide in the case where
86549                 select(selection.node().parentNode).classed('hide', !isOK);
86550
86551                 // if form field is hidden or has detached from dom, clean up.
86552                 if (!isOK ||
86553                     !context.container().select('.inspector-wrap.inspector-hidden').empty() ||
86554                     !selection.node().parentNode ||
86555                     !selection.node().parentNode.parentNode) {
86556                     selection.call(restrictions.off);
86557                     return;
86558                 }
86559
86560
86561                 var wrap = selection.selectAll('.form-field-input-wrap')
86562                     .data([0]);
86563
86564                 wrap = wrap.enter()
86565                     .append('div')
86566                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
86567                     .merge(wrap);
86568
86569                 var container = wrap.selectAll('.restriction-container')
86570                     .data([0]);
86571
86572                 // enter
86573                 var containerEnter = container.enter()
86574                     .append('div')
86575                     .attr('class', 'restriction-container');
86576
86577                 containerEnter
86578                     .append('div')
86579                     .attr('class', 'restriction-help');
86580
86581                 // update
86582                 _container = containerEnter
86583                     .merge(container)
86584                     .call(renderViewer);
86585
86586                 var controls = wrap.selectAll('.restriction-controls')
86587                     .data([0]);
86588
86589                 // enter/update
86590                 controls.enter()
86591                     .append('div')
86592                     .attr('class', 'restriction-controls-container')
86593                     .append('div')
86594                     .attr('class', 'restriction-controls')
86595                     .merge(controls)
86596                     .call(renderControls);
86597             }
86598
86599
86600             function renderControls(selection) {
86601                 var distControl = selection.selectAll('.restriction-distance')
86602                     .data([0]);
86603
86604                 distControl.exit()
86605                     .remove();
86606
86607                 var distControlEnter = distControl.enter()
86608                     .append('div')
86609                     .attr('class', 'restriction-control restriction-distance');
86610
86611                 distControlEnter
86612                     .append('span')
86613                     .attr('class', 'restriction-control-label restriction-distance-label')
86614                     .text(_t('restriction.controls.distance') + ':');
86615
86616                 distControlEnter
86617                     .append('input')
86618                     .attr('class', 'restriction-distance-input')
86619                     .attr('type', 'range')
86620                     .attr('min', '20')
86621                     .attr('max', '50')
86622                     .attr('step', '5');
86623
86624                 distControlEnter
86625                     .append('span')
86626                     .attr('class', 'restriction-distance-text');
86627
86628                 // update
86629                 selection.selectAll('.restriction-distance-input')
86630                     .property('value', _maxDistance)
86631                     .on('input', function() {
86632                         var val = select(this).property('value');
86633                         _maxDistance = +val;
86634                         _intersection = null;
86635                         _container.selectAll('.layer-osm .layer-turns *').remove();
86636                         corePreferences('turn-restriction-distance', _maxDistance);
86637                         _parent.call(restrictions);
86638                     });
86639
86640                 selection.selectAll('.restriction-distance-text')
86641                     .text(displayMaxDistance(_maxDistance));
86642
86643
86644                 var viaControl = selection.selectAll('.restriction-via-way')
86645                     .data([0]);
86646
86647                 viaControl.exit()
86648                     .remove();
86649
86650                 var viaControlEnter = viaControl.enter()
86651                     .append('div')
86652                     .attr('class', 'restriction-control restriction-via-way');
86653
86654                 viaControlEnter
86655                     .append('span')
86656                     .attr('class', 'restriction-control-label restriction-via-way-label')
86657                     .text(_t('restriction.controls.via') + ':');
86658
86659                 viaControlEnter
86660                     .append('input')
86661                     .attr('class', 'restriction-via-way-input')
86662                     .attr('type', 'range')
86663                     .attr('min', '0')
86664                     .attr('max', '2')
86665                     .attr('step', '1');
86666
86667                 viaControlEnter
86668                     .append('span')
86669                     .attr('class', 'restriction-via-way-text');
86670
86671                 // update
86672                 selection.selectAll('.restriction-via-way-input')
86673                     .property('value', _maxViaWay)
86674                     .on('input', function() {
86675                         var val = select(this).property('value');
86676                         _maxViaWay = +val;
86677                         _container.selectAll('.layer-osm .layer-turns *').remove();
86678                         corePreferences('turn-restriction-via-way0', _maxViaWay);
86679                         _parent.call(restrictions);
86680                     });
86681
86682                 selection.selectAll('.restriction-via-way-text')
86683                     .text(displayMaxVia(_maxViaWay));
86684             }
86685
86686
86687             function renderViewer(selection) {
86688                 if (!_intersection) return;
86689
86690                 var vgraph = _intersection.graph;
86691                 var filter = utilFunctor(true);
86692                 var projection = geoRawMercator();
86693
86694                 // Reflow warning: `utilGetDimensions` calls `getBoundingClientRect`
86695                 // Instead of asking the restriction-container for its dimensions,
86696                 //  we can ask the .sidebar, which can have its dimensions cached.
86697                 // width: calc as sidebar - padding
86698                 // height: hardcoded (from `80_app.css`)
86699                 // var d = utilGetDimensions(selection);
86700                 var sdims = utilGetDimensions(context.container().select('.sidebar'));
86701                 var d = [ sdims[0] - 50, 370 ];
86702                 var c = geoVecScale(d, 0.5);
86703                 var z = 22;
86704
86705                 projection.scale(geoZoomToScale(z));
86706
86707                 // Calculate extent of all key vertices
86708                 var extent = geoExtent();
86709                 for (var i = 0; i < _intersection.vertices.length; i++) {
86710                     extent._extend(_intersection.vertices[i].extent());
86711                 }
86712
86713                 // If this is a large intersection, adjust zoom to fit extent
86714                 if (_intersection.vertices.length > 1) {
86715                     var padding = 180;   // in z22 pixels
86716                     var tl = projection([extent[0][0], extent[1][1]]);
86717                     var br = projection([extent[1][0], extent[0][1]]);
86718                     var hFactor = (br[0] - tl[0]) / (d[0] - padding);
86719                     var vFactor = (br[1] - tl[1]) / (d[1] - padding);
86720                     var hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2;
86721                     var vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2;
86722                     z = z - Math.max(hZoomDiff, vZoomDiff);
86723                     projection.scale(geoZoomToScale(z));
86724                 }
86725
86726                 var padTop = 35;   // reserve top space for hint text
86727                 var extentCenter = projection(extent.center());
86728                 extentCenter[1] = extentCenter[1] - padTop;
86729
86730                 projection
86731                     .translate(geoVecSubtract(c, extentCenter))
86732                     .clipExtent([[0, 0], d]);
86733
86734                 var drawLayers = svgLayers(projection, context).only(['osm','touch']).dimensions(d);
86735                 var drawVertices = svgVertices(projection, context);
86736                 var drawLines = svgLines(projection, context);
86737                 var drawTurns = svgTurns(projection, context);
86738
86739                 var firstTime = selection.selectAll('.surface').empty();
86740
86741                 selection
86742                     .call(drawLayers);
86743
86744                 var surface = selection.selectAll('.surface')
86745                     .classed('tr', true);
86746
86747                 if (firstTime) {
86748                     _initialized = true;
86749
86750                     surface
86751                         .call(breathe);
86752                 }
86753
86754                 // This can happen if we've lowered the detail while a FROM way
86755                 // is selected, and that way is no longer part of the intersection.
86756                 if (_fromWayID && !vgraph.hasEntity(_fromWayID)) {
86757                     _fromWayID = null;
86758                     _oldTurns = null;
86759                 }
86760
86761                 surface
86762                     .call(utilSetDimensions, d)
86763                     .call(drawVertices, vgraph, _intersection.vertices, filter, extent, z)
86764                     .call(drawLines, vgraph, _intersection.ways, filter)
86765                     .call(drawTurns, vgraph, _intersection.turns(_fromWayID, _maxViaWay));
86766
86767                 surface
86768                     .on('click.restrictions', click)
86769                     .on('mouseover.restrictions', mouseover);
86770
86771                 surface
86772                     .selectAll('.selected')
86773                     .classed('selected', false);
86774
86775                 surface
86776                     .selectAll('.related')
86777                     .classed('related', false);
86778
86779                 if (_fromWayID) {
86780                     var way = vgraph.entity(_fromWayID);
86781                     surface
86782                         .selectAll('.' + _fromWayID)
86783                         .classed('selected', true)
86784                         .classed('related', true);
86785                 }
86786
86787                 document.addEventListener('resizeWindow', function () {
86788                     utilSetDimensions(_container, null);
86789                     redraw(1);
86790                 }, false);
86791
86792                 updateHints(null);
86793
86794
86795                 function click() {
86796                     surface
86797                         .call(breathe.off)
86798                         .call(breathe);
86799
86800                     var datum = event.target.__data__;
86801                     var entity = datum && datum.properties && datum.properties.entity;
86802                     if (entity) {
86803                         datum = entity;
86804                     }
86805
86806                     if (datum instanceof osmWay && (datum.__from || datum.__via)) {
86807                         _fromWayID = datum.id;
86808                         _oldTurns = null;
86809                         redraw();
86810
86811                     } else if (datum instanceof osmTurn) {
86812                         var actions, extraActions, turns, i;
86813                         var restrictionType = osmInferRestriction(vgraph, datum, projection);
86814
86815                         if (datum.restrictionID && !datum.direct) {
86816                             return;
86817
86818                         } else if (datum.restrictionID && !datum.only) {    // NO -> ONLY
86819                             var seen = {};
86820                             var datumOnly = JSON.parse(JSON.stringify(datum));   // deep clone the datum
86821                             datumOnly.only = true;                               // but change this property
86822                             restrictionType = restrictionType.replace(/^no/, 'only');
86823
86824                             // Adding an ONLY restriction should destroy all other direct restrictions from the FROM towards the VIA.
86825                             // We will remember them in _oldTurns, and restore them if the user clicks again.
86826                             turns = _intersection.turns(_fromWayID, 2);
86827                             extraActions = [];
86828                             _oldTurns = [];
86829                             for (i = 0; i < turns.length; i++) {
86830                                 var turn = turns[i];
86831                                 if (seen[turn.restrictionID]) continue;  // avoid deleting the turn twice (#4968, #4928)
86832
86833                                 if (turn.direct && turn.path[1] === datum.path[1]) {
86834                                     seen[turns[i].restrictionID] = true;
86835                                     turn.restrictionType = osmInferRestriction(vgraph, turn, projection);
86836                                     _oldTurns.push(turn);
86837                                     extraActions.push(actionUnrestrictTurn(turn));
86838                                 }
86839                             }
86840
86841                             actions = _intersection.actions.concat(extraActions, [
86842                                 actionRestrictTurn(datumOnly, restrictionType),
86843                                 _t('operations.restriction.annotation.create')
86844                             ]);
86845
86846                         } else if (datum.restrictionID) {   // ONLY -> Allowed
86847                             // Restore whatever restrictions we might have destroyed by cycling thru the ONLY state.
86848                             // This relies on the assumption that the intersection was already split up when we
86849                             // performed the previous action (NO -> ONLY), so the IDs in _oldTurns shouldn't have changed.
86850                             turns = _oldTurns || [];
86851                             extraActions = [];
86852                             for (i = 0; i < turns.length; i++) {
86853                                 if (turns[i].key !== datum.key) {
86854                                     extraActions.push(actionRestrictTurn(turns[i], turns[i].restrictionType));
86855                                 }
86856                             }
86857                             _oldTurns = null;
86858
86859                             actions = _intersection.actions.concat(extraActions, [
86860                                 actionUnrestrictTurn(datum),
86861                                 _t('operations.restriction.annotation.delete')
86862                             ]);
86863
86864                         } else {    // Allowed -> NO
86865                             actions = _intersection.actions.concat([
86866                                 actionRestrictTurn(datum, restrictionType),
86867                                 _t('operations.restriction.annotation.create')
86868                             ]);
86869                         }
86870
86871                         context.perform.apply(context, actions);
86872
86873                         // At this point the datum will be changed, but will have same key..
86874                         // Refresh it and update the help..
86875                         var s = surface.selectAll('.' + datum.key);
86876                         datum = s.empty() ? null : s.datum();
86877                         updateHints(datum);
86878
86879                     } else {
86880                         _fromWayID = null;
86881                         _oldTurns = null;
86882                         redraw();
86883                     }
86884                 }
86885
86886
86887                 function mouseover() {
86888                     var datum = event.target.__data__;
86889                     updateHints(datum);
86890                 }
86891
86892                 _lastXPos = _lastXPos || sdims[0];
86893
86894                 function redraw(minChange) {
86895                     var xPos = -1;
86896
86897                     if (minChange) {
86898                         xPos = utilGetDimensions(context.container().select('.sidebar'))[0];
86899                     }
86900
86901                     if (!minChange || (minChange && Math.abs(xPos - _lastXPos) >= minChange)) {
86902                         if (context.hasEntity(_vertexID)) {
86903                             _lastXPos = xPos;
86904                             _container.call(renderViewer);
86905                         }
86906                     }
86907                 }
86908
86909
86910                 function highlightPathsFrom(wayID) {
86911                     surface.selectAll('.related')
86912                         .classed('related', false)
86913                         .classed('allow', false)
86914                         .classed('restrict', false)
86915                         .classed('only', false);
86916
86917                     surface.selectAll('.' + wayID)
86918                         .classed('related', true);
86919
86920                     if (wayID) {
86921                         var turns = _intersection.turns(wayID, _maxViaWay);
86922                         for (var i = 0; i < turns.length; i++) {
86923                             var turn = turns[i];
86924                             var ids = [turn.to.way];
86925                             var klass = (turn.no ? 'restrict' : (turn.only ? 'only' : 'allow'));
86926
86927                             if (turn.only || turns.length === 1) {
86928                                 if (turn.via.ways) {
86929                                     ids = ids.concat(turn.via.ways);
86930                                 }
86931                             } else if (turn.to.way === wayID) {
86932                                 continue;
86933                             }
86934
86935                             surface.selectAll(utilEntitySelector(ids))
86936                                 .classed('related', true)
86937                                 .classed('allow', (klass === 'allow'))
86938                                 .classed('restrict', (klass === 'restrict'))
86939                                 .classed('only', (klass === 'only'));
86940                         }
86941                     }
86942                 }
86943
86944
86945                 function updateHints(datum) {
86946                     var help = _container.selectAll('.restriction-help').html('');
86947
86948                     var placeholders = {};
86949                     ['from', 'via', 'to'].forEach(function(k) {
86950                         placeholders[k] = '<span class="qualifier">' + _t('restriction.help.' + k) + '</span>';
86951                     });
86952
86953                     var entity = datum && datum.properties && datum.properties.entity;
86954                     if (entity) {
86955                         datum = entity;
86956                     }
86957
86958                     if (_fromWayID) {
86959                         way = vgraph.entity(_fromWayID);
86960                         surface
86961                             .selectAll('.' + _fromWayID)
86962                             .classed('selected', true)
86963                             .classed('related', true);
86964                     }
86965
86966                     // Hovering a way
86967                     if (datum instanceof osmWay && datum.__from) {
86968                         way = datum;
86969
86970                         highlightPathsFrom(_fromWayID ? null : way.id);
86971                         surface.selectAll('.' + way.id)
86972                             .classed('related', true);
86973
86974                         var clickSelect = (!_fromWayID || _fromWayID !== way.id);
86975                         help
86976                             .append('div')      // "Click to select FROM {fromName}." / "FROM {fromName}"
86977                             .html(_t('restriction.help.' + (clickSelect ? 'select_from_name' : 'from_name'), {
86978                                 from: placeholders.from,
86979                                 fromName: displayName(way.id, vgraph)
86980                             }));
86981
86982
86983                     // Hovering a turn arrow
86984                     } else if (datum instanceof osmTurn) {
86985                         var restrictionType = osmInferRestriction(vgraph, datum, projection);
86986                         var turnType = restrictionType.replace(/^(only|no)\_/, '');
86987                         var indirect = (datum.direct === false ? _t('restriction.help.indirect') : '');
86988                         var klass, turnText, nextText;
86989
86990                         if (datum.no) {
86991                             klass = 'restrict';
86992                             turnText = _t('restriction.help.turn.no_' + turnType, { indirect: indirect });
86993                             nextText = _t('restriction.help.turn.only_' + turnType, { indirect: '' });
86994                         } else if (datum.only) {
86995                             klass = 'only';
86996                             turnText = _t('restriction.help.turn.only_' + turnType, { indirect: indirect });
86997                             nextText = _t('restriction.help.turn.allowed_' + turnType, { indirect: '' });
86998                         } else {
86999                             klass = 'allow';
87000                             turnText = _t('restriction.help.turn.allowed_' + turnType, { indirect: indirect });
87001                             nextText = _t('restriction.help.turn.no_' + turnType, { indirect: '' });
87002                         }
87003
87004                         help
87005                             .append('div')      // "NO Right Turn (indirect)"
87006                             .attr('class', 'qualifier ' + klass)
87007                             .text(turnText);
87008
87009                         help
87010                             .append('div')      // "FROM {fromName} TO {toName}"
87011                             .html(_t('restriction.help.from_name_to_name', {
87012                                 from: placeholders.from,
87013                                 fromName: displayName(datum.from.way, vgraph),
87014                                 to: placeholders.to,
87015                                 toName: displayName(datum.to.way, vgraph)
87016                             }));
87017
87018                         if (datum.via.ways && datum.via.ways.length) {
87019                             var names = [];
87020                             for (var i = 0; i < datum.via.ways.length; i++) {
87021                                 var prev = names[names.length - 1];
87022                                 var curr = displayName(datum.via.ways[i], vgraph);
87023                                 if (!prev || curr !== prev)   // collapse identical names
87024                                     names.push(curr);
87025                             }
87026
87027                             help
87028                                 .append('div')      // "VIA {viaNames}"
87029                                 .html(_t('restriction.help.via_names', {
87030                                     via: placeholders.via,
87031                                     viaNames: names.join(', ')
87032                                 }));
87033                         }
87034
87035                         if (!indirect) {
87036                             help
87037                                 .append('div')      // Click for "No Right Turn"
87038                                 .text(_t('restriction.help.toggle', { turn: nextText.trim() }));
87039                         }
87040
87041                         highlightPathsFrom(null);
87042                         var alongIDs = datum.path.slice();
87043                         surface.selectAll(utilEntitySelector(alongIDs))
87044                             .classed('related', true)
87045                             .classed('allow', (klass === 'allow'))
87046                             .classed('restrict', (klass === 'restrict'))
87047                             .classed('only', (klass === 'only'));
87048
87049
87050                     // Hovering empty surface
87051                     } else {
87052                         highlightPathsFrom(null);
87053                         if (_fromWayID) {
87054                             help
87055                                 .append('div')      // "FROM {fromName}"
87056                                 .html(_t('restriction.help.from_name', {
87057                                     from: placeholders.from,
87058                                     fromName: displayName(_fromWayID, vgraph)
87059                                 }));
87060
87061                         } else {
87062                             help
87063                                 .append('div')      // "Click to select a FROM segment."
87064                                 .html(_t('restriction.help.select_from', {
87065                                     from: placeholders.from
87066                                 }));
87067                         }
87068                     }
87069                 }
87070             }
87071
87072
87073             function displayMaxDistance(maxDist) {
87074                 var isImperial = !_mainLocalizer.usesMetric();
87075                 var opts;
87076
87077                 if (isImperial) {
87078                     var distToFeet = {   // imprecise conversion for prettier display
87079                         20: 70, 25: 85, 30: 100, 35: 115, 40: 130, 45: 145, 50: 160
87080                     }[maxDist];
87081                     opts = { distance: _t('units.feet', { quantity: distToFeet }) };
87082                 } else {
87083                     opts = { distance: _t('units.meters', { quantity: maxDist }) };
87084                 }
87085
87086                 return _t('restriction.controls.distance_up_to', opts);
87087             }
87088
87089
87090             function displayMaxVia(maxVia) {
87091                 return maxVia === 0 ? _t('restriction.controls.via_node_only')
87092                     : maxVia === 1 ? _t('restriction.controls.via_up_to_one')
87093                     : _t('restriction.controls.via_up_to_two');
87094             }
87095
87096
87097             function displayName(entityID, graph) {
87098                 var entity = graph.entity(entityID);
87099                 var name = utilDisplayName(entity) || '';
87100                 var matched = _mainPresetIndex.match(entity, graph);
87101                 var type = (matched && matched.name()) || utilDisplayType(entity.id);
87102                 return name || type;
87103             }
87104
87105
87106             restrictions.entityIDs = function(val) {
87107                 _intersection = null;
87108                 _fromWayID = null;
87109                 _oldTurns = null;
87110                 _vertexID = val[0];
87111             };
87112
87113
87114             restrictions.tags = function() {};
87115             restrictions.focus = function() {};
87116
87117
87118             restrictions.off = function(selection) {
87119                 if (!_initialized) return;
87120
87121                 selection.selectAll('.surface')
87122                     .call(breathe.off)
87123                     .on('click.restrictions', null)
87124                     .on('mouseover.restrictions', null);
87125
87126                 select(window)
87127                     .on('resize.restrictions', null);
87128             };
87129
87130
87131             return utilRebind(restrictions, dispatch$1, 'on');
87132         }
87133
87134         uiFieldRestrictions.supportsMultiselection = false;
87135
87136         function uiFieldTextarea(field, context) {
87137             var dispatch$1 = dispatch('change');
87138             var input = select(null);
87139             var _tags;
87140
87141
87142             function textarea(selection) {
87143                 var wrap = selection.selectAll('.form-field-input-wrap')
87144                     .data([0]);
87145
87146                 wrap = wrap.enter()
87147                     .append('div')
87148                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
87149                     .merge(wrap);
87150
87151                 input = wrap.selectAll('textarea')
87152                     .data([0]);
87153
87154                 input = input.enter()
87155                     .append('textarea')
87156                     .attr('id', field.domId)
87157                     .call(utilNoAuto)
87158                     .on('input', change(true))
87159                     .on('blur', change())
87160                     .on('change', change())
87161                     .merge(input);
87162             }
87163
87164
87165             function change(onInput) {
87166                 return function() {
87167
87168                     var val = utilGetSetValue(input);
87169                     if (!onInput) val = context.cleanTagValue(val);
87170
87171                     // don't override multiple values with blank string
87172                     if (!val && Array.isArray(_tags[field.key])) return;
87173
87174                     var t = {};
87175                     t[field.key] = val || undefined;
87176                     dispatch$1.call('change', this, t, onInput);
87177                 };
87178             }
87179
87180
87181             textarea.tags = function(tags) {
87182                 _tags = tags;
87183
87184                 var isMixed = Array.isArray(tags[field.key]);
87185
87186                 utilGetSetValue(input, !isMixed && tags[field.key] ? tags[field.key] : '')
87187                     .attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : undefined)
87188                     .attr('placeholder', isMixed ? _t('inspector.multiple_values') : (field.placeholder() || _t('inspector.unknown')))
87189                     .classed('mixed', isMixed);
87190             };
87191
87192
87193             textarea.focus = function() {
87194                 input.node().focus();
87195             };
87196
87197
87198             return utilRebind(textarea, dispatch$1, 'on');
87199         }
87200
87201         function uiFieldWikidata(field, context) {
87202             var wikidata = services.wikidata;
87203             var dispatch$1 = dispatch('change');
87204
87205             var _selection = select(null);
87206             var _searchInput = select(null);
87207             var _qid = null;
87208             var _wikidataEntity = null;
87209             var _wikiURL = '';
87210             var _entityIDs = [];
87211
87212             var _wikipediaKey = field.keys && field.keys.find(function(key) {
87213                     return key.includes('wikipedia');
87214                 }),
87215                 _hintKey = field.key === 'wikidata' ? 'name' : field.key.split(':')[0];
87216
87217             var combobox = uiCombobox(context, 'combo-' + field.safeid)
87218                 .caseSensitive(true)
87219                 .minItems(1);
87220
87221             function wiki(selection) {
87222
87223                 _selection = selection;
87224
87225                 var wrap = selection.selectAll('.form-field-input-wrap')
87226                     .data([0]);
87227
87228                 wrap = wrap.enter()
87229                     .append('div')
87230                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
87231                     .merge(wrap);
87232
87233
87234                 var list = wrap.selectAll('ul')
87235                     .data([0]);
87236
87237                 list = list.enter()
87238                     .append('ul')
87239                     .attr('class', 'rows')
87240                     .merge(list);
87241
87242                 var searchRow = list.selectAll('li.wikidata-search')
87243                     .data([0]);
87244
87245                 var searchRowEnter = searchRow.enter()
87246                     .append('li')
87247                     .attr('class', 'wikidata-search');
87248
87249                 searchRowEnter
87250                     .append('input')
87251                     .attr('type', 'text')
87252                     .attr('id', field.domId)
87253                     .style('flex', '1')
87254                     .call(utilNoAuto)
87255                     .on('focus', function() {
87256                         var node = select(this).node();
87257                         node.setSelectionRange(0, node.value.length);
87258                     })
87259                     .on('blur', function() {
87260                         setLabelForEntity();
87261                     })
87262                     .call(combobox.fetcher(fetchWikidataItems));
87263
87264                 combobox.on('accept', function(d) {
87265                     _qid = d.id;
87266                     change();
87267                 }).on('cancel', function() {
87268                     setLabelForEntity();
87269                 });
87270
87271                 searchRowEnter
87272                     .append('button')
87273                     .attr('class', 'form-field-button wiki-link')
87274                     .attr('title', _t('icons.view_on', { domain: 'wikidata.org' }))
87275                     .attr('tabindex', -1)
87276                     .call(svgIcon('#iD-icon-out-link'))
87277                     .on('click', function() {
87278                         event.preventDefault();
87279                         if (_wikiURL) window.open(_wikiURL, '_blank');
87280                     });
87281
87282                 searchRow = searchRow.merge(searchRowEnter);
87283
87284                 _searchInput = searchRow.select('input');
87285
87286                 var wikidataProperties = ['description', 'identifier'];
87287
87288                 var items = list.selectAll('li.labeled-input')
87289                     .data(wikidataProperties);
87290
87291                 // Enter
87292                 var enter = items.enter()
87293                     .append('li')
87294                     .attr('class', function(d) { return 'labeled-input preset-wikidata-' + d; });
87295
87296                 enter
87297                     .append('span')
87298                     .attr('class', 'label')
87299                     .text(function(d) { return _t('wikidata.' + d); });
87300
87301                 enter
87302                     .append('input')
87303                     .attr('type', 'text')
87304                     .call(utilNoAuto)
87305                     .classed('disabled', 'true')
87306                     .attr('readonly', 'true');
87307
87308                 enter
87309                     .append('button')
87310                     .attr('class', 'form-field-button')
87311                     .attr('title', _t('icons.copy'))
87312                     .attr('tabindex', -1)
87313                     .call(svgIcon('#iD-operation-copy'))
87314                     .on('click', function() {
87315                         event.preventDefault();
87316                         select(this.parentNode)
87317                             .select('input')
87318                             .node()
87319                             .select();
87320                         document.execCommand('copy');
87321                     });
87322
87323             }
87324
87325             function fetchWikidataItems(q, callback) {
87326
87327                 if (!q && _hintKey) {
87328                     // other tags may be good search terms
87329                     for (var i in _entityIDs) {
87330                         var entity = context.hasEntity(_entityIDs[i]);
87331                         if (entity.tags[_hintKey]) {
87332                             q = entity.tags[_hintKey];
87333                             break;
87334                         }
87335                     }
87336                 }
87337
87338                 wikidata.itemsForSearchQuery(q, function(err, data) {
87339                     if (err) return;
87340
87341                     for (var i in data) {
87342                         data[i].value = data[i].label + ' (' +  data[i].id + ')';
87343                         data[i].title = data[i].description;
87344                     }
87345
87346                     if (callback) callback(data);
87347                 });
87348             }
87349
87350
87351             function change() {
87352                 var syncTags = {};
87353                 syncTags[field.key] = _qid;
87354                 dispatch$1.call('change', this, syncTags);
87355
87356                 // attempt asynchronous update of wikidata tag..
87357                 var initGraph = context.graph();
87358                 var initEntityIDs = _entityIDs;
87359
87360                 wikidata.entityByQID(_qid, function(err, entity) {
87361                     if (err) return;
87362
87363                     // If graph has changed, we can't apply this update.
87364                     if (context.graph() !== initGraph) return;
87365
87366                     if (!entity.sitelinks) return;
87367
87368                     var langs = wikidata.languagesToQuery();
87369                     // use the label and description languages as fallbacks
87370                     ['labels', 'descriptions'].forEach(function(key) {
87371                         if (!entity[key]) return;
87372
87373                         var valueLangs = Object.keys(entity[key]);
87374                         if (valueLangs.length === 0) return;
87375                         var valueLang = valueLangs[0];
87376
87377                         if (langs.indexOf(valueLang) === -1) {
87378                             langs.push(valueLang);
87379                         }
87380                     });
87381
87382                     var newWikipediaValue;
87383
87384                     if (_wikipediaKey) {
87385                         var foundPreferred;
87386                         for (var i in langs) {
87387                             var lang = langs[i];
87388                             var siteID = lang.replace('-', '_') + 'wiki';
87389                             if (entity.sitelinks[siteID]) {
87390                                 foundPreferred = true;
87391                                 newWikipediaValue = lang + ':' + entity.sitelinks[siteID].title;
87392                                 // use the first match
87393                                 break;
87394                             }
87395                         }
87396
87397                         if (!foundPreferred) {
87398                             // No wikipedia sites available in the user's language or the fallback languages,
87399                             // default to any wikipedia sitelink
87400
87401                             var wikiSiteKeys = Object.keys(entity.sitelinks).filter(function(site) {
87402                                 return site.endsWith('wiki');
87403                             });
87404
87405                             if (wikiSiteKeys.length === 0) {
87406                                 // if no wikipedia pages are linked to this wikidata entity, delete that tag
87407                                 newWikipediaValue = null;
87408                             } else {
87409                                 var wikiLang = wikiSiteKeys[0].slice(0, -4).replace('_', '-');
87410                                 var wikiTitle = entity.sitelinks[wikiSiteKeys[0]].title;
87411                                 newWikipediaValue = wikiLang + ':' + wikiTitle;
87412                             }
87413                         }
87414                     }
87415
87416                     if (newWikipediaValue) {
87417                         newWikipediaValue = context.cleanTagValue(newWikipediaValue);
87418                     }
87419
87420                     if (typeof newWikipediaValue === 'undefined') return;
87421
87422                     var actions = initEntityIDs.map(function(entityID) {
87423                         var entity = context.hasEntity(entityID);
87424                         if (!entity) return;
87425
87426                         var currTags = Object.assign({}, entity.tags);  // shallow copy
87427                         if (newWikipediaValue === null) {
87428                             if (!currTags[_wikipediaKey]) return;
87429
87430                             delete currTags[_wikipediaKey];
87431                         } else {
87432                             currTags[_wikipediaKey] = newWikipediaValue;
87433                         }
87434
87435                         return actionChangeTags(entityID, currTags);
87436                     }).filter(Boolean);
87437
87438                     if (!actions.length) return;
87439
87440                     // Coalesce the update of wikidata tag into the previous tag change
87441                     context.overwrite(
87442                         function actionUpdateWikipediaTags(graph) {
87443                             actions.forEach(function(action) {
87444                                 graph = action(graph);
87445                             });
87446                             return graph;
87447                         },
87448                         context.history().undoAnnotation()
87449                     );
87450
87451                     // do not dispatch.call('change') here, because entity_editor
87452                     // changeTags() is not intended to be called asynchronously
87453                 });
87454             }
87455
87456             function setLabelForEntity() {
87457                 var label = '';
87458                 if (_wikidataEntity) {
87459                     label = entityPropertyForDisplay(_wikidataEntity, 'labels');
87460                     if (label.length === 0) {
87461                         label = _wikidataEntity.id.toString();
87462                     }
87463                 }
87464                 utilGetSetValue(_searchInput, label);
87465             }
87466
87467
87468             wiki.tags = function(tags) {
87469
87470                 var isMixed = Array.isArray(tags[field.key]);
87471                 _searchInput
87472                     .attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : null)
87473                     .attr('placeholder', isMixed ? _t('inspector.multiple_values') : '')
87474                     .classed('mixed', isMixed);
87475
87476                 _qid = typeof tags[field.key] === 'string' && tags[field.key] || '';
87477
87478                 if (!/^Q[0-9]*$/.test(_qid)) {   // not a proper QID
87479                     unrecognized();
87480                     return;
87481                 }
87482
87483                 // QID value in correct format
87484                 _wikiURL = 'https://wikidata.org/wiki/' + _qid;
87485                 wikidata.entityByQID(_qid, function(err, entity) {
87486                     if (err) {
87487                         unrecognized();
87488                         return;
87489                     }
87490                     _wikidataEntity = entity;
87491
87492                     setLabelForEntity();
87493
87494                     var description = entityPropertyForDisplay(entity, 'descriptions');
87495
87496                     _selection.select('button.wiki-link')
87497                         .classed('disabled', false);
87498
87499                     _selection.select('.preset-wikidata-description')
87500                         .style('display', function(){
87501                             return description.length > 0 ? 'flex' : 'none';
87502                         })
87503                         .select('input')
87504                         .attr('value', description);
87505
87506                     _selection.select('.preset-wikidata-identifier')
87507                         .style('display', function(){
87508                             return entity.id ? 'flex' : 'none';
87509                         })
87510                         .select('input')
87511                         .attr('value', entity.id);
87512                 });
87513
87514
87515                 // not a proper QID
87516                 function unrecognized() {
87517                     _wikidataEntity = null;
87518                     setLabelForEntity();
87519
87520                     _selection.select('.preset-wikidata-description')
87521                         .style('display', 'none');
87522                     _selection.select('.preset-wikidata-identifier')
87523                         .style('display', 'none');
87524
87525                     _selection.select('button.wiki-link')
87526                         .classed('disabled', true);
87527
87528                     if (_qid && _qid !== '') {
87529                         _wikiURL = 'https://wikidata.org/wiki/Special:Search?search=' + _qid;
87530                     } else {
87531                         _wikiURL = '';
87532                     }
87533                 }
87534             };
87535
87536             function entityPropertyForDisplay(wikidataEntity, propKey) {
87537                 if (!wikidataEntity[propKey]) return '';
87538                 var propObj = wikidataEntity[propKey];
87539                 var langKeys = Object.keys(propObj);
87540                 if (langKeys.length === 0) return '';
87541                 // sorted by priority, since we want to show the user's language first if possible
87542                 var langs = wikidata.languagesToQuery();
87543                 for (var i in langs) {
87544                     var lang = langs[i];
87545                     var valueObj = propObj[lang];
87546                     if (valueObj && valueObj.value && valueObj.value.length > 0) return valueObj.value;
87547                 }
87548                 // default to any available value
87549                 return propObj[langKeys[0]].value;
87550             }
87551
87552
87553             wiki.entityIDs = function(val) {
87554                 if (!arguments.length) return _entityIDs;
87555                 _entityIDs = val;
87556                 return wiki;
87557             };
87558
87559
87560             wiki.focus = function() {
87561                 _searchInput.node().focus();
87562             };
87563
87564
87565             return utilRebind(wiki, dispatch$1, 'on');
87566         }
87567
87568         function uiFieldWikipedia(field, context) {
87569           const dispatch$1 = dispatch('change');
87570           const wikipedia = services.wikipedia;
87571           const wikidata = services.wikidata;
87572           let _langInput = select(null);
87573           let _titleInput = select(null);
87574           let _wikiURL = '';
87575           let _entityIDs;
87576           let _tags;
87577
87578           let _dataWikipedia = [];
87579           _mainFileFetcher.get('wmf_sitematrix')
87580             .then(d => {
87581               _dataWikipedia = d;
87582               if (_tags) updateForTags(_tags);
87583             })
87584             .catch(() => { /* ignore */ });
87585
87586
87587           const langCombo = uiCombobox(context, 'wikipedia-lang')
87588             .fetcher((value, callback) => {
87589               const v = value.toLowerCase();
87590               callback(_dataWikipedia
87591                 .filter(d => {
87592                   return d[0].toLowerCase().indexOf(v) >= 0 ||
87593                     d[1].toLowerCase().indexOf(v) >= 0 ||
87594                     d[2].toLowerCase().indexOf(v) >= 0;
87595                 })
87596                 .map(d => ({ value: d[1] }))
87597               );
87598             });
87599
87600           const titleCombo = uiCombobox(context, 'wikipedia-title')
87601             .fetcher((value, callback) => {
87602               if (!value) {
87603                 value = '';
87604                 for (let i in _entityIDs) {
87605                   let entity = context.hasEntity(_entityIDs[i]);
87606                   if (entity.tags.name) {
87607                     value = entity.tags.name;
87608                     break;
87609                   }
87610                 }
87611               }
87612               const searchfn = value.length > 7 ? wikipedia.search : wikipedia.suggestions;
87613               searchfn(language()[2], value, (query, data) => {
87614                 callback( data.map(d => ({ value: d })) );
87615               });
87616             });
87617
87618
87619           function wiki(selection) {
87620             let wrap = selection.selectAll('.form-field-input-wrap')
87621               .data([0]);
87622
87623             wrap = wrap.enter()
87624               .append('div')
87625               .attr('class', `form-field-input-wrap form-field-input-${field.type}`)
87626               .merge(wrap);
87627
87628
87629             let langContainer = wrap.selectAll('.wiki-lang-container')
87630               .data([0]);
87631
87632             langContainer = langContainer.enter()
87633               .append('div')
87634               .attr('class', 'wiki-lang-container')
87635               .merge(langContainer);
87636
87637
87638             _langInput = langContainer.selectAll('input.wiki-lang')
87639               .data([0]);
87640
87641             _langInput = _langInput.enter()
87642               .append('input')
87643               .attr('type', 'text')
87644               .attr('class', 'wiki-lang')
87645               .attr('placeholder', _t('translate.localized_translation_language'))
87646               .call(utilNoAuto)
87647               .call(langCombo)
87648               .merge(_langInput);
87649
87650             utilGetSetValue(_langInput, language()[1]);
87651
87652             _langInput
87653               .on('blur', changeLang)
87654               .on('change', changeLang);
87655
87656
87657             let titleContainer = wrap.selectAll('.wiki-title-container')
87658               .data([0]);
87659
87660             titleContainer = titleContainer.enter()
87661               .append('div')
87662               .attr('class', 'wiki-title-container')
87663               .merge(titleContainer);
87664
87665             _titleInput = titleContainer.selectAll('input.wiki-title')
87666               .data([0]);
87667
87668             _titleInput = _titleInput.enter()
87669               .append('input')
87670               .attr('type', 'text')
87671               .attr('class', 'wiki-title')
87672               .attr('id', field.domId)
87673               .call(utilNoAuto)
87674               .call(titleCombo)
87675               .merge(_titleInput);
87676
87677             _titleInput
87678               .on('blur', blur)
87679               .on('change', change);
87680
87681
87682             let link = titleContainer.selectAll('.wiki-link')
87683               .data([0]);
87684
87685             link = link.enter()
87686               .append('button')
87687               .attr('class', 'form-field-button wiki-link')
87688               .attr('tabindex', -1)
87689               .attr('title', _t('icons.view_on', { domain: 'wikipedia.org' }))
87690               .call(svgIcon('#iD-icon-out-link'))
87691               .merge(link);
87692
87693             link
87694               .on('click', () => {
87695                 event.preventDefault();
87696                 if (_wikiURL) window.open(_wikiURL, '_blank');
87697               });
87698           }
87699
87700
87701           function language() {
87702             const value = utilGetSetValue(_langInput).toLowerCase();
87703             const locale = _mainLocalizer.localeCode().toLowerCase();
87704             let localeLanguage;
87705             return _dataWikipedia.find(d => {
87706               if (d[2] === locale) localeLanguage = d;
87707               return d[0].toLowerCase() === value || d[1].toLowerCase() === value || d[2] === value;
87708             }) || localeLanguage || ['English', 'English', 'en'];
87709           }
87710
87711
87712           function changeLang() {
87713             utilGetSetValue(_langInput, language()[1]);
87714             change(true);
87715           }
87716
87717
87718           function blur() {
87719             change(true);
87720           }
87721
87722
87723           function change(skipWikidata) {
87724             let value = utilGetSetValue(_titleInput);
87725             const m = value.match(/https?:\/\/([-a-z]+)\.wikipedia\.org\/(?:wiki|\1-[-a-z]+)\/([^#]+)(?:#(.+))?/);
87726             const l = m && _dataWikipedia.find(d => m[1] === d[2]);
87727             let syncTags = {};
87728
87729             if (l) {
87730               // Normalize title http://www.mediawiki.org/wiki/API:Query#Title_normalization
87731               value = decodeURIComponent(m[2]).replace(/_/g, ' ');
87732               if (m[3]) {
87733                 let anchor;
87734                 // try {
87735                 // leave this out for now - #6232
87736                   // Best-effort `anchordecode:` implementation
87737                   // anchor = decodeURIComponent(m[3].replace(/\.([0-9A-F]{2})/g, '%$1'));
87738                 // } catch (e) {
87739                 anchor = decodeURIComponent(m[3]);
87740                 // }
87741                 value += '#' + anchor.replace(/_/g, ' ');
87742               }
87743               value = value.slice(0, 1).toUpperCase() + value.slice(1);
87744               utilGetSetValue(_langInput, l[1]);
87745               utilGetSetValue(_titleInput, value);
87746             }
87747
87748             if (value) {
87749               syncTags.wikipedia = context.cleanTagValue(language()[2] + ':' + value);
87750             } else {
87751               syncTags.wikipedia = undefined;
87752             }
87753
87754             dispatch$1.call('change', this, syncTags);
87755
87756
87757             if (skipWikidata || !value || !language()[2]) return;
87758
87759             // attempt asynchronous update of wikidata tag..
87760             const initGraph = context.graph();
87761             const initEntityIDs = _entityIDs;
87762
87763             wikidata.itemsByTitle(language()[2], value, (err, data) => {
87764               if (err || !data || !Object.keys(data).length) return;
87765
87766               // If graph has changed, we can't apply this update.
87767               if (context.graph() !== initGraph) return;
87768
87769               const qids = Object.keys(data);
87770               const value = qids && qids.find(id => id.match(/^Q\d+$/));
87771
87772               let actions = initEntityIDs.map((entityID) => {
87773                 let entity = context.entity(entityID).tags;
87774                 let currTags = Object.assign({}, entity);  // shallow copy
87775                 if (currTags.wikidata !== value) {
87776                     currTags.wikidata = value;
87777                     return actionChangeTags(entityID, currTags);
87778                 }
87779               }).filter(Boolean);
87780
87781               if (!actions.length) return;
87782
87783               // Coalesce the update of wikidata tag into the previous tag change
87784               context.overwrite(
87785                 function actionUpdateWikidataTags(graph) {
87786                   actions.forEach(function(action) {
87787                     graph = action(graph);
87788                   });
87789                   return graph;
87790                 },
87791                 context.history().undoAnnotation()
87792               );
87793
87794               // do not dispatch.call('change') here, because entity_editor
87795               // changeTags() is not intended to be called asynchronously
87796             });
87797           }
87798
87799
87800           wiki.tags = (tags) => {
87801             _tags = tags;
87802             updateForTags(tags);
87803           };
87804
87805           function updateForTags(tags) {
87806
87807             const value = typeof tags[field.key] === 'string' ? tags[field.key] : '';
87808             const m = value.match(/([^:]+):([^#]+)(?:#(.+))?/);
87809             const l = m && _dataWikipedia.find(d => m[1] === d[2]);
87810             let anchor = m && m[3];
87811
87812             // value in correct format
87813             if (l) {
87814               utilGetSetValue(_langInput, l[1]);
87815               utilGetSetValue(_titleInput, m[2] + (anchor ? ('#' + anchor) : ''));
87816               if (anchor) {
87817                 try {
87818                   // Best-effort `anchorencode:` implementation
87819                   anchor = encodeURIComponent(anchor.replace(/ /g, '_')).replace(/%/g, '.');
87820                 } catch (e) {
87821                   anchor = anchor.replace(/ /g, '_');
87822                 }
87823               }
87824               _wikiURL = 'https://' + m[1] + '.wikipedia.org/wiki/' +
87825                 m[2].replace(/ /g, '_') + (anchor ? ('#' + anchor) : '');
87826
87827             // unrecognized value format
87828             } else {
87829               utilGetSetValue(_titleInput, value);
87830               if (value && value !== '') {
87831                 utilGetSetValue(_langInput, '');
87832                 _wikiURL = `https://en.wikipedia.org/wiki/Special:Search?search=${value}`;
87833               } else {
87834                 _wikiURL = '';
87835               }
87836             }
87837           }
87838
87839
87840           wiki.entityIDs = (val) => {
87841             if (!arguments.length) return _entityIDs;
87842             _entityIDs = val;
87843             return wiki;
87844           };
87845
87846
87847           wiki.focus = () => {
87848             _titleInput.node().focus();
87849           };
87850
87851
87852           return utilRebind(wiki, dispatch$1, 'on');
87853         }
87854
87855         uiFieldWikipedia.supportsMultiselection = false;
87856
87857         var uiFields = {
87858             access: uiFieldAccess,
87859             address: uiFieldAddress,
87860             check: uiFieldCheck,
87861             combo: uiFieldCombo,
87862             cycleway: uiFieldCycleway,
87863             defaultCheck: uiFieldCheck,
87864             email: uiFieldText,
87865             identifier: uiFieldText,
87866             lanes: uiFieldLanes,
87867             localized: uiFieldLocalized,
87868             maxspeed: uiFieldMaxspeed,
87869             multiCombo: uiFieldCombo,
87870             networkCombo: uiFieldCombo,
87871             number: uiFieldText,
87872             onewayCheck: uiFieldCheck,
87873             radio: uiFieldRadio,
87874             restrictions: uiFieldRestrictions,
87875             semiCombo: uiFieldCombo,
87876             structureRadio: uiFieldRadio,
87877             tel: uiFieldText,
87878             text: uiFieldText,
87879             textarea: uiFieldTextarea,
87880             typeCombo: uiFieldCombo,
87881             url: uiFieldText,
87882             wikidata: uiFieldWikidata,
87883             wikipedia: uiFieldWikipedia
87884         };
87885
87886         function uiField(context, presetField, entityIDs, options) {
87887             options = Object.assign({
87888                 show: true,
87889                 wrap: true,
87890                 remove: true,
87891                 revert: true,
87892                 info: true
87893             }, options);
87894
87895             var dispatch$1 = dispatch('change', 'revert');
87896             var field = Object.assign({}, presetField);   // shallow copy
87897             field.domId = utilUniqueDomId('form-field-' + field.safeid);
87898             var _show = options.show;
87899             var _state = '';
87900             var _tags = {};
87901
87902             var _locked = false;
87903             var _lockedTip = uiTooltip()
87904                 .title(_t('inspector.lock.suggestion', { label: field.label }))
87905                 .placement('bottom');
87906
87907
87908             field.keys = field.keys || [field.key];
87909
87910             // only create the fields that are actually being shown
87911             if (_show && !field.impl) {
87912                 createField();
87913             }
87914
87915             // Creates the field.. This is done lazily,
87916             // once we know that the field will be shown.
87917             function createField() {
87918                 field.impl = uiFields[field.type](field, context)
87919                     .on('change', function(t, onInput) {
87920                         dispatch$1.call('change', field, t, onInput);
87921                     });
87922
87923                 if (entityIDs) {
87924                     field.entityIDs = entityIDs;
87925                     // if this field cares about the entities, pass them along
87926                     if (field.impl.entityIDs) {
87927                         field.impl.entityIDs(entityIDs);
87928                     }
87929                 }
87930             }
87931
87932
87933             function isModified() {
87934                 if (!entityIDs || !entityIDs.length) return false;
87935                 return entityIDs.some(function(entityID) {
87936                     var original = context.graph().base().entities[entityID];
87937                     var latest = context.graph().entity(entityID);
87938                     return field.keys.some(function(key) {
87939                         return original ? latest.tags[key] !== original.tags[key] : latest.tags[key];
87940                     });
87941                 });
87942             }
87943
87944
87945             function tagsContainFieldKey() {
87946                 return field.keys.some(function(key) {
87947                     if (field.type === 'multiCombo') {
87948                         for (var tagKey in _tags) {
87949                             if (tagKey.indexOf(key) === 0) {
87950                                 return true;
87951                             }
87952                         }
87953                         return false;
87954                     }
87955                     return _tags[key] !== undefined;
87956                 });
87957             }
87958
87959
87960             function revert(d) {
87961                 event.stopPropagation();
87962                 event.preventDefault();
87963                 if (!entityIDs || _locked) return;
87964
87965                 dispatch$1.call('revert', d, d.keys);
87966             }
87967
87968
87969             function remove(d) {
87970                 event.stopPropagation();
87971                 event.preventDefault();
87972                 if (_locked) return;
87973
87974                 var t = {};
87975                 d.keys.forEach(function(key) {
87976                     t[key] = undefined;
87977                 });
87978
87979                 dispatch$1.call('change', d, t);
87980             }
87981
87982
87983             field.render = function(selection) {
87984                 var container = selection.selectAll('.form-field')
87985                     .data([field]);
87986
87987                 // Enter
87988                 var enter = container.enter()
87989                     .append('div')
87990                     .attr('class', function(d) { return 'form-field form-field-' + d.safeid; })
87991                     .classed('nowrap', !options.wrap);
87992
87993                 if (options.wrap) {
87994                     var labelEnter = enter
87995                         .append('label')
87996                         .attr('class', 'field-label')
87997                         .attr('for', function(d) { return d.domId; });
87998
87999                     var textEnter = labelEnter
88000                         .append('span')
88001                         .attr('class', 'label-text');
88002
88003                     textEnter
88004                         .append('span')
88005                         .attr('class', 'label-textvalue')
88006                         .text(function(d) { return d.label(); });
88007
88008                     textEnter
88009                         .append('span')
88010                         .attr('class', 'label-textannotation');
88011
88012                     if (options.remove) {
88013                         labelEnter
88014                             .append('button')
88015                             .attr('class', 'remove-icon')
88016                             .attr('title', _t('icons.remove'))
88017                             .attr('tabindex', -1)
88018                             .call(svgIcon('#iD-operation-delete'));
88019                     }
88020
88021                     if (options.revert) {
88022                         labelEnter
88023                             .append('button')
88024                             .attr('class', 'modified-icon')
88025                             .attr('title', _t('icons.undo'))
88026                             .attr('tabindex', -1)
88027                             .call(svgIcon((_mainLocalizer.textDirection() === 'rtl') ? '#iD-icon-redo' : '#iD-icon-undo'));
88028                     }
88029                 }
88030
88031
88032                 // Update
88033                 container = container
88034                     .merge(enter);
88035
88036                 container.select('.field-label > .remove-icon')  // propagate bound data
88037                     .on('click', remove);
88038
88039                 container.select('.field-label > .modified-icon')  // propagate bound data
88040                     .on('click', revert);
88041
88042                 container
88043                     .each(function(d) {
88044                         var selection = select(this);
88045
88046                         if (!d.impl) {
88047                             createField();
88048                         }
88049
88050                         var reference, help;
88051
88052                         // instantiate field help
88053                         if (options.wrap && field.type === 'restrictions') {
88054                             help = uiFieldHelp(context, 'restrictions');
88055                         }
88056
88057                         // instantiate tag reference
88058                         if (options.wrap && options.info) {
88059                             var referenceKey = d.key;
88060                             if (d.type === 'multiCombo') {   // lookup key without the trailing ':'
88061                                 referenceKey = referenceKey.replace(/:$/, '');
88062                             }
88063
88064                             reference = uiTagReference(d.reference || { key: referenceKey });
88065                             if (_state === 'hover') {
88066                                 reference.showing(false);
88067                             }
88068                         }
88069
88070                         selection
88071                             .call(d.impl);
88072
88073                         // add field help components
88074                         if (help) {
88075                             selection
88076                                 .call(help.body)
88077                                 .select('.field-label')
88078                                 .call(help.button);
88079                         }
88080
88081                         // add tag reference components
88082                         if (reference) {
88083                             selection
88084                                 .call(reference.body)
88085                                 .select('.field-label')
88086                                 .call(reference.button);
88087                         }
88088
88089                         d.impl.tags(_tags);
88090                     });
88091
88092
88093                     container
88094                         .classed('locked', _locked)
88095                         .classed('modified', isModified())
88096                         .classed('present', tagsContainFieldKey());
88097
88098
88099                     // show a tip and lock icon if the field is locked
88100                     var annotation = container.selectAll('.field-label .label-textannotation');
88101                     var icon = annotation.selectAll('.icon')
88102                         .data(_locked ? [0]: []);
88103
88104                     icon.exit()
88105                         .remove();
88106
88107                     icon.enter()
88108                         .append('svg')
88109                         .attr('class', 'icon')
88110                         .append('use')
88111                         .attr('xlink:href', '#fas-lock');
88112
88113                     container.call(_locked ? _lockedTip : _lockedTip.destroy);
88114             };
88115
88116
88117             field.state = function(val) {
88118                 if (!arguments.length) return _state;
88119                 _state = val;
88120                 return field;
88121             };
88122
88123
88124             field.tags = function(val) {
88125                 if (!arguments.length) return _tags;
88126                 _tags = val;
88127
88128                 if (tagsContainFieldKey() && !_show) {
88129                     // always show a field if it has a value to display
88130                     _show = true;
88131                     if (!field.impl) {
88132                         createField();
88133                     }
88134                 }
88135
88136                 return field;
88137             };
88138
88139
88140             field.locked = function(val) {
88141                 if (!arguments.length) return _locked;
88142                 _locked = val;
88143                 return field;
88144             };
88145
88146
88147             field.show = function() {
88148                 _show = true;
88149                 if (!field.impl) {
88150                     createField();
88151                 }
88152                 if (field.default && field.key && _tags[field.key] !== field.default) {
88153                     var t = {};
88154                     t[field.key] = field.default;
88155                     dispatch$1.call('change', this, t);
88156                 }
88157             };
88158
88159             // A shown field has a visible UI, a non-shown field is in the 'Add field' dropdown
88160             field.isShown = function() {
88161                 return _show;
88162             };
88163
88164
88165             // An allowed field can appear in the UI or in the 'Add field' dropdown.
88166             // A non-allowed field is hidden from the user altogether
88167             field.isAllowed = function() {
88168
88169                 if (entityIDs &&
88170                     entityIDs.length > 1 &&
88171                     uiFields[field.type].supportsMultiselection === false) return false;
88172
88173                 if (field.geometry && !entityIDs.every(function(entityID) {
88174                     return field.matchGeometry(context.graph().geometry(entityID));
88175                 })) return false;
88176
88177                 if (field.countryCodes || field.notCountryCodes) {
88178                     var extent = combinedEntityExtent();
88179                     if (!extent) return true;
88180
88181                     var center = extent.center();
88182                     var countryCode = iso1A2Code(center);
88183
88184                     if (!countryCode) return false;
88185
88186                     countryCode = countryCode.toLowerCase();
88187
88188                     if (field.countryCodes && field.countryCodes.indexOf(countryCode) === -1) {
88189                         return false;
88190                     }
88191                     if (field.notCountryCodes && field.notCountryCodes.indexOf(countryCode) !== -1) {
88192                         return false;
88193                     }
88194                 }
88195
88196                 var prerequisiteTag = field.prerequisiteTag;
88197
88198                 if (entityIDs &&
88199                     !tagsContainFieldKey() && // ignore tagging prerequisites if a value is already present
88200                     prerequisiteTag) {
88201
88202                     if (!entityIDs.every(function(entityID) {
88203                         var entity = context.graph().entity(entityID);
88204                         if (prerequisiteTag.key) {
88205                             var value = entity.tags[prerequisiteTag.key];
88206                             if (!value) return false;
88207
88208                             if (prerequisiteTag.valueNot) {
88209                                 return prerequisiteTag.valueNot !== value;
88210                             }
88211                             if (prerequisiteTag.value) {
88212                                 return prerequisiteTag.value === value;
88213                             }
88214                         } else if (prerequisiteTag.keyNot) {
88215                             if (entity.tags[prerequisiteTag.keyNot]) return false;
88216                         }
88217                         return true;
88218                     })) return false;
88219                 }
88220
88221                 return true;
88222             };
88223
88224
88225             field.focus = function() {
88226                 if (field.impl) {
88227                     field.impl.focus();
88228                 }
88229             };
88230
88231
88232             function combinedEntityExtent() {
88233                 return entityIDs && entityIDs.length && entityIDs.reduce(function(extent, entityID) {
88234                     var entity = context.graph().entity(entityID);
88235                     return extent.extend(entity.extent(context.graph()));
88236                 }, geoExtent());
88237             }
88238
88239
88240             return utilRebind(field, dispatch$1, 'on');
88241         }
88242
88243         function uiFormFields(context) {
88244             var moreCombo = uiCombobox(context, 'more-fields').minItems(1);
88245             var _fieldsArr = [];
88246             var _lastPlaceholder = '';
88247             var _state = '';
88248             var _klass = '';
88249
88250
88251             function formFields(selection) {
88252                 var allowedFields = _fieldsArr.filter(function(field) { return field.isAllowed(); });
88253                 var shown = allowedFields.filter(function(field) { return field.isShown(); });
88254                 var notShown = allowedFields.filter(function(field) { return !field.isShown(); });
88255
88256                 var container = selection.selectAll('.form-fields-container')
88257                     .data([0]);
88258
88259                 container = container.enter()
88260                     .append('div')
88261                     .attr('class', 'form-fields-container ' + (_klass || ''))
88262                     .merge(container);
88263
88264
88265                 var fields = container.selectAll('.wrap-form-field')
88266                     .data(shown, function(d) { return d.id + (d.entityIDs ? d.entityIDs.join() : ''); });
88267
88268                 fields.exit()
88269                     .remove();
88270
88271                 // Enter
88272                 var enter = fields.enter()
88273                     .append('div')
88274                     .attr('class', function(d) { return 'wrap-form-field wrap-form-field-' + d.safeid; });
88275
88276                 // Update
88277                 fields = fields
88278                     .merge(enter);
88279
88280                 fields
88281                     .order()
88282                     .each(function(d) {
88283                         select(this)
88284                             .call(d.render);
88285                     });
88286
88287
88288                 var titles = [];
88289                 var moreFields = notShown.map(function(field) {
88290                     var label = field.label();
88291                     titles.push(label);
88292
88293                     var terms = field.terms();
88294                     if (field.key) terms.push(field.key);
88295                     if (field.keys) terms = terms.concat(field.keys);
88296
88297                     return {
88298                         title: label,
88299                         value: label,
88300                         field: field,
88301                         terms: terms
88302                     };
88303                 });
88304
88305                 var placeholder = titles.slice(0,3).join(', ') + ((titles.length > 3) ? '…' : '');
88306
88307
88308                 var more = selection.selectAll('.more-fields')
88309                     .data((_state === 'hover' || moreFields.length === 0) ? [] : [0]);
88310
88311                 more.exit()
88312                     .remove();
88313
88314                 var moreEnter = more.enter()
88315                     .append('div')
88316                     .attr('class', 'more-fields')
88317                     .append('label');
88318
88319                 moreEnter
88320                     .append('span')
88321                     .text(_t('inspector.add_fields'));
88322
88323                 more = moreEnter
88324                     .merge(more);
88325
88326
88327                 var input = more.selectAll('.value')
88328                     .data([0]);
88329
88330                 input.exit()
88331                     .remove();
88332
88333                 input = input.enter()
88334                     .append('input')
88335                     .attr('class', 'value')
88336                     .attr('type', 'text')
88337                     .attr('placeholder', placeholder)
88338                     .call(utilNoAuto)
88339                     .merge(input);
88340
88341                 input
88342                     .call(utilGetSetValue, '')
88343                     .call(moreCombo
88344                         .data(moreFields)
88345                         .on('accept', function (d) {
88346                             if (!d) return;  // user entered something that was not matched
88347                             var field = d.field;
88348                             field.show();
88349                             selection.call(formFields);  // rerender
88350                             if (field.type !== 'semiCombo' && field.type !== 'multiCombo') {
88351                                 field.focus();
88352                             }
88353                         })
88354                     );
88355
88356                 // avoid updating placeholder excessively (triggers style recalc)
88357                 if (_lastPlaceholder !== placeholder) {
88358                     input.attr('placeholder', placeholder);
88359                     _lastPlaceholder = placeholder;
88360                 }
88361             }
88362
88363
88364             formFields.fieldsArr = function(val) {
88365                 if (!arguments.length) return _fieldsArr;
88366                 _fieldsArr = val || [];
88367                 return formFields;
88368             };
88369
88370             formFields.state = function(val) {
88371                 if (!arguments.length) return _state;
88372                 _state = val;
88373                 return formFields;
88374             };
88375
88376             formFields.klass = function(val) {
88377                 if (!arguments.length) return _klass;
88378                 _klass = val;
88379                 return formFields;
88380             };
88381
88382
88383             return formFields;
88384         }
88385
88386         function uiSectionPresetFields(context) {
88387
88388             var section = uiSection('preset-fields', context)
88389                 .title(function() {
88390                     return _t('inspector.fields');
88391                 })
88392                 .disclosureContent(renderDisclosureContent);
88393
88394             var dispatch$1 = dispatch('change', 'revert');
88395             var formFields = uiFormFields(context);
88396             var _state;
88397             var _fieldsArr;
88398             var _presets = [];
88399             var _tags;
88400             var _entityIDs;
88401
88402             function renderDisclosureContent(selection) {
88403                 if (!_fieldsArr) {
88404
88405                     var graph = context.graph();
88406
88407                     var geometries = Object.keys(_entityIDs.reduce(function(geoms, entityID) {
88408                         return geoms[graph.entity(entityID).geometry(graph)] = true;
88409                     }, {}));
88410
88411                     var presetsManager = _mainPresetIndex;
88412
88413                     var allFields = [];
88414                     var allMoreFields = [];
88415                     var sharedTotalFields;
88416
88417                     _presets.forEach(function(preset) {
88418                         var fields = preset.fields();
88419                         var moreFields = preset.moreFields();
88420
88421                         allFields = utilArrayUnion(allFields, fields);
88422                         allMoreFields = utilArrayUnion(allMoreFields, moreFields);
88423
88424                         if (!sharedTotalFields) {
88425                             sharedTotalFields = utilArrayUnion(fields, moreFields);
88426                         } else {
88427                             sharedTotalFields = sharedTotalFields.filter(function(field) {
88428                                 return fields.indexOf(field) !== -1 || moreFields.indexOf(field) !== -1;
88429                             });
88430                         }
88431                     });
88432
88433                     var sharedFields = allFields.filter(function(field) {
88434                         return sharedTotalFields.indexOf(field) !== -1;
88435                     });
88436                     var sharedMoreFields = allMoreFields.filter(function(field) {
88437                         return sharedTotalFields.indexOf(field) !== -1;
88438                     });
88439
88440                     _fieldsArr = [];
88441
88442                     sharedFields.forEach(function(field) {
88443                         if (field.matchAllGeometry(geometries)) {
88444                             _fieldsArr.push(
88445                                 uiField(context, field, _entityIDs)
88446                             );
88447                         }
88448                     });
88449
88450                     var singularEntity = _entityIDs.length === 1 && graph.hasEntity(_entityIDs[0]);
88451                     if (singularEntity && singularEntity.isHighwayIntersection(graph) && presetsManager.field('restrictions')) {
88452                         _fieldsArr.push(
88453                             uiField(context, presetsManager.field('restrictions'), _entityIDs)
88454                         );
88455                     }
88456
88457                     var additionalFields = utilArrayUnion(sharedMoreFields, presetsManager.universal());
88458                     additionalFields.sort(function(field1, field2) {
88459                         return field1.label().localeCompare(field2.label(), _mainLocalizer.localeCode());
88460                     });
88461
88462                     additionalFields.forEach(function(field) {
88463                         if (sharedFields.indexOf(field) === -1 &&
88464                             field.matchAllGeometry(geometries)) {
88465                             _fieldsArr.push(
88466                                 uiField(context, field, _entityIDs, { show: false })
88467                             );
88468                         }
88469                     });
88470
88471                     _fieldsArr.forEach(function(field) {
88472                         field
88473                             .on('change', function(t, onInput) {
88474                                 dispatch$1.call('change', field, _entityIDs, t, onInput);
88475                             })
88476                             .on('revert', function(keys) {
88477                                 dispatch$1.call('revert', field, keys);
88478                             });
88479                     });
88480                 }
88481
88482                 _fieldsArr.forEach(function(field) {
88483                     field
88484                         .state(_state)
88485                         .tags(_tags);
88486                 });
88487
88488
88489                 selection
88490                     .call(formFields
88491                         .fieldsArr(_fieldsArr)
88492                         .state(_state)
88493                         .klass('grouped-items-area')
88494                     );
88495
88496
88497                 selection.selectAll('.wrap-form-field input')
88498                     .on('keydown', function() {
88499                         // if user presses enter, and combobox is not active, accept edits..
88500                         if (event.keyCode === 13 && context.container().select('.combobox').empty()) {
88501                             context.enter(modeBrowse(context));
88502                         }
88503                     });
88504             }
88505
88506             section.presets = function(val) {
88507                 if (!arguments.length) return _presets;
88508                 if (!_presets || !val || !utilArrayIdentical(_presets, val)) {
88509                     _presets = val;
88510                     _fieldsArr = null;
88511                 }
88512                 return section;
88513             };
88514
88515             section.state = function(val) {
88516                 if (!arguments.length) return _state;
88517                 _state = val;
88518                 return section;
88519             };
88520
88521             section.tags = function(val) {
88522                 if (!arguments.length) return _tags;
88523                 _tags = val;
88524                 // Don't reset _fieldsArr here.
88525                 return section;
88526             };
88527
88528             section.entityIDs = function(val) {
88529                 if (!arguments.length) return _entityIDs;
88530                 if (!val || !_entityIDs || !utilArrayIdentical(_entityIDs, val)) {
88531                     _entityIDs = val;
88532                     _fieldsArr = null;
88533                 }
88534                 return section;
88535             };
88536
88537             return utilRebind(section, dispatch$1, 'on');
88538         }
88539
88540         function uiSectionRawMemberEditor(context) {
88541
88542             var section = uiSection('raw-member-editor', context)
88543                 .shouldDisplay(function() {
88544                     if (!_entityIDs || _entityIDs.length !== 1) return false;
88545
88546                     var entity = context.hasEntity(_entityIDs[0]);
88547                     return entity && entity.type === 'relation';
88548                 })
88549                 .title(function() {
88550                     var entity = context.hasEntity(_entityIDs[0]);
88551                     if (!entity) return '';
88552
88553                     var gt = entity.members.length > _maxMembers ? '>' : '';
88554                     var count = gt + entity.members.slice(0, _maxMembers).length;
88555                     return _t('inspector.title_count', { title: _t('inspector.members'), count: count });
88556                 })
88557                 .disclosureContent(renderDisclosureContent);
88558
88559             var taginfo = services.taginfo;
88560             var _entityIDs;
88561             var _maxMembers = 1000;
88562
88563             function downloadMember(d) {
88564                 event.preventDefault();
88565
88566                 // display the loading indicator
88567                 select(this.parentNode).classed('tag-reference-loading', true);
88568                 context.loadEntity(d.id, function() {
88569                     section.reRender();
88570                 });
88571             }
88572
88573             function zoomToMember(d) {
88574                 event.preventDefault();
88575
88576                 var entity = context.entity(d.id);
88577                 context.map().zoomToEase(entity);
88578
88579                 // highlight the feature in case it wasn't previously on-screen
88580                 utilHighlightEntities([d.id], true, context);
88581             }
88582
88583
88584             function selectMember(d) {
88585                 event.preventDefault();
88586
88587                 // remove the hover-highlight styling
88588                 utilHighlightEntities([d.id], false, context);
88589
88590                 var entity = context.entity(d.id);
88591                 var mapExtent = context.map().extent();
88592                 if (!entity.intersects(mapExtent, context.graph())) {
88593                     // zoom to the entity if its extent is not visible now
88594                     context.map().zoomToEase(entity);
88595                 }
88596
88597                 context.enter(modeSelect(context, [d.id]));
88598             }
88599
88600
88601             function changeRole(d) {
88602                 var oldRole = d.role;
88603                 var newRole = context.cleanRelationRole(select(this).property('value'));
88604
88605                 if (oldRole !== newRole) {
88606                     var member = { id: d.id, type: d.type, role: newRole };
88607                     context.perform(
88608                         actionChangeMember(d.relation.id, member, d.index),
88609                         _t('operations.change_role.annotation')
88610                     );
88611                 }
88612             }
88613
88614
88615             function deleteMember(d) {
88616
88617                 // remove the hover-highlight styling
88618                 utilHighlightEntities([d.id], false, context);
88619
88620                 context.perform(
88621                     actionDeleteMember(d.relation.id, d.index),
88622                     _t('operations.delete_member.annotation')
88623                 );
88624
88625                 if (!context.hasEntity(d.relation.id)) {
88626                     context.enter(modeBrowse(context));
88627                 }
88628             }
88629
88630             function renderDisclosureContent(selection) {
88631
88632                 var entityID = _entityIDs[0];
88633
88634                 var memberships = [];
88635                 var entity = context.entity(entityID);
88636                 entity.members.slice(0, _maxMembers).forEach(function(member, index) {
88637                     memberships.push({
88638                         index: index,
88639                         id: member.id,
88640                         type: member.type,
88641                         role: member.role,
88642                         relation: entity,
88643                         member: context.hasEntity(member.id),
88644                         domId: utilUniqueDomId(entityID + '-member-' + index)
88645                     });
88646                 });
88647
88648                 var list = selection.selectAll('.member-list')
88649                     .data([0]);
88650
88651                 list = list.enter()
88652                     .append('ul')
88653                     .attr('class', 'member-list')
88654                     .merge(list);
88655
88656
88657                 var items = list.selectAll('li')
88658                     .data(memberships, function(d) {
88659                         return osmEntity.key(d.relation) + ',' + d.index + ',' +
88660                             (d.member ? osmEntity.key(d.member) : 'incomplete');
88661                     });
88662
88663                 items.exit()
88664                     .each(unbind)
88665                     .remove();
88666
88667                 var itemsEnter = items.enter()
88668                     .append('li')
88669                     .attr('class', 'member-row form-field')
88670                     .classed('member-incomplete', function(d) { return !d.member; });
88671
88672                 itemsEnter
88673                     .each(function(d) {
88674                         var item = select(this);
88675
88676                         var label = item
88677                             .append('label')
88678                             .attr('class', 'field-label')
88679                             .attr('for', d.domId);
88680
88681                         if (d.member) {
88682                             // highlight the member feature in the map while hovering on the list item
88683                             item
88684                                 .on('mouseover', function() {
88685                                     utilHighlightEntities([d.id], true, context);
88686                                 })
88687                                 .on('mouseout', function() {
88688                                     utilHighlightEntities([d.id], false, context);
88689                                 });
88690
88691                             var labelLink = label
88692                                 .append('span')
88693                                 .attr('class', 'label-text')
88694                                 .append('a')
88695                                 .attr('href', '#')
88696                                 .on('click', selectMember);
88697
88698                             labelLink
88699                                 .append('span')
88700                                 .attr('class', 'member-entity-type')
88701                                 .text(function(d) {
88702                                     var matched = _mainPresetIndex.match(d.member, context.graph());
88703                                     return (matched && matched.name()) || utilDisplayType(d.member.id);
88704                                 });
88705
88706                             labelLink
88707                                 .append('span')
88708                                 .attr('class', 'member-entity-name')
88709                                 .text(function(d) { return utilDisplayName(d.member); });
88710
88711                             label
88712                                 .append('button')
88713                                 .attr('tabindex', -1)
88714                                 .attr('title', _t('icons.remove'))
88715                                 .attr('class', 'remove member-delete')
88716                                 .call(svgIcon('#iD-operation-delete'));
88717
88718                             label
88719                                 .append('button')
88720                                 .attr('class', 'member-zoom')
88721                                 .attr('title', _t('icons.zoom_to'))
88722                                 .call(svgIcon('#iD-icon-framed-dot', 'monochrome'))
88723                                 .on('click', zoomToMember);
88724
88725                         } else {
88726                             var labelText = label
88727                                 .append('span')
88728                                 .attr('class', 'label-text');
88729
88730                             labelText
88731                                 .append('span')
88732                                 .attr('class', 'member-entity-type')
88733                                 .text(_t('inspector.' + d.type, { id: d.id }));
88734
88735                             labelText
88736                                 .append('span')
88737                                 .attr('class', 'member-entity-name')
88738                                 .text(_t('inspector.incomplete', { id: d.id }));
88739
88740                             label
88741                                 .append('button')
88742                                 .attr('class', 'member-download')
88743                                 .attr('title', _t('icons.download'))
88744                                 .attr('tabindex', -1)
88745                                 .call(svgIcon('#iD-icon-load'))
88746                                 .on('click', downloadMember);
88747                         }
88748                     });
88749
88750                 var wrapEnter = itemsEnter
88751                     .append('div')
88752                     .attr('class', 'form-field-input-wrap form-field-input-member');
88753
88754                 wrapEnter
88755                     .append('input')
88756                     .attr('class', 'member-role')
88757                     .attr('id', function(d) {
88758                         return d.domId;
88759                     })
88760                     .property('type', 'text')
88761                     .attr('placeholder', _t('inspector.role'))
88762                     .call(utilNoAuto);
88763
88764                 if (taginfo) {
88765                     wrapEnter.each(bindTypeahead);
88766                 }
88767
88768                 // update
88769                 items = items
88770                     .merge(itemsEnter)
88771                     .order();
88772
88773                 items.select('input.member-role')
88774                     .property('value', function(d) { return d.role; })
88775                     .on('blur', changeRole)
88776                     .on('change', changeRole);
88777
88778                 items.select('button.member-delete')
88779                     .on('click', deleteMember);
88780
88781                 var dragOrigin, targetIndex;
88782
88783                 items.call(d3_drag()
88784                     .on('start', function() {
88785                         dragOrigin = {
88786                             x: event.x,
88787                             y: event.y
88788                         };
88789                         targetIndex = null;
88790                     })
88791                     .on('drag', function(d, index) {
88792                         var x = event.x - dragOrigin.x,
88793                             y = event.y - dragOrigin.y;
88794
88795                         if (!select(this).classed('dragging') &&
88796                             // don't display drag until dragging beyond a distance threshold
88797                             Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) <= 5) return;
88798
88799                         select(this)
88800                             .classed('dragging', true);
88801
88802                         targetIndex = null;
88803
88804                         selection.selectAll('li.member-row')
88805                             .style('transform', function(d2, index2) {
88806                                 var node = select(this).node();
88807                                 if (index === index2) {
88808                                     return 'translate(' + x + 'px, ' + y + 'px)';
88809                                 } else if (index2 > index && event.y > node.offsetTop) {
88810                                     if (targetIndex === null || index2 > targetIndex) {
88811                                         targetIndex = index2;
88812                                     }
88813                                     return 'translateY(-100%)';
88814                                 } else if (index2 < index && event.y < node.offsetTop + node.offsetHeight) {
88815                                     if (targetIndex === null || index2 < targetIndex) {
88816                                         targetIndex = index2;
88817                                     }
88818                                     return 'translateY(100%)';
88819                                 }
88820                                 return null;
88821                             });
88822                     })
88823                     .on('end', function(d, index) {
88824
88825                         if (!select(this).classed('dragging')) {
88826                             return;
88827                         }
88828
88829                         select(this)
88830                             .classed('dragging', false);
88831
88832                         selection.selectAll('li.member-row')
88833                             .style('transform', null);
88834
88835                         if (targetIndex !== null) {
88836                             // dragged to a new position, reorder
88837                             context.perform(
88838                                 actionMoveMember(d.relation.id, index, targetIndex),
88839                                 _t('operations.reorder_members.annotation')
88840                             );
88841                         }
88842                     })
88843                 );
88844
88845
88846
88847                 function bindTypeahead(d) {
88848                     var row = select(this);
88849                     var role = row.selectAll('input.member-role');
88850                     var origValue = role.property('value');
88851
88852                     function sort(value, data) {
88853                         var sameletter = [];
88854                         var other = [];
88855                         for (var i = 0; i < data.length; i++) {
88856                             if (data[i].value.substring(0, value.length) === value) {
88857                                 sameletter.push(data[i]);
88858                             } else {
88859                                 other.push(data[i]);
88860                             }
88861                         }
88862                         return sameletter.concat(other);
88863                     }
88864
88865                     role.call(uiCombobox(context, 'member-role')
88866                         .fetcher(function(role, callback) {
88867                             // The `geometry` param is used in the `taginfo.js` interface for
88868                             // filtering results, as a key into the `tag_members_fractions`
88869                             // object.  If we don't know the geometry because the member is
88870                             // not yet downloaded, it's ok to guess based on type.
88871                             var geometry;
88872                             if (d.member) {
88873                                 geometry = context.graph().geometry(d.member.id);
88874                             } else if (d.type === 'relation') {
88875                                 geometry = 'relation';
88876                             } else if (d.type === 'way') {
88877                                 geometry = 'line';
88878                             } else {
88879                                 geometry = 'point';
88880                             }
88881
88882                             var rtype = entity.tags.type;
88883                             taginfo.roles({
88884                                 debounce: true,
88885                                 rtype: rtype || '',
88886                                 geometry: geometry,
88887                                 query: role
88888                             }, function(err, data) {
88889                                 if (!err) callback(sort(role, data));
88890                             });
88891                         })
88892                         .on('cancel', function() {
88893                             role.property('value', origValue);
88894                         })
88895                     );
88896                 }
88897
88898
88899                 function unbind() {
88900                     var row = select(this);
88901
88902                     row.selectAll('input.member-role')
88903                         .call(uiCombobox.off, context);
88904                 }
88905             }
88906
88907             section.entityIDs = function(val) {
88908                 if (!arguments.length) return _entityIDs;
88909                 _entityIDs = val;
88910                 return section;
88911             };
88912
88913
88914             return section;
88915         }
88916
88917         function uiSectionRawMembershipEditor(context) {
88918
88919             var section = uiSection('raw-membership-editor', context)
88920                 .shouldDisplay(function() {
88921                     return _entityIDs && _entityIDs.length === 1;
88922                 })
88923                 .title(function() {
88924                     var entity = context.hasEntity(_entityIDs[0]);
88925                     if (!entity) return '';
88926
88927                     var parents = context.graph().parentRelations(entity);
88928                     var gt = parents.length > _maxMemberships ? '>' : '';
88929                     var count = gt + parents.slice(0, _maxMemberships).length;
88930                     return _t('inspector.title_count', { title: _t('inspector.relations'), count: count });
88931                 })
88932                 .disclosureContent(renderDisclosureContent);
88933
88934             var taginfo = services.taginfo;
88935             var nearbyCombo = uiCombobox(context, 'parent-relation')
88936                 .minItems(1)
88937                 .fetcher(fetchNearbyRelations)
88938                 .itemsMouseEnter(function(d) {
88939                     if (d.relation) utilHighlightEntities([d.relation.id], true, context);
88940                 })
88941                 .itemsMouseLeave(function(d) {
88942                     if (d.relation) utilHighlightEntities([d.relation.id], false, context);
88943                 });
88944             var _inChange = false;
88945             var _entityIDs = [];
88946             var _showBlank;
88947             var _maxMemberships = 1000;
88948
88949             function selectRelation(d) {
88950                 event.preventDefault();
88951
88952                 // remove the hover-highlight styling
88953                 utilHighlightEntities([d.relation.id], false, context);
88954
88955                 context.enter(modeSelect(context, [d.relation.id]));
88956             }
88957
88958             function zoomToRelation(d) {
88959                 event.preventDefault();
88960
88961                 var entity = context.entity(d.relation.id);
88962                 context.map().zoomToEase(entity);
88963
88964                 // highlight the relation in case it wasn't previously on-screen
88965                 utilHighlightEntities([d.relation.id], true, context);
88966             }
88967
88968
88969             function changeRole(d) {
88970                 if (d === 0) return;    // called on newrow (shoudn't happen)
88971                 if (_inChange) return;  // avoid accidental recursive call #5731
88972
88973                 var oldRole = d.member.role;
88974                 var newRole = context.cleanRelationRole(select(this).property('value'));
88975
88976                 if (oldRole !== newRole) {
88977                     _inChange = true;
88978                     context.perform(
88979                         actionChangeMember(d.relation.id, Object.assign({}, d.member, { role: newRole }), d.index),
88980                         _t('operations.change_role.annotation')
88981                     );
88982                 }
88983                 _inChange = false;
88984             }
88985
88986
88987             function addMembership(d, role) {
88988                 this.blur();           // avoid keeping focus on the button
88989                 _showBlank = false;
88990
88991                 var member = { id: _entityIDs[0], type: context.entity(_entityIDs[0]).type, role: role };
88992
88993                 if (d.relation) {
88994                     context.perform(
88995                         actionAddMember(d.relation.id, member),
88996                         _t('operations.add_member.annotation')
88997                     );
88998
88999                 } else {
89000                     var relation = osmRelation();
89001                     context.perform(
89002                         actionAddEntity(relation),
89003                         actionAddMember(relation.id, member),
89004                         _t('operations.add.annotation.relation')
89005                     );
89006
89007                     context.enter(modeSelect(context, [relation.id]).newFeature(true));
89008                 }
89009             }
89010
89011
89012             function deleteMembership(d) {
89013                 this.blur();           // avoid keeping focus on the button
89014                 if (d === 0) return;   // called on newrow (shoudn't happen)
89015
89016                 // remove the hover-highlight styling
89017                 utilHighlightEntities([d.relation.id], false, context);
89018
89019                 context.perform(
89020                     actionDeleteMember(d.relation.id, d.index),
89021                     _t('operations.delete_member.annotation')
89022                 );
89023             }
89024
89025
89026             function fetchNearbyRelations(q, callback) {
89027                 var newRelation = { relation: null, value: _t('inspector.new_relation') };
89028
89029                 var entityID = _entityIDs[0];
89030
89031                 var result = [];
89032
89033                 var graph = context.graph();
89034
89035                 function baseDisplayLabel(entity) {
89036                     var matched = _mainPresetIndex.match(entity, graph);
89037                     var presetName = (matched && matched.name()) || _t('inspector.relation');
89038                     var entityName = utilDisplayName(entity) || '';
89039
89040                     return presetName + ' ' + entityName;
89041                 }
89042
89043                 var explicitRelation = q && context.hasEntity(q.toLowerCase());
89044                 if (explicitRelation && explicitRelation.type === 'relation' && explicitRelation.id !== entityID) {
89045                     // loaded relation is specified explicitly, only show that
89046
89047                     result.push({
89048                         relation: explicitRelation,
89049                         value: baseDisplayLabel(explicitRelation) + ' ' + explicitRelation.id
89050                     });
89051                 } else {
89052
89053                     context.history().intersects(context.map().extent()).forEach(function(entity) {
89054                         if (entity.type !== 'relation' || entity.id === entityID) return;
89055
89056                         var value = baseDisplayLabel(entity);
89057                         if (q && (value + ' ' + entity.id).toLowerCase().indexOf(q.toLowerCase()) === -1) return;
89058
89059                         result.push({ relation: entity, value: value });
89060                     });
89061
89062                     result.sort(function(a, b) {
89063                         return osmRelation.creationOrder(a.relation, b.relation);
89064                     });
89065
89066                     // Dedupe identical names by appending relation id - see #2891
89067                     var dupeGroups = Object.values(utilArrayGroupBy(result, 'value'))
89068                         .filter(function(v) { return v.length > 1; });
89069
89070                     dupeGroups.forEach(function(group) {
89071                         group.forEach(function(obj) {
89072                             obj.value += ' ' + obj.relation.id;
89073                         });
89074                     });
89075                 }
89076
89077                 result.forEach(function(obj) {
89078                     obj.title = obj.value;
89079                 });
89080
89081                 result.unshift(newRelation);
89082                 callback(result);
89083             }
89084
89085             function renderDisclosureContent(selection) {
89086
89087                 var entityID = _entityIDs[0];
89088
89089                 var entity = context.entity(entityID);
89090                 var parents = context.graph().parentRelations(entity);
89091
89092                 var memberships = [];
89093
89094                 parents.slice(0, _maxMemberships).forEach(function(relation) {
89095                     relation.members.forEach(function(member, index) {
89096                         if (member.id === entity.id) {
89097                             memberships.push({
89098                                 relation: relation,
89099                                 member: member,
89100                                 index: index,
89101                                 domId: utilUniqueDomId(entityID + '-membership-' + relation.id + '-' + index)
89102                             });
89103                         }
89104                     });
89105                 });
89106
89107                 var list = selection.selectAll('.member-list')
89108                     .data([0]);
89109
89110                 list = list.enter()
89111                     .append('ul')
89112                     .attr('class', 'member-list')
89113                     .merge(list);
89114
89115
89116                 var items = list.selectAll('li.member-row-normal')
89117                     .data(memberships, function(d) {
89118                         return osmEntity.key(d.relation) + ',' + d.index;
89119                     });
89120
89121                 items.exit()
89122                     .each(unbind)
89123                     .remove();
89124
89125                 // Enter
89126                 var itemsEnter = items.enter()
89127                     .append('li')
89128                     .attr('class', 'member-row member-row-normal form-field');
89129
89130                 // highlight the relation in the map while hovering on the list item
89131                 itemsEnter.on('mouseover', function(d) {
89132                         utilHighlightEntities([d.relation.id], true, context);
89133                     })
89134                     .on('mouseout', function(d) {
89135                         utilHighlightEntities([d.relation.id], false, context);
89136                     });
89137
89138                 var labelEnter = itemsEnter
89139                     .append('label')
89140                     .attr('class', 'field-label')
89141                     .attr('for', function(d) {
89142                         return d.domId;
89143                     });
89144
89145                 var labelLink = labelEnter
89146                     .append('span')
89147                     .attr('class', 'label-text')
89148                     .append('a')
89149                     .attr('href', '#')
89150                     .on('click', selectRelation);
89151
89152                 labelLink
89153                     .append('span')
89154                     .attr('class', 'member-entity-type')
89155                     .text(function(d) {
89156                         var matched = _mainPresetIndex.match(d.relation, context.graph());
89157                         return (matched && matched.name()) || _t('inspector.relation');
89158                     });
89159
89160                 labelLink
89161                     .append('span')
89162                     .attr('class', 'member-entity-name')
89163                     .text(function(d) { return utilDisplayName(d.relation); });
89164
89165                 labelEnter
89166                     .append('button')
89167                     .attr('tabindex', -1)
89168                     .attr('class', 'remove member-delete')
89169                     .call(svgIcon('#iD-operation-delete'))
89170                     .on('click', deleteMembership);
89171
89172                 labelEnter
89173                     .append('button')
89174                     .attr('class', 'member-zoom')
89175                     .attr('title', _t('icons.zoom_to'))
89176                     .call(svgIcon('#iD-icon-framed-dot', 'monochrome'))
89177                     .on('click', zoomToRelation);
89178
89179                 var wrapEnter = itemsEnter
89180                     .append('div')
89181                     .attr('class', 'form-field-input-wrap form-field-input-member');
89182
89183                 wrapEnter
89184                     .append('input')
89185                     .attr('class', 'member-role')
89186                     .attr('id', function(d) {
89187                         return d.domId;
89188                     })
89189                     .property('type', 'text')
89190                     .attr('placeholder', _t('inspector.role'))
89191                     .call(utilNoAuto)
89192                     .property('value', function(d) { return d.member.role; })
89193                     .on('blur', changeRole)
89194                     .on('change', changeRole);
89195
89196                 if (taginfo) {
89197                     wrapEnter.each(bindTypeahead);
89198                 }
89199
89200
89201                 var newMembership = list.selectAll('.member-row-new')
89202                     .data(_showBlank ? [0] : []);
89203
89204                 // Exit
89205                 newMembership.exit()
89206                     .remove();
89207
89208                 // Enter
89209                 var newMembershipEnter = newMembership.enter()
89210                     .append('li')
89211                     .attr('class', 'member-row member-row-new form-field');
89212
89213                 var newLabelEnter = newMembershipEnter
89214                     .append('label')
89215                     .attr('class', 'field-label');
89216
89217                 newLabelEnter
89218                     .append('input')
89219                     .attr('placeholder', _t('inspector.choose_relation'))
89220                     .attr('type', 'text')
89221                     .attr('class', 'member-entity-input')
89222                     .call(utilNoAuto);
89223
89224                 newLabelEnter
89225                     .append('button')
89226                     .attr('tabindex', -1)
89227                     .attr('class', 'remove member-delete')
89228                     .call(svgIcon('#iD-operation-delete'))
89229                     .on('click', function() {
89230                         list.selectAll('.member-row-new')
89231                             .remove();
89232                     });
89233
89234                 var newWrapEnter = newMembershipEnter
89235                     .append('div')
89236                     .attr('class', 'form-field-input-wrap form-field-input-member');
89237
89238                 newWrapEnter
89239                     .append('input')
89240                     .attr('class', 'member-role')
89241                     .property('type', 'text')
89242                     .attr('placeholder', _t('inspector.role'))
89243                     .call(utilNoAuto);
89244
89245                 // Update
89246                 newMembership = newMembership
89247                     .merge(newMembershipEnter);
89248
89249                 newMembership.selectAll('.member-entity-input')
89250                     .on('blur', cancelEntity)   // if it wasn't accepted normally, cancel it
89251                     .call(nearbyCombo
89252                         .on('accept', acceptEntity)
89253                         .on('cancel', cancelEntity)
89254                     );
89255
89256
89257                 // Container for the Add button
89258                 var addRow = selection.selectAll('.add-row')
89259                     .data([0]);
89260
89261                 // enter
89262                 var addRowEnter = addRow.enter()
89263                     .append('div')
89264                     .attr('class', 'add-row');
89265
89266                 var addRelationButton = addRowEnter
89267                     .append('button')
89268                     .attr('class', 'add-relation');
89269
89270                 addRelationButton
89271                     .call(svgIcon('#iD-icon-plus', 'light'));
89272                 addRelationButton
89273                     .call(uiTooltip().title(_t('inspector.add_to_relation')).placement(_mainLocalizer.textDirection() === 'ltr' ? 'right' : 'left'));
89274
89275                 addRowEnter
89276                     .append('div')
89277                     .attr('class', 'space-value');   // preserve space
89278
89279                 addRowEnter
89280                     .append('div')
89281                     .attr('class', 'space-buttons');  // preserve space
89282
89283                 // update
89284                 addRow = addRow
89285                     .merge(addRowEnter);
89286
89287                 addRow.select('.add-relation')
89288                     .on('click', function() {
89289                         _showBlank = true;
89290                         section.reRender();
89291                         list.selectAll('.member-entity-input').node().focus();
89292                     });
89293
89294
89295                 function acceptEntity(d) {
89296                     if (!d) {
89297                         cancelEntity();
89298                         return;
89299                     }
89300                     // remove hover-higlighting
89301                     if (d.relation) utilHighlightEntities([d.relation.id], false, context);
89302
89303                     var role = context.cleanRelationRole(list.selectAll('.member-row-new .member-role').property('value'));
89304                     addMembership(d, role);
89305                 }
89306
89307
89308                 function cancelEntity() {
89309                     var input = newMembership.selectAll('.member-entity-input');
89310                     input.property('value', '');
89311
89312                     // remove hover-higlighting
89313                     context.surface().selectAll('.highlighted')
89314                         .classed('highlighted', false);
89315                 }
89316
89317
89318                 function bindTypeahead(d) {
89319                     var row = select(this);
89320                     var role = row.selectAll('input.member-role');
89321                     var origValue = role.property('value');
89322
89323                     function sort(value, data) {
89324                         var sameletter = [];
89325                         var other = [];
89326                         for (var i = 0; i < data.length; i++) {
89327                             if (data[i].value.substring(0, value.length) === value) {
89328                                 sameletter.push(data[i]);
89329                             } else {
89330                                 other.push(data[i]);
89331                             }
89332                         }
89333                         return sameletter.concat(other);
89334                     }
89335
89336                     role.call(uiCombobox(context, 'member-role')
89337                         .fetcher(function(role, callback) {
89338                             var rtype = d.relation.tags.type;
89339                             taginfo.roles({
89340                                 debounce: true,
89341                                 rtype: rtype || '',
89342                                 geometry: context.graph().geometry(entityID),
89343                                 query: role
89344                             }, function(err, data) {
89345                                 if (!err) callback(sort(role, data));
89346                             });
89347                         })
89348                         .on('cancel', function() {
89349                             role.property('value', origValue);
89350                         })
89351                     );
89352                 }
89353
89354
89355                 function unbind() {
89356                     var row = select(this);
89357
89358                     row.selectAll('input.member-role')
89359                         .call(uiCombobox.off, context);
89360                 }
89361             }
89362
89363
89364             section.entityIDs = function(val) {
89365                 if (!arguments.length) return _entityIDs;
89366                 _entityIDs = val;
89367                 _showBlank = false;
89368                 return section;
89369             };
89370
89371
89372             return section;
89373         }
89374
89375         function uiSectionSelectionList(context) {
89376
89377             var _selectedIDs = [];
89378
89379             var section = uiSection('selected-features', context)
89380                 .shouldDisplay(function() {
89381                     return _selectedIDs.length > 1;
89382                 })
89383                 .title(function() {
89384                     return _t('inspector.title_count', { title: _t('inspector.features'), count: _selectedIDs.length });
89385                 })
89386                 .disclosureContent(renderDisclosureContent);
89387
89388             context.history()
89389                 .on('change.selectionList', function(difference) {
89390                     if (difference) {
89391                         section.reRender();
89392                     }
89393                 });
89394
89395             section.entityIDs = function(val) {
89396                 if (!arguments.length) return _selectedIDs;
89397                 _selectedIDs = val;
89398                 return section;
89399             };
89400
89401             function selectEntity(entity) {
89402                 context.enter(modeSelect(context, [entity.id]));
89403             }
89404
89405             function deselectEntity(entity) {
89406                 event.stopPropagation();
89407
89408                 var selectedIDs = _selectedIDs.slice();
89409                 var index = selectedIDs.indexOf(entity.id);
89410                 if (index > -1) {
89411                     selectedIDs.splice(index, 1);
89412                     context.enter(modeSelect(context, selectedIDs));
89413                 }
89414             }
89415
89416             function renderDisclosureContent(selection) {
89417
89418                 var list = selection.selectAll('.feature-list')
89419                     .data([0]);
89420
89421                 list = list.enter()
89422                     .append('div')
89423                     .attr('class', 'feature-list')
89424                     .merge(list);
89425
89426                 var entities = _selectedIDs
89427                     .map(function(id) { return context.hasEntity(id); })
89428                     .filter(Boolean);
89429
89430                 var items = list.selectAll('.feature-list-item')
89431                     .data(entities, osmEntity.key);
89432
89433                 items.exit()
89434                     .remove();
89435
89436                 // Enter
89437                 var enter = items.enter()
89438                     .append('div')
89439                     .attr('class', 'feature-list-item')
89440                     .on('click', selectEntity);
89441
89442                 enter
89443                     .each(function(d) {
89444                         select(this).on('mouseover', function() {
89445                             utilHighlightEntities([d.id], true, context);
89446                         });
89447                         select(this).on('mouseout', function() {
89448                             utilHighlightEntities([d.id], false, context);
89449                         });
89450                     });
89451
89452                 var label = enter
89453                     .append('button')
89454                     .attr('class', 'label');
89455
89456                 enter
89457                     .append('button')
89458                     .attr('class', 'close')
89459                     .attr('title', _t('icons.deselect'))
89460                     .on('click', deselectEntity)
89461                     .call(svgIcon('#iD-icon-close'));
89462
89463                 label
89464                     .append('span')
89465                     .attr('class', 'entity-geom-icon')
89466                     .call(svgIcon('', 'pre-text'));
89467
89468                 label
89469                     .append('span')
89470                     .attr('class', 'entity-type');
89471
89472                 label
89473                     .append('span')
89474                     .attr('class', 'entity-name');
89475
89476                 // Update
89477                 items = items.merge(enter);
89478
89479                 items.selectAll('.entity-geom-icon use')
89480                     .attr('href', function() {
89481                         var entity = this.parentNode.parentNode.__data__;
89482                         return '#iD-icon-' + entity.geometry(context.graph());
89483                     });
89484
89485                 items.selectAll('.entity-type')
89486                     .text(function(entity) { return _mainPresetIndex.match(entity, context.graph()).name(); });
89487
89488                 items.selectAll('.entity-name')
89489                     .text(function(d) {
89490                         // fetch latest entity
89491                         var entity = context.entity(d.id);
89492                         return utilDisplayName(entity);
89493                     });
89494             }
89495
89496             return section;
89497         }
89498
89499         function uiEntityEditor(context) {
89500             var dispatch$1 = dispatch('choose');
89501             var _state = 'select';
89502             var _coalesceChanges = false;
89503             var _modified = false;
89504             var _base;
89505             var _entityIDs;
89506             var _activePresets = [];
89507             var _newFeature;
89508
89509             var _sections;
89510
89511             function entityEditor(selection) {
89512
89513                 var combinedTags = utilCombinedTags(_entityIDs, context.graph());
89514
89515                 // Header
89516                 var header = selection.selectAll('.header')
89517                     .data([0]);
89518
89519                 // Enter
89520                 var headerEnter = header.enter()
89521                     .append('div')
89522                     .attr('class', 'header fillL cf');
89523
89524                 headerEnter
89525                     .append('button')
89526                     .attr('class', 'preset-reset preset-choose')
89527                     .call(svgIcon((_mainLocalizer.textDirection() === 'rtl') ? '#iD-icon-forward' : '#iD-icon-backward'));
89528
89529                 headerEnter
89530                     .append('button')
89531                     .attr('class', 'close')
89532                     .on('click', function() { context.enter(modeBrowse(context)); })
89533                     .call(svgIcon(_modified ? '#iD-icon-apply' : '#iD-icon-close'));
89534
89535                 headerEnter
89536                     .append('h3');
89537
89538                 // Update
89539                 header = header
89540                     .merge(headerEnter);
89541
89542                 header.selectAll('h3')
89543                     .text(_entityIDs.length === 1 ? _t('inspector.edit') : _t('inspector.edit_features'));
89544
89545                 header.selectAll('.preset-reset')
89546                     .on('click', function() {
89547                         dispatch$1.call('choose', this, _activePresets);
89548                     });
89549
89550                 // Body
89551                 var body = selection.selectAll('.inspector-body')
89552                     .data([0]);
89553
89554                 // Enter
89555                 var bodyEnter = body.enter()
89556                     .append('div')
89557                     .attr('class', 'entity-editor inspector-body sep-top');
89558
89559                 // Update
89560                 body = body
89561                     .merge(bodyEnter);
89562
89563                 if (!_sections) {
89564                     _sections = [
89565                         uiSectionSelectionList(context),
89566                         uiSectionFeatureType(context).on('choose', function(presets) {
89567                             dispatch$1.call('choose', this, presets);
89568                         }),
89569                         uiSectionEntityIssues(context),
89570                         uiSectionPresetFields(context).on('change', changeTags).on('revert', revertTags),
89571                         uiSectionRawTagEditor('raw-tag-editor', context).on('change', changeTags),
89572                         uiSectionRawMemberEditor(context),
89573                         uiSectionRawMembershipEditor(context)
89574                     ];
89575                 }
89576
89577                 _sections.forEach(function(section) {
89578                     if (section.entityIDs) {
89579                         section.entityIDs(_entityIDs);
89580                     }
89581                     if (section.presets) {
89582                         section.presets(_activePresets);
89583                     }
89584                     if (section.tags) {
89585                         section.tags(combinedTags);
89586                     }
89587                     if (section.state) {
89588                         section.state(_state);
89589                     }
89590                     body.call(section.render);
89591                 });
89592
89593                 body
89594                     .selectAll('.key-trap-wrap')
89595                     .data([0])
89596                     .enter()
89597                     .append('div')
89598                     .attr('class', 'key-trap-wrap')
89599                     .append('input')
89600                     .attr('type', 'text')
89601                     .attr('class', 'key-trap')
89602                     .on('keydown.key-trap', function() {
89603                         // On tabbing, send focus back to the first field on the inspector-body
89604                         // (probably the `name` field) #4159
89605                         if (event.keyCode === 9 && !event.shiftKey) {
89606                             event.preventDefault();
89607                             body.select('input').node().focus();
89608                         }
89609                     });
89610
89611                 context.history()
89612                     .on('change.entity-editor', historyChanged);
89613
89614                 function historyChanged(difference) {
89615                     if (selection.selectAll('.entity-editor').empty()) return;
89616                     if (_state === 'hide') return;
89617                     var significant = !difference ||
89618                             difference.didChange.properties ||
89619                             difference.didChange.addition ||
89620                             difference.didChange.deletion;
89621                     if (!significant) return;
89622
89623                     _entityIDs = _entityIDs.filter(context.hasEntity);
89624                     if (!_entityIDs.length) return;
89625
89626                     var priorActivePreset = _activePresets.length === 1 && _activePresets[0];
89627
89628                     loadActivePresets();
89629
89630                     var graph = context.graph();
89631                     entityEditor.modified(_base !== graph);
89632                     entityEditor(selection);
89633
89634                     if (priorActivePreset && _activePresets.length === 1 && priorActivePreset !== _activePresets[0]) {
89635                         // flash the button to indicate the preset changed
89636                         context.container().selectAll('.entity-editor button.preset-reset .label')
89637                             .style('background-color', '#fff')
89638                             .transition()
89639                             .duration(750)
89640                             .style('background-color', null);
89641                     }
89642                 }
89643             }
89644
89645
89646             // Tag changes that fire on input can all get coalesced into a single
89647             // history operation when the user leaves the field.  #2342
89648             // Use explicit entityIDs in case the selection changes before the event is fired.
89649             function changeTags(entityIDs, changed, onInput) {
89650
89651                 var actions = [];
89652                 for (var i in entityIDs) {
89653                     var entityID = entityIDs[i];
89654                     var entity = context.entity(entityID);
89655
89656                     var tags = Object.assign({}, entity.tags);   // shallow copy
89657
89658                     for (var k in changed) {
89659                         if (!k) continue;
89660                         var v = changed[k];
89661                         if (v !== undefined || tags.hasOwnProperty(k)) {
89662                             tags[k] = v;
89663                         }
89664                     }
89665
89666                     if (!onInput) {
89667                         tags = utilCleanTags(tags);
89668                     }
89669
89670                     if (!fastDeepEqual(entity.tags, tags)) {
89671                         actions.push(actionChangeTags(entityID, tags));
89672                     }
89673                 }
89674
89675                 if (actions.length) {
89676                     var combinedAction = function(graph) {
89677                         actions.forEach(function(action) {
89678                             graph = action(graph);
89679                         });
89680                         return graph;
89681                     };
89682
89683                     var annotation = _t('operations.change_tags.annotation');
89684
89685                     if (_coalesceChanges) {
89686                         context.overwrite(combinedAction, annotation);
89687                     } else {
89688                         context.perform(combinedAction, annotation);
89689                         _coalesceChanges = !!onInput;
89690                     }
89691                 }
89692
89693                 // if leaving field (blur event), rerun validation
89694                 if (!onInput) {
89695                     context.validator().validate();
89696                 }
89697             }
89698
89699             function revertTags(keys) {
89700
89701                 var actions = [];
89702                 for (var i in _entityIDs) {
89703                     var entityID = _entityIDs[i];
89704
89705                     var original = context.graph().base().entities[entityID];
89706                     var changed = {};
89707                     for (var j in keys) {
89708                         var key = keys[j];
89709                         changed[key] = original ? original.tags[key] : undefined;
89710                     }
89711
89712                     var entity = context.entity(entityID);
89713                     var tags = Object.assign({}, entity.tags);   // shallow copy
89714
89715                     for (var k in changed) {
89716                         if (!k) continue;
89717                         var v = changed[k];
89718                         if (v !== undefined || tags.hasOwnProperty(k)) {
89719                             tags[k] = v;
89720                         }
89721                     }
89722
89723
89724                     tags = utilCleanTags(tags);
89725
89726                     if (!fastDeepEqual(entity.tags, tags)) {
89727                         actions.push(actionChangeTags(entityID, tags));
89728                     }
89729
89730                 }
89731
89732                 if (actions.length) {
89733                     var combinedAction = function(graph) {
89734                         actions.forEach(function(action) {
89735                             graph = action(graph);
89736                         });
89737                         return graph;
89738                     };
89739
89740                     var annotation = _t('operations.change_tags.annotation');
89741
89742                     if (_coalesceChanges) {
89743                         context.overwrite(combinedAction, annotation);
89744                     } else {
89745                         context.perform(combinedAction, annotation);
89746                         _coalesceChanges = false;
89747                     }
89748                 }
89749
89750                 context.validator().validate();
89751             }
89752
89753
89754             entityEditor.modified = function(val) {
89755                 if (!arguments.length) return _modified;
89756                 _modified = val;
89757                 return entityEditor;
89758             };
89759
89760
89761             entityEditor.state = function(val) {
89762                 if (!arguments.length) return _state;
89763                 _state = val;
89764                 return entityEditor;
89765             };
89766
89767
89768             entityEditor.entityIDs = function(val) {
89769                 if (!arguments.length) return _entityIDs;
89770                 if (val && _entityIDs && utilArrayIdentical(_entityIDs, val)) return entityEditor;  // exit early if no change
89771
89772                 _entityIDs = val;
89773                 _base = context.graph();
89774                 _coalesceChanges = false;
89775
89776                 loadActivePresets();
89777
89778                 return entityEditor
89779                     .modified(false);
89780             };
89781
89782
89783             entityEditor.newFeature = function(val) {
89784                 if (!arguments.length) return _newFeature;
89785                 _newFeature = val;
89786                 return entityEditor;
89787             };
89788
89789
89790             function loadActivePresets() {
89791
89792                 var graph = context.graph();
89793
89794                 var counts = {};
89795
89796                 for (var i in _entityIDs) {
89797                     var entity = graph.hasEntity(_entityIDs[i]);
89798                     if (!entity) return;
89799
89800                     var match = _mainPresetIndex.match(entity, graph);
89801
89802                     if (!counts[match.id]) counts[match.id] = 0;
89803                     counts[match.id] += 1;
89804                 }
89805
89806                 var matches = Object.keys(counts).sort(function(p1, p2) {
89807                     return counts[p2] - counts[p1];
89808                 }).map(function(pID) {
89809                     return _mainPresetIndex.item(pID);
89810                 });
89811
89812                 // A "weak" preset doesn't set any tags. (e.g. "Address")
89813                 var weakPreset = _activePresets.length === 1 &&
89814                     Object.keys(_activePresets[0].addTags || {}).length === 0;
89815                 // Don't replace a weak preset with a fallback preset (e.g. "Point")
89816                 if (weakPreset && matches.length === 1 && matches[0].isFallback()) return;
89817
89818                 entityEditor.presets(matches);
89819             }
89820
89821             entityEditor.presets = function(val) {
89822                 if (!arguments.length) return _activePresets;
89823
89824                 // don't reload the same preset
89825                 if (!utilArrayIdentical(val, _activePresets)) {
89826                     _activePresets = val;
89827                 }
89828                 return entityEditor;
89829             };
89830
89831             return utilRebind(entityEditor, dispatch$1, 'on');
89832         }
89833
89834         function uiPresetList(context) {
89835             var dispatch$1 = dispatch('cancel', 'choose');
89836             var _entityIDs;
89837             var _currentPresets;
89838             var _autofocus = false;
89839
89840
89841             function presetList(selection) {
89842                 if (!_entityIDs) return;
89843
89844                 var presets = _mainPresetIndex.matchAllGeometry(entityGeometries());
89845
89846                 selection.html('');
89847
89848                 var messagewrap = selection
89849                     .append('div')
89850                     .attr('class', 'header fillL');
89851
89852                 var message = messagewrap
89853                     .append('h3')
89854                     .text(_t('inspector.choose'));
89855
89856                 messagewrap
89857                     .append('button')
89858                     .attr('class', 'preset-choose')
89859                     .on('click', function() { dispatch$1.call('cancel', this); })
89860                     .call(svgIcon((_mainLocalizer.textDirection() === 'rtl') ? '#iD-icon-backward' : '#iD-icon-forward'));
89861
89862                 function initialKeydown() {
89863                     // hack to let delete shortcut work when search is autofocused
89864                     if (search.property('value').length === 0 &&
89865                         (event.keyCode === utilKeybinding.keyCodes['⌫'] ||
89866                          event.keyCode === utilKeybinding.keyCodes['⌦'])) {
89867                         event.preventDefault();
89868                         event.stopPropagation();
89869                         operationDelete(context, _entityIDs)();
89870
89871                     // hack to let undo work when search is autofocused
89872                     } else if (search.property('value').length === 0 &&
89873                         (event.ctrlKey || event.metaKey) &&
89874                         event.keyCode === utilKeybinding.keyCodes.z) {
89875                         event.preventDefault();
89876                         event.stopPropagation();
89877                         context.undo();
89878                     } else if (!event.ctrlKey && !event.metaKey) {
89879                         // don't check for delete/undo hack on future keydown events
89880                         select(this).on('keydown', keydown);
89881                         keydown.call(this);
89882                     }
89883                 }
89884
89885                 function keydown() {
89886                     // down arrow
89887                     if (event.keyCode === utilKeybinding.keyCodes['↓'] &&
89888                         // if insertion point is at the end of the string
89889                         search.node().selectionStart === search.property('value').length) {
89890                         event.preventDefault();
89891                         event.stopPropagation();
89892                         // move focus to the first item in the preset list
89893                         var buttons = list.selectAll('.preset-list-button');
89894                         if (!buttons.empty()) buttons.nodes()[0].focus();
89895                     }
89896                 }
89897
89898                 function keypress() {
89899                     // enter
89900                     var value = search.property('value');
89901                     if (event.keyCode === 13 && value.length) {
89902                         list.selectAll('.preset-list-item:first-child')
89903                             .each(function(d) { d.choose.call(this); });
89904                     }
89905                 }
89906
89907                 function inputevent() {
89908                     var value = search.property('value');
89909                     list.classed('filtered', value.length);
89910                     var extent = combinedEntityExtent();
89911                     var results, messageText;
89912                     if (value.length && extent) {
89913                         var center = extent.center();
89914                         var countryCode = iso1A2Code(center);
89915
89916                         results = presets.search(value, entityGeometries()[0], countryCode && countryCode.toLowerCase());
89917                         messageText = _t('inspector.results', {
89918                             n: results.collection.length,
89919                             search: value
89920                         });
89921                     } else {
89922                         results = _mainPresetIndex.defaults(entityGeometries()[0], 36, !context.inIntro());
89923                         messageText = _t('inspector.choose');
89924                     }
89925                     list.call(drawList, results);
89926                     message.text(messageText);
89927                 }
89928
89929                 var searchWrap = selection
89930                     .append('div')
89931                     .attr('class', 'search-header');
89932
89933                 var search = searchWrap
89934                     .append('input')
89935                     .attr('class', 'preset-search-input')
89936                     .attr('placeholder', _t('inspector.search'))
89937                     .attr('type', 'search')
89938                     .call(utilNoAuto)
89939                     .on('keydown', initialKeydown)
89940                     .on('keypress', keypress)
89941                     .on('input', inputevent);
89942
89943                 searchWrap
89944                     .call(svgIcon('#iD-icon-search', 'pre-text'));
89945
89946                 if (_autofocus) {
89947                     search.node().focus();
89948                 }
89949
89950                 var listWrap = selection
89951                     .append('div')
89952                     .attr('class', 'inspector-body');
89953
89954                 var list = listWrap
89955                     .append('div')
89956                     .attr('class', 'preset-list')
89957                     .call(drawList, _mainPresetIndex.defaults(entityGeometries()[0], 36, !context.inIntro()));
89958
89959                 context.features().on('change.preset-list', updateForFeatureHiddenState);
89960             }
89961
89962
89963             function drawList(list, presets) {
89964                 presets = presets.matchAllGeometry(entityGeometries());
89965                 var collection = presets.collection.reduce(function(collection, preset) {
89966                     if (!preset) return collection;
89967
89968                     if (preset.members) {
89969                         if (preset.members.collection.filter(function(preset) {
89970                             return preset.addable();
89971                         }).length > 1) {
89972                             collection.push(CategoryItem(preset));
89973                         }
89974                     } else if (preset.addable()) {
89975                         collection.push(PresetItem(preset));
89976                     }
89977                     return collection;
89978                 }, []);
89979
89980                 var items = list.selectAll('.preset-list-item')
89981                     .data(collection, function(d) { return d.preset.id; });
89982
89983                 items.order();
89984
89985                 items.exit()
89986                     .remove();
89987
89988                 items.enter()
89989                     .append('div')
89990                     .attr('class', function(item) { return 'preset-list-item preset-' + item.preset.id.replace('/', '-'); })
89991                     .classed('current', function(item) { return _currentPresets.indexOf(item.preset) !== -1; })
89992                     .each(function(item) { select(this).call(item); })
89993                     .style('opacity', 0)
89994                     .transition()
89995                     .style('opacity', 1);
89996
89997                 updateForFeatureHiddenState();
89998             }
89999
90000             function itemKeydown(){
90001                 // the actively focused item
90002                 var item = select(this.closest('.preset-list-item'));
90003                 var parentItem = select(item.node().parentNode.closest('.preset-list-item'));
90004
90005                 // arrow down, move focus to the next, lower item
90006                 if (event.keyCode === utilKeybinding.keyCodes['↓']) {
90007                     event.preventDefault();
90008                     event.stopPropagation();
90009                     // the next item in the list at the same level
90010                     var nextItem = select(item.node().nextElementSibling);
90011                     // if there is no next item in this list
90012                     if (nextItem.empty()) {
90013                         // if there is a parent item
90014                         if (!parentItem.empty()) {
90015                             // the item is the last item of a sublist,
90016                             // select the next item at the parent level
90017                             nextItem = select(parentItem.node().nextElementSibling);
90018                         }
90019                     // if the focused item is expanded
90020                     } else if (select(this).classed('expanded')) {
90021                         // select the first subitem instead
90022                         nextItem = item.select('.subgrid .preset-list-item:first-child');
90023                     }
90024                     if (!nextItem.empty()) {
90025                         // focus on the next item
90026                         nextItem.select('.preset-list-button').node().focus();
90027                     }
90028
90029                 // arrow up, move focus to the previous, higher item
90030                 } else if (event.keyCode === utilKeybinding.keyCodes['↑']) {
90031                     event.preventDefault();
90032                     event.stopPropagation();
90033                     // the previous item in the list at the same level
90034                     var previousItem = select(item.node().previousElementSibling);
90035
90036                     // if there is no previous item in this list
90037                     if (previousItem.empty()) {
90038                         // if there is a parent item
90039                         if (!parentItem.empty()) {
90040                             // the item is the first subitem of a sublist select the parent item
90041                             previousItem = parentItem;
90042                         }
90043                     // if the previous item is expanded
90044                     } else if (previousItem.select('.preset-list-button').classed('expanded')) {
90045                         // select the last subitem of the sublist of the previous item
90046                         previousItem = previousItem.select('.subgrid .preset-list-item:last-child');
90047                     }
90048
90049                     if (!previousItem.empty()) {
90050                         // focus on the previous item
90051                         previousItem.select('.preset-list-button').node().focus();
90052                     } else {
90053                         // the focus is at the top of the list, move focus back to the search field
90054                         var search = select(this.closest('.preset-list-pane')).select('.preset-search-input');
90055                         search.node().focus();
90056                     }
90057
90058                 // arrow left, move focus to the parent item if there is one
90059                 } else if (event.keyCode === utilKeybinding.keyCodes[(_mainLocalizer.textDirection() === 'rtl') ? '→' : '←']) {
90060                     event.preventDefault();
90061                     event.stopPropagation();
90062                     // if there is a parent item, focus on the parent item
90063                     if (!parentItem.empty()) {
90064                         parentItem.select('.preset-list-button').node().focus();
90065                     }
90066
90067                 // arrow right, choose this item
90068                 } else if (event.keyCode === utilKeybinding.keyCodes[(_mainLocalizer.textDirection() === 'rtl') ? '←' : '→']) {
90069                     event.preventDefault();
90070                     event.stopPropagation();
90071                     item.datum().choose.call(select(this).node());
90072                 }
90073             }
90074
90075
90076             function CategoryItem(preset) {
90077                 var box, sublist, shown = false;
90078
90079                 function item(selection) {
90080                     var wrap = selection.append('div')
90081                         .attr('class', 'preset-list-button-wrap category');
90082
90083                     function click() {
90084                         var isExpanded = select(this).classed('expanded');
90085                         var iconName = isExpanded ?
90086                             (_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward') : '#iD-icon-down';
90087                         select(this)
90088                             .classed('expanded', !isExpanded);
90089                         select(this).selectAll('div.label-inner svg.icon use')
90090                             .attr('href', iconName);
90091                         item.choose();
90092                     }
90093
90094                     var geometries = entityGeometries();
90095
90096                     var button = wrap
90097                         .append('button')
90098                         .attr('class', 'preset-list-button')
90099                         .classed('expanded', false)
90100                         .call(uiPresetIcon()
90101                             .geometry(geometries.length === 1 && geometries[0])
90102                             .preset(preset))
90103                         .on('click', click)
90104                         .on('keydown', function() {
90105                             // right arrow, expand the focused item
90106                             if (event.keyCode === utilKeybinding.keyCodes[(_mainLocalizer.textDirection() === 'rtl') ? '←' : '→']) {
90107                                 event.preventDefault();
90108                                 event.stopPropagation();
90109                                 // if the item isn't expanded
90110                                 if (!select(this).classed('expanded')) {
90111                                     // toggle expansion (expand the item)
90112                                     click.call(this);
90113                                 }
90114                             // left arrow, collapse the focused item
90115                             } else if (event.keyCode === utilKeybinding.keyCodes[(_mainLocalizer.textDirection() === 'rtl') ? '→' : '←']) {
90116                                 event.preventDefault();
90117                                 event.stopPropagation();
90118                                 // if the item is expanded
90119                                 if (select(this).classed('expanded')) {
90120                                     // toggle expansion (collapse the item)
90121                                     click.call(this);
90122                                 }
90123                             } else {
90124                                 itemKeydown.call(this);
90125                             }
90126                         });
90127
90128                     var label = button
90129                         .append('div')
90130                         .attr('class', 'label')
90131                         .append('div')
90132                         .attr('class', 'label-inner');
90133
90134                     label
90135                         .append('div')
90136                         .attr('class', 'namepart')
90137                         .call(svgIcon((_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward'), 'inline'))
90138                         .append('span')
90139                         .html(function() { return preset.name() + '&hellip;'; });
90140
90141                     box = selection.append('div')
90142                         .attr('class', 'subgrid')
90143                         .style('max-height', '0px')
90144                         .style('opacity', 0);
90145
90146                     box.append('div')
90147                         .attr('class', 'arrow');
90148
90149                     sublist = box.append('div')
90150                         .attr('class', 'preset-list fillL3');
90151                 }
90152
90153
90154                 item.choose = function() {
90155                     if (!box || !sublist) return;
90156
90157                     if (shown) {
90158                         shown = false;
90159                         box.transition()
90160                             .duration(200)
90161                             .style('opacity', '0')
90162                             .style('max-height', '0px')
90163                             .style('padding-bottom', '0px');
90164                     } else {
90165                         shown = true;
90166                         var members = preset.members.matchAllGeometry(entityGeometries());
90167                         sublist.call(drawList, members);
90168                         box.transition()
90169                             .duration(200)
90170                             .style('opacity', '1')
90171                             .style('max-height', 200 + members.collection.length * 190 + 'px')
90172                             .style('padding-bottom', '10px');
90173                     }
90174                 };
90175
90176                 item.preset = preset;
90177                 return item;
90178             }
90179
90180
90181             function PresetItem(preset) {
90182                 function item(selection) {
90183                     var wrap = selection.append('div')
90184                         .attr('class', 'preset-list-button-wrap');
90185
90186                     var geometries = entityGeometries();
90187
90188                     var button = wrap.append('button')
90189                         .attr('class', 'preset-list-button')
90190                         .call(uiPresetIcon()
90191                             .geometry(geometries.length === 1 && geometries[0])
90192                             .preset(preset))
90193                         .on('click', item.choose)
90194                         .on('keydown', itemKeydown);
90195
90196                     var label = button
90197                         .append('div')
90198                         .attr('class', 'label')
90199                         .append('div')
90200                         .attr('class', 'label-inner');
90201
90202                     // NOTE: split/join on en-dash, not a hypen (to avoid conflict with fr - nl names in Brussels etc)
90203                     label.selectAll('.namepart')
90204                         .data(preset.name().split(' – '))
90205                         .enter()
90206                         .append('div')
90207                         .attr('class', 'namepart')
90208                         .text(function(d) { return d; });
90209
90210                     wrap.call(item.reference.button);
90211                     selection.call(item.reference.body);
90212                 }
90213
90214                 item.choose = function() {
90215                     if (select(this).classed('disabled')) return;
90216                     if (!context.inIntro()) {
90217                         _mainPresetIndex.setMostRecent(preset, entityGeometries()[0]);
90218                     }
90219                     context.perform(
90220                         function(graph) {
90221                             for (var i in _entityIDs) {
90222                                 var entityID = _entityIDs[i];
90223                                 var oldPreset = _mainPresetIndex.match(graph.entity(entityID), graph);
90224                                 graph = actionChangePreset(entityID, oldPreset, preset)(graph);
90225                             }
90226                             return graph;
90227                         },
90228                         _t('operations.change_tags.annotation')
90229                     );
90230
90231                     context.validator().validate();  // rerun validation
90232                     dispatch$1.call('choose', this, preset);
90233                 };
90234
90235                 item.help = function() {
90236                     event.stopPropagation();
90237                     item.reference.toggle();
90238                 };
90239
90240                 item.preset = preset;
90241                 item.reference = uiTagReference(preset.reference(entityGeometries()[0]));
90242
90243                 return item;
90244             }
90245
90246
90247             function updateForFeatureHiddenState() {
90248                 if (!_entityIDs.every(context.hasEntity)) return;
90249
90250                 var geometries = entityGeometries();
90251                 var button = context.container().selectAll('.preset-list .preset-list-button');
90252
90253                 // remove existing tooltips
90254                 button.call(uiTooltip().destroyAny);
90255
90256                 button.each(function(item, index) {
90257                     var hiddenPresetFeaturesId;
90258                     for (var i in geometries) {
90259                         hiddenPresetFeaturesId = context.features().isHiddenPreset(item.preset, geometries[i]);
90260                         if (hiddenPresetFeaturesId) break;
90261                     }
90262                     var isHiddenPreset = !context.inIntro() &&
90263                         !!hiddenPresetFeaturesId &&
90264                         (_currentPresets.length !== 1 || item.preset !== _currentPresets[0]);
90265
90266                     select(this)
90267                         .classed('disabled', isHiddenPreset);
90268
90269                     if (isHiddenPreset) {
90270                         var isAutoHidden = context.features().autoHidden(hiddenPresetFeaturesId);
90271                         var tooltipIdSuffix = isAutoHidden ? 'zoom' : 'manual';
90272                         var tooltipObj = { features: _t('feature.' + hiddenPresetFeaturesId + '.description') };
90273                         select(this).call(uiTooltip()
90274                             .title(_t('inspector.hidden_preset.' + tooltipIdSuffix, tooltipObj))
90275                             .placement(index < 2 ? 'bottom' : 'top')
90276                         );
90277                     }
90278                 });
90279             }
90280
90281             presetList.autofocus = function(val) {
90282                 if (!arguments.length) return _autofocus;
90283                 _autofocus = val;
90284                 return presetList;
90285             };
90286
90287             presetList.entityIDs = function(val) {
90288                 if (!arguments.length) return _entityIDs;
90289                 _entityIDs = val;
90290                 if (_entityIDs && _entityIDs.length) {
90291                     var presets = _entityIDs.map(function(entityID) {
90292                         return _mainPresetIndex.match(context.entity(entityID), context.graph());
90293                     });
90294                     presetList.presets(presets);
90295                 }
90296                 return presetList;
90297             };
90298
90299             presetList.presets = function(val) {
90300                 if (!arguments.length) return _currentPresets;
90301                 _currentPresets = val;
90302                 return presetList;
90303             };
90304
90305             function entityGeometries() {
90306
90307                 var counts = {};
90308
90309                 for (var i in _entityIDs) {
90310                     var entityID = _entityIDs[i];
90311                     var entity = context.entity(entityID);
90312                     var geometry = entity.geometry(context.graph());
90313
90314                     // Treat entities on addr:interpolation lines as points, not vertices (#3241)
90315                     if (geometry === 'vertex' && entity.isOnAddressLine(context.graph())) {
90316                         geometry = 'point';
90317                     }
90318
90319                     if (!counts[geometry]) counts[geometry] = 0;
90320                     counts[geometry] += 1;
90321                 }
90322
90323                 return Object.keys(counts).sort(function(geom1, geom2) {
90324                     return counts[geom2] - counts[geom1];
90325                 });
90326             }
90327
90328             function combinedEntityExtent() {
90329                 return _entityIDs.reduce(function(extent, entityID) {
90330                     var entity = context.graph().entity(entityID);
90331                     return extent.extend(entity.extent(context.graph()));
90332                 }, geoExtent());
90333             }
90334
90335             return utilRebind(presetList, dispatch$1, 'on');
90336         }
90337
90338         function uiInspector(context) {
90339             var presetList = uiPresetList(context);
90340             var entityEditor = uiEntityEditor(context);
90341             var wrap = select(null),
90342                 presetPane = select(null),
90343                 editorPane = select(null);
90344             var _state = 'select';
90345             var _entityIDs;
90346             var _newFeature = false;
90347
90348
90349             function inspector(selection) {
90350                 presetList
90351                     .entityIDs(_entityIDs)
90352                     .autofocus(_newFeature)
90353                     .on('choose', inspector.setPreset)
90354                     .on('cancel', function() {
90355                         wrap.transition()
90356                             .styleTween('right', function() { return interpolate('-100%', '0%'); });
90357                         editorPane.call(entityEditor);
90358                     });
90359
90360                 entityEditor
90361                     .state(_state)
90362                     .entityIDs(_entityIDs)
90363                     .on('choose', inspector.showList);
90364
90365                 wrap = selection.selectAll('.panewrap')
90366                     .data([0]);
90367
90368                 var enter = wrap.enter()
90369                     .append('div')
90370                     .attr('class', 'panewrap');
90371
90372                 enter
90373                     .append('div')
90374                     .attr('class', 'preset-list-pane pane');
90375
90376                 enter
90377                     .append('div')
90378                     .attr('class', 'entity-editor-pane pane');
90379
90380                 wrap = wrap.merge(enter);
90381                 presetPane = wrap.selectAll('.preset-list-pane');
90382                 editorPane = wrap.selectAll('.entity-editor-pane');
90383
90384                 function shouldDefaultToPresetList() {
90385                     // always show the inspector on hover
90386                     if (_state !== 'select') return false;
90387
90388                     // can only change preset on single selection
90389                     if (_entityIDs.length !== 1) return false;
90390
90391                     var entityID = _entityIDs[0];
90392                     var entity = context.hasEntity(entityID);
90393                     if (!entity) return false;
90394
90395                     // default to inspector if there are already tags
90396                     if (entity.hasNonGeometryTags()) return false;
90397
90398                     // prompt to select preset if feature is new and untagged
90399                     if (_newFeature) return true;
90400
90401                     // all existing features except vertices should default to inspector
90402                     if (entity.geometry(context.graph()) !== 'vertex') return false;
90403
90404                     // show vertex relations if any
90405                     if (context.graph().parentRelations(entity).length) return false;
90406
90407                     // show vertex issues if there are any
90408                     if (context.validator().getEntityIssues(entityID).length) return false;
90409
90410                     // show turn retriction editor for junction vertices
90411                     if (entity.isHighwayIntersection(context.graph())) return false;
90412
90413                     // otherwise show preset list for uninteresting vertices
90414                     return true;
90415                 }
90416
90417                 if (shouldDefaultToPresetList()) {
90418                     wrap.style('right', '-100%');
90419                     presetPane.call(presetList);
90420                 } else {
90421                     wrap.style('right', '0%');
90422                     editorPane.call(entityEditor);
90423                 }
90424
90425                 var footer = selection.selectAll('.footer')
90426                     .data([0]);
90427
90428                 footer = footer.enter()
90429                     .append('div')
90430                     .attr('class', 'footer')
90431                     .merge(footer);
90432
90433                 footer
90434                     .call(uiViewOnOSM(context)
90435                         .what(context.hasEntity(_entityIDs.length === 1 && _entityIDs[0]))
90436                     );
90437             }
90438
90439             inspector.showList = function(presets) {
90440
90441                 wrap.transition()
90442                     .styleTween('right', function() { return interpolate('0%', '-100%'); });
90443
90444                 if (presets) {
90445                     presetList.presets(presets);
90446                 }
90447
90448                 presetPane
90449                     .call(presetList.autofocus(true));
90450             };
90451
90452             inspector.setPreset = function(preset) {
90453
90454                 // upon setting multipolygon, go to the area preset list instead of the editor
90455                 if (preset.id === 'type/multipolygon') {
90456                     presetPane
90457                         .call(presetList.autofocus(true));
90458
90459                 } else {
90460                     wrap.transition()
90461                         .styleTween('right', function() { return interpolate('-100%', '0%'); });
90462
90463                     editorPane
90464                         .call(entityEditor.presets([preset]));
90465                 }
90466
90467             };
90468
90469             inspector.state = function(val) {
90470                 if (!arguments.length) return _state;
90471                 _state = val;
90472                 entityEditor.state(_state);
90473
90474                 // remove any old field help overlay that might have gotten attached to the inspector
90475                 context.container().selectAll('.field-help-body').remove();
90476
90477                 return inspector;
90478             };
90479
90480
90481             inspector.entityIDs = function(val) {
90482                 if (!arguments.length) return _entityIDs;
90483                 _entityIDs = val;
90484                 return inspector;
90485             };
90486
90487
90488             inspector.newFeature = function(val) {
90489                 if (!arguments.length) return _newFeature;
90490                 _newFeature = val;
90491                 return inspector;
90492             };
90493
90494
90495             return inspector;
90496         }
90497
90498         function uiSidebar(context) {
90499             var inspector = uiInspector(context);
90500             var dataEditor = uiDataEditor(context);
90501             var noteEditor = uiNoteEditor(context);
90502             var improveOsmEditor = uiImproveOsmEditor(context);
90503             var keepRightEditor = uiKeepRightEditor(context);
90504             var osmoseEditor = uiOsmoseEditor(context);
90505             var _current;
90506             var _wasData = false;
90507             var _wasNote = false;
90508             var _wasQaItem = false;
90509
90510             // use pointer events on supported platforms; fallback to mouse events
90511             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
90512
90513
90514             function sidebar(selection) {
90515                 var container = context.container();
90516                 var minWidth = 240;
90517                 var sidebarWidth;
90518                 var containerWidth;
90519                 var dragOffset;
90520
90521                 // Set the initial width constraints
90522                 selection
90523                     .style('min-width', minWidth + 'px')
90524                     .style('max-width', '400px')
90525                     .style('width', '33.3333%');
90526
90527                 var resizer = selection
90528                     .append('div')
90529                     .attr('class', 'sidebar-resizer')
90530                     .on(_pointerPrefix + 'down.sidebar-resizer', pointerdown);
90531
90532                 var downPointerId, lastClientX, containerLocGetter;
90533
90534                 function pointerdown() {
90535                     if (downPointerId) return;
90536
90537                     if ('button' in event && event.button !== 0) return;
90538
90539                     downPointerId = event.pointerId || 'mouse';
90540
90541                     lastClientX = event.clientX;
90542
90543                     containerLocGetter = utilFastMouse(container.node());
90544
90545                     // offset from edge of sidebar-resizer
90546                     dragOffset = utilFastMouse(resizer.node())(event)[0] - 1;
90547
90548                     sidebarWidth = selection.node().getBoundingClientRect().width;
90549                     containerWidth = container.node().getBoundingClientRect().width;
90550                     var widthPct = (sidebarWidth / containerWidth) * 100;
90551                     selection
90552                         .style('width', widthPct + '%')    // lock in current width
90553                         .style('max-width', '85%');        // but allow larger widths
90554
90555                     resizer.classed('dragging', true);
90556
90557                     select(window)
90558                         .on('touchmove.sidebar-resizer', function() {
90559                             // disable page scrolling while resizing on touch input
90560                             event.preventDefault();
90561                         }, { passive: false })
90562                         .on(_pointerPrefix + 'move.sidebar-resizer', pointermove)
90563                         .on(_pointerPrefix + 'up.sidebar-resizer pointercancel.sidebar-resizer', pointerup);
90564                 }
90565
90566                 function pointermove() {
90567
90568                     if (downPointerId !== (event.pointerId || 'mouse')) return;
90569
90570                     event.preventDefault();
90571
90572                     var dx = event.clientX - lastClientX;
90573
90574                     lastClientX = event.clientX;
90575
90576                     var isRTL = (_mainLocalizer.textDirection() === 'rtl');
90577                     var scaleX = isRTL ? 0 : 1;
90578                     var xMarginProperty = isRTL ? 'margin-right' : 'margin-left';
90579
90580                     var x = containerLocGetter(event)[0] - dragOffset;
90581                     sidebarWidth = isRTL ? containerWidth - x : x;
90582
90583                     var isCollapsed = selection.classed('collapsed');
90584                     var shouldCollapse = sidebarWidth < minWidth;
90585
90586                     selection.classed('collapsed', shouldCollapse);
90587
90588                     if (shouldCollapse) {
90589                         if (!isCollapsed) {
90590                             selection
90591                                 .style(xMarginProperty, '-400px')
90592                                 .style('width', '400px');
90593
90594                             context.ui().onResize([(sidebarWidth - dx) * scaleX, 0]);
90595                         }
90596
90597                     } else {
90598                         var widthPct = (sidebarWidth / containerWidth) * 100;
90599                         selection
90600                             .style(xMarginProperty, null)
90601                             .style('width', widthPct + '%');
90602
90603                         if (isCollapsed) {
90604                             context.ui().onResize([-sidebarWidth * scaleX, 0]);
90605                         } else {
90606                             context.ui().onResize([-dx * scaleX, 0]);
90607                         }
90608                     }
90609                 }
90610
90611                 function pointerup() {
90612                     if (downPointerId !== (event.pointerId || 'mouse')) return;
90613
90614                     downPointerId = null;
90615
90616                     resizer.classed('dragging', false);
90617
90618                     select(window)
90619                         .on('touchmove.sidebar-resizer', null)
90620                         .on(_pointerPrefix + 'move.sidebar-resizer', null)
90621                         .on(_pointerPrefix + 'up.sidebar-resizer pointercancel.sidebar-resizer', null);
90622                 }
90623
90624                 var featureListWrap = selection
90625                     .append('div')
90626                     .attr('class', 'feature-list-pane')
90627                     .call(uiFeatureList(context));
90628
90629                 var inspectorWrap = selection
90630                     .append('div')
90631                     .attr('class', 'inspector-hidden inspector-wrap');
90632
90633                 var hoverModeSelect = function(targets) {
90634                     context.container().selectAll('.feature-list-item').classed('hover', false);
90635
90636                     if (context.selectedIDs().length > 1 &&
90637                         targets && targets.length) {
90638
90639                         var elements = context.container().selectAll('.feature-list-item')
90640                             .filter(function (node) {
90641                                 return targets.indexOf(node) !== -1;
90642                             });
90643
90644                         if (!elements.empty()) {
90645                             elements.classed('hover', true);
90646                         }
90647                     }
90648                 };
90649
90650                 sidebar.hoverModeSelect = throttle(hoverModeSelect, 200);
90651
90652                 function hover(targets) {
90653                     var datum = targets && targets.length && targets[0];
90654                     if (datum && datum.__featurehash__) {   // hovering on data
90655                         _wasData = true;
90656                         sidebar
90657                             .show(dataEditor.datum(datum));
90658
90659                         selection.selectAll('.sidebar-component')
90660                             .classed('inspector-hover', true);
90661
90662                     } else if (datum instanceof osmNote) {
90663                         if (context.mode().id === 'drag-note') return;
90664                         _wasNote = true;
90665
90666                         var osm = services.osm;
90667                         if (osm) {
90668                             datum = osm.getNote(datum.id);   // marker may contain stale data - get latest
90669                         }
90670
90671                         sidebar
90672                             .show(noteEditor.note(datum));
90673
90674                         selection.selectAll('.sidebar-component')
90675                             .classed('inspector-hover', true);
90676
90677                     } else if (datum instanceof QAItem) {
90678                         _wasQaItem = true;
90679
90680                         var errService = services[datum.service];
90681                         if (errService) {
90682                             // marker may contain stale data - get latest
90683                             datum = errService.getError(datum.id);
90684                         }
90685
90686                         // Currently only three possible services
90687                         var errEditor;
90688                         if (datum.service === 'keepRight') {
90689                             errEditor = keepRightEditor;
90690                         } else if (datum.service === 'osmose') {
90691                             errEditor = osmoseEditor;
90692                         } else {
90693                             errEditor = improveOsmEditor;
90694                         }
90695
90696                         context.container().selectAll('.qaItem.' + datum.service)
90697                             .classed('hover', function(d) { return d.id === datum.id; });
90698
90699                         sidebar
90700                             .show(errEditor.error(datum));
90701
90702                         selection.selectAll('.sidebar-component')
90703                             .classed('inspector-hover', true);
90704
90705                     } else if (!_current && (datum instanceof osmEntity)) {
90706                         featureListWrap
90707                             .classed('inspector-hidden', true);
90708
90709                         inspectorWrap
90710                             .classed('inspector-hidden', false)
90711                             .classed('inspector-hover', true);
90712
90713                         if (!inspector.entityIDs() || !utilArrayIdentical(inspector.entityIDs(), [datum.id]) || inspector.state() !== 'hover') {
90714                             inspector
90715                                 .state('hover')
90716                                 .entityIDs([datum.id])
90717                                 .newFeature(false);
90718
90719                             inspectorWrap
90720                                 .call(inspector);
90721                         }
90722
90723                     } else if (!_current) {
90724                         featureListWrap
90725                             .classed('inspector-hidden', false);
90726                         inspectorWrap
90727                             .classed('inspector-hidden', true);
90728                         inspector
90729                             .state('hide');
90730
90731                     } else if (_wasData || _wasNote || _wasQaItem) {
90732                         _wasNote = false;
90733                         _wasData = false;
90734                         _wasQaItem = false;
90735                         context.container().selectAll('.note').classed('hover', false);
90736                         context.container().selectAll('.qaItem').classed('hover', false);
90737                         sidebar.hide();
90738                     }
90739                 }
90740
90741                 sidebar.hover = throttle(hover, 200);
90742
90743
90744                 sidebar.intersects = function(extent) {
90745                     var rect = selection.node().getBoundingClientRect();
90746                     return extent.intersects([
90747                         context.projection.invert([0, rect.height]),
90748                         context.projection.invert([rect.width, 0])
90749                     ]);
90750                 };
90751
90752
90753                 sidebar.select = function(ids, newFeature) {
90754                     sidebar.hide();
90755
90756                     if (ids && ids.length) {
90757
90758                         var entity = ids.length === 1 && context.entity(ids[0]);
90759                         if (entity && newFeature && selection.classed('collapsed')) {
90760                             // uncollapse the sidebar
90761                             var extent = entity.extent(context.graph());
90762                             sidebar.expand(sidebar.intersects(extent));
90763                         }
90764
90765                         featureListWrap
90766                             .classed('inspector-hidden', true);
90767
90768                         inspectorWrap
90769                             .classed('inspector-hidden', false)
90770                             .classed('inspector-hover', false);
90771
90772                         if (!inspector.entityIDs() || !utilArrayIdentical(inspector.entityIDs(), ids) || inspector.state() !== 'select') {
90773                             inspector
90774                                 .state('select')
90775                                 .entityIDs(ids)
90776                                 .newFeature(newFeature);
90777
90778                             inspectorWrap
90779                                 .call(inspector);
90780                         }
90781
90782                     } else {
90783                         inspector
90784                             .state('hide');
90785                     }
90786                 };
90787
90788
90789                 sidebar.showPresetList = function() {
90790                     inspector.showList();
90791                 };
90792
90793
90794                 sidebar.show = function(component, element) {
90795                     featureListWrap
90796                         .classed('inspector-hidden', true);
90797                     inspectorWrap
90798                         .classed('inspector-hidden', true);
90799
90800                     if (_current) _current.remove();
90801                     _current = selection
90802                         .append('div')
90803                         .attr('class', 'sidebar-component')
90804                         .call(component, element);
90805                 };
90806
90807
90808                 sidebar.hide = function() {
90809                     featureListWrap
90810                         .classed('inspector-hidden', false);
90811                     inspectorWrap
90812                         .classed('inspector-hidden', true);
90813
90814                     if (_current) _current.remove();
90815                     _current = null;
90816                 };
90817
90818
90819                 sidebar.expand = function(moveMap) {
90820                     if (selection.classed('collapsed')) {
90821                         sidebar.toggle(moveMap);
90822                     }
90823                 };
90824
90825
90826                 sidebar.collapse = function(moveMap) {
90827                     if (!selection.classed('collapsed')) {
90828                         sidebar.toggle(moveMap);
90829                     }
90830                 };
90831
90832
90833                 sidebar.toggle = function(moveMap) {
90834                     var e = event;
90835                     if (e && e.sourceEvent) {
90836                         e.sourceEvent.preventDefault();
90837                     } else if (e) {
90838                         e.preventDefault();
90839                     }
90840
90841                     // Don't allow sidebar to toggle when the user is in the walkthrough.
90842                     if (context.inIntro()) return;
90843
90844                     var isCollapsed = selection.classed('collapsed');
90845                     var isCollapsing = !isCollapsed;
90846                     var isRTL = (_mainLocalizer.textDirection() === 'rtl');
90847                     var scaleX = isRTL ? 0 : 1;
90848                     var xMarginProperty = isRTL ? 'margin-right' : 'margin-left';
90849
90850                     sidebarWidth = selection.node().getBoundingClientRect().width;
90851
90852                     // switch from % to px
90853                     selection.style('width', sidebarWidth + 'px');
90854
90855                     var startMargin, endMargin, lastMargin;
90856                     if (isCollapsing) {
90857                         startMargin = lastMargin = 0;
90858                         endMargin = -sidebarWidth;
90859                     } else {
90860                         startMargin = lastMargin = -sidebarWidth;
90861                         endMargin = 0;
90862                     }
90863
90864                     selection.transition()
90865                         .style(xMarginProperty, endMargin + 'px')
90866                         .tween('panner', function() {
90867                             var i = d3_interpolateNumber(startMargin, endMargin);
90868                             return function(t) {
90869                                 var dx = lastMargin - Math.round(i(t));
90870                                 lastMargin = lastMargin - dx;
90871                                 context.ui().onResize(moveMap ? undefined : [dx * scaleX, 0]);
90872                             };
90873                         })
90874                         .on('end', function() {
90875                             selection.classed('collapsed', isCollapsing);
90876
90877                             // switch back from px to %
90878                             if (!isCollapsing) {
90879                                 var containerWidth = container.node().getBoundingClientRect().width;
90880                                 var widthPct = (sidebarWidth / containerWidth) * 100;
90881                                 selection
90882                                     .style(xMarginProperty, null)
90883                                     .style('width', widthPct + '%');
90884                             }
90885                         });
90886                 };
90887
90888                 // toggle the sidebar collapse when double-clicking the resizer
90889                 resizer.on('dblclick', sidebar.toggle);
90890
90891                 // ensure hover sidebar is closed when zooming out beyond editable zoom
90892                 context.map().on('crossEditableZoom.sidebar', function(within) {
90893                     if (!within && !selection.select('.inspector-hover').empty()) {
90894                         hover([]);
90895                     }
90896                 });
90897             }
90898
90899             sidebar.showPresetList = function() {};
90900             sidebar.hover = function() {};
90901             sidebar.hover.cancel = function() {};
90902             sidebar.intersects = function() {};
90903             sidebar.select = function() {};
90904             sidebar.show = function() {};
90905             sidebar.hide = function() {};
90906             sidebar.expand = function() {};
90907             sidebar.collapse = function() {};
90908             sidebar.toggle = function() {};
90909
90910             return sidebar;
90911         }
90912
90913         function uiSourceSwitch(context) {
90914             var keys;
90915
90916
90917             function click() {
90918                 event.preventDefault();
90919
90920                 var osm = context.connection();
90921                 if (!osm) return;
90922
90923                 if (context.inIntro()) return;
90924
90925                 if (context.history().hasChanges() &&
90926                     !window.confirm(_t('source_switch.lose_changes'))) return;
90927
90928                 var isLive = select(this)
90929                     .classed('live');
90930
90931                 isLive = !isLive;
90932                 context.enter(modeBrowse(context));
90933                 context.history().clearSaved();          // remove saved history
90934                 context.flush();                         // remove stored data
90935
90936                 select(this)
90937                     .text(isLive ? _t('source_switch.live') : _t('source_switch.dev'))
90938                     .classed('live', isLive)
90939                     .classed('chip', isLive);
90940
90941                 osm.switch(isLive ? keys[0] : keys[1]);  // switch connection (warning: dispatches 'change' event)
90942             }
90943
90944             var sourceSwitch = function(selection) {
90945                 selection
90946                     .append('a')
90947                     .attr('href', '#')
90948                     .text(_t('source_switch.live'))
90949                     .attr('class', 'live chip')
90950                     .on('click', click);
90951             };
90952
90953
90954             sourceSwitch.keys = function(_) {
90955                 if (!arguments.length) return keys;
90956                 keys = _;
90957                 return sourceSwitch;
90958             };
90959
90960
90961             return sourceSwitch;
90962         }
90963
90964         function uiSpinner(context) {
90965             var osm = context.connection();
90966
90967
90968             return function(selection) {
90969                 var img = selection
90970                     .append('img')
90971                     .attr('src', context.imagePath('loader-black.gif'))
90972                     .style('opacity', 0);
90973
90974                 if (osm) {
90975                     osm
90976                         .on('loading.spinner', function() {
90977                             img.transition()
90978                                 .style('opacity', 1);
90979                         })
90980                         .on('loaded.spinner', function() {
90981                             img.transition()
90982                                 .style('opacity', 0);
90983                         });
90984                 }
90985             };
90986         }
90987
90988         function uiSplash(context) {
90989           return (selection) => {
90990             // Exception - if there are restorable changes, skip this splash screen.
90991             // This is because we currently only support one `uiModal` at a time
90992             //  and we need to show them `uiRestore`` instead of this one.
90993             if (context.history().hasRestorableChanges()) return;
90994
90995             // If user has not seen this version of the privacy policy, show the splash again.
90996             let updateMessage = '';
90997             const sawPrivacyVersion = corePreferences('sawPrivacyVersion');
90998             let showSplash = !corePreferences('sawSplash');
90999             if (sawPrivacyVersion !== context.privacyVersion) {
91000               updateMessage = _t('splash.privacy_update');
91001               showSplash = true;
91002             }
91003
91004             if (!showSplash) return;
91005
91006             corePreferences('sawSplash', true);
91007             corePreferences('sawPrivacyVersion', context.privacyVersion);
91008
91009             // fetch intro graph data now, while user is looking at the splash screen
91010             _mainFileFetcher.get('intro_graph');
91011
91012             let modalSelection = uiModal(selection);
91013
91014             modalSelection.select('.modal')
91015               .attr('class', 'modal-splash modal');
91016
91017             let introModal = modalSelection.select('.content')
91018               .append('div')
91019               .attr('class', 'fillL');
91020
91021             introModal
91022               .append('div')
91023               .attr('class','modal-section')
91024               .append('h3')
91025               .text(_t('splash.welcome'));
91026
91027             let modalSection = introModal
91028               .append('div')
91029               .attr('class','modal-section');
91030
91031             modalSection
91032               .append('p')
91033               .html(_t('splash.text', {
91034                 version: context.version,
91035                 website: '<a target="_blank" href="http://ideditor.blog/">ideditor.blog</a>',
91036                 github: '<a target="_blank" href="https://github.com/openstreetmap/iD">github.com</a>'
91037               }));
91038
91039             modalSection
91040               .append('p')
91041               .html(_t('splash.privacy', {
91042                 updateMessage: updateMessage,
91043                 privacyLink: '<a target="_blank" href="https://github.com/openstreetmap/iD/blob/release/PRIVACY.md">' +
91044                   _t('splash.privacy_policy') + '</a>'
91045               }));
91046
91047             let buttonWrap = introModal
91048               .append('div')
91049               .attr('class', 'modal-actions');
91050
91051             let walkthrough = buttonWrap
91052               .append('button')
91053               .attr('class', 'walkthrough')
91054               .on('click', () => {
91055                 context.container().call(uiIntro(context));
91056                 modalSelection.close();
91057               });
91058
91059             walkthrough
91060               .append('svg')
91061               .attr('class', 'logo logo-walkthrough')
91062               .append('use')
91063               .attr('xlink:href', '#iD-logo-walkthrough');
91064
91065             walkthrough
91066               .append('div')
91067               .text(_t('splash.walkthrough'));
91068
91069             let startEditing = buttonWrap
91070               .append('button')
91071               .attr('class', 'start-editing')
91072               .on('click', modalSelection.close);
91073
91074             startEditing
91075               .append('svg')
91076               .attr('class', 'logo logo-features')
91077               .append('use')
91078               .attr('xlink:href', '#iD-logo-features');
91079
91080             startEditing
91081               .append('div')
91082               .text(_t('splash.start'));
91083
91084             modalSelection.select('button.close')
91085               .attr('class','hide');
91086           };
91087         }
91088
91089         function uiStatus(context) {
91090             var osm = context.connection();
91091
91092
91093             return function(selection) {
91094                 if (!osm) return;
91095
91096                 function update(err, apiStatus) {
91097                     selection.html('');
91098
91099                     if (err) {
91100                         if (apiStatus === 'connectionSwitched') {
91101                             // if the connection was just switched, we can't rely on
91102                             // the status (we're getting the status of the previous api)
91103                             return;
91104
91105                         } else if (apiStatus === 'rateLimited') {
91106                             selection
91107                                 .text(_t('osm_api_status.message.rateLimit'))
91108                                 .append('a')
91109                                 .attr('class', 'api-status-login')
91110                                 .attr('target', '_blank')
91111                                 .call(svgIcon('#iD-icon-out-link', 'inline'))
91112                                 .append('span')
91113                                 .text(_t('login'))
91114                                 .on('click.login', function() {
91115                                     event.preventDefault();
91116                                     osm.authenticate();
91117                                 });
91118                         } else {
91119
91120                             // don't allow retrying too rapidly
91121                             var throttledRetry = throttle(function() {
91122                                 // try loading the visible tiles
91123                                 context.loadTiles(context.projection);
91124                                 // manually reload the status too in case all visible tiles were already loaded
91125                                 osm.reloadApiStatus();
91126                             }, 2000);
91127
91128                             // eslint-disable-next-line no-warning-comments
91129                             // TODO: nice messages for different error types
91130                             selection
91131                                 .text(_t('osm_api_status.message.error') + ' ')
91132                                 .append('a')
91133                                 // let the user manually retry their connection directly
91134                                 .text(_t('osm_api_status.retry'))
91135                                 .on('click.retry', function() {
91136                                     event.preventDefault();
91137                                     throttledRetry();
91138                                 });
91139                         }
91140
91141                     } else if (apiStatus === 'readonly') {
91142                         selection.text(_t('osm_api_status.message.readonly'));
91143                     } else if (apiStatus === 'offline') {
91144                         selection.text(_t('osm_api_status.message.offline'));
91145                     }
91146
91147                     selection.attr('class', 'api-status ' + (err ? 'error' : apiStatus));
91148                 }
91149
91150                 osm.on('apiStatusChange.uiStatus', update);
91151
91152                 // reload the status periodically regardless of other factors
91153                 window.setInterval(function() {
91154                     osm.reloadApiStatus();
91155                 }, 90000);
91156
91157                 // load the initial status in case no OSM data was loaded yet
91158                 osm.reloadApiStatus();
91159             };
91160         }
91161
91162         function modeDrawArea(context, wayID, startGraph, button) {
91163             var mode = {
91164                 button: button,
91165                 id: 'draw-area'
91166             };
91167
91168             var behavior = behaviorDrawWay(context, wayID, mode, startGraph)
91169                 .on('rejectedSelfIntersection.modeDrawArea', function() {
91170                     context.ui().flash
91171                         .text(_t('self_intersection.error.areas'))();
91172                 });
91173
91174             mode.wayID = wayID;
91175
91176             mode.enter = function() {
91177                 context.install(behavior);
91178             };
91179
91180             mode.exit = function() {
91181                 context.uninstall(behavior);
91182             };
91183
91184             mode.selectedIDs = function() {
91185                 return [wayID];
91186             };
91187
91188             mode.activeID = function() {
91189                 return (behavior && behavior.activeID()) || [];
91190             };
91191
91192             return mode;
91193         }
91194
91195         function modeAddArea(context, mode) {
91196             mode.id = 'add-area';
91197
91198             var behavior = behaviorAddWay(context)
91199                 .on('start', start)
91200                 .on('startFromWay', startFromWay)
91201                 .on('startFromNode', startFromNode);
91202
91203             var defaultTags = { area: 'yes' };
91204             if (mode.preset) defaultTags = mode.preset.setTags(defaultTags, 'area');
91205
91206
91207             function actionClose(wayId) {
91208                 return function (graph) {
91209                     return graph.replace(graph.entity(wayId).close());
91210                 };
91211             }
91212
91213
91214             function start(loc) {
91215                 var startGraph = context.graph();
91216                 var node = osmNode({ loc: loc });
91217                 var way = osmWay({ tags: defaultTags });
91218
91219                 context.perform(
91220                     actionAddEntity(node),
91221                     actionAddEntity(way),
91222                     actionAddVertex(way.id, node.id),
91223                     actionClose(way.id)
91224                 );
91225
91226                 context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
91227             }
91228
91229
91230             function startFromWay(loc, edge) {
91231                 var startGraph = context.graph();
91232                 var node = osmNode({ loc: loc });
91233                 var way = osmWay({ tags: defaultTags });
91234
91235                 context.perform(
91236                     actionAddEntity(node),
91237                     actionAddEntity(way),
91238                     actionAddVertex(way.id, node.id),
91239                     actionClose(way.id),
91240                     actionAddMidpoint({ loc: loc, edge: edge }, node)
91241                 );
91242
91243                 context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
91244             }
91245
91246
91247             function startFromNode(node) {
91248                 var startGraph = context.graph();
91249                 var way = osmWay({ tags: defaultTags });
91250
91251                 context.perform(
91252                     actionAddEntity(way),
91253                     actionAddVertex(way.id, node.id),
91254                     actionClose(way.id)
91255                 );
91256
91257                 context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
91258             }
91259
91260
91261             mode.enter = function() {
91262                 context.install(behavior);
91263             };
91264
91265
91266             mode.exit = function() {
91267                 context.uninstall(behavior);
91268             };
91269
91270
91271             return mode;
91272         }
91273
91274         function modeAddLine(context, mode) {
91275             mode.id = 'add-line';
91276
91277             var behavior = behaviorAddWay(context)
91278                 .on('start', start)
91279                 .on('startFromWay', startFromWay)
91280                 .on('startFromNode', startFromNode);
91281
91282             var defaultTags = {};
91283             if (mode.preset) defaultTags = mode.preset.setTags(defaultTags, 'line');
91284
91285
91286             function start(loc) {
91287                 var startGraph = context.graph();
91288                 var node = osmNode({ loc: loc });
91289                 var way = osmWay({ tags: defaultTags });
91290
91291                 context.perform(
91292                     actionAddEntity(node),
91293                     actionAddEntity(way),
91294                     actionAddVertex(way.id, node.id)
91295                 );
91296
91297                 context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
91298             }
91299
91300
91301             function startFromWay(loc, edge) {
91302                 var startGraph = context.graph();
91303                 var node = osmNode({ loc: loc });
91304                 var way = osmWay({ tags: defaultTags });
91305
91306                 context.perform(
91307                     actionAddEntity(node),
91308                     actionAddEntity(way),
91309                     actionAddVertex(way.id, node.id),
91310                     actionAddMidpoint({ loc: loc, edge: edge }, node)
91311                 );
91312
91313                 context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
91314             }
91315
91316
91317             function startFromNode(node) {
91318                 var startGraph = context.graph();
91319                 var way = osmWay({ tags: defaultTags });
91320
91321                 context.perform(
91322                     actionAddEntity(way),
91323                     actionAddVertex(way.id, node.id)
91324                 );
91325
91326                 context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
91327             }
91328
91329
91330             mode.enter = function() {
91331                 context.install(behavior);
91332             };
91333
91334
91335             mode.exit = function() {
91336                 context.uninstall(behavior);
91337             };
91338
91339             return mode;
91340         }
91341
91342         function modeAddPoint(context, mode) {
91343
91344             mode.id = 'add-point';
91345
91346             var behavior = behaviorDraw(context)
91347                 .on('click', add)
91348                 .on('clickWay', addWay)
91349                 .on('clickNode', addNode)
91350                 .on('cancel', cancel)
91351                 .on('finish', cancel);
91352
91353             var defaultTags = {};
91354             if (mode.preset) defaultTags = mode.preset.setTags(defaultTags, 'point');
91355
91356
91357             function add(loc) {
91358                 var node = osmNode({ loc: loc, tags: defaultTags });
91359
91360                 context.perform(
91361                     actionAddEntity(node),
91362                     _t('operations.add.annotation.point')
91363                 );
91364
91365                 enterSelectMode(node);
91366             }
91367
91368
91369             function addWay(loc, edge) {
91370                 var node = osmNode({ tags: defaultTags });
91371
91372                 context.perform(
91373                     actionAddMidpoint({loc: loc, edge: edge}, node),
91374                     _t('operations.add.annotation.vertex')
91375                 );
91376
91377                 enterSelectMode(node);
91378             }
91379
91380             function enterSelectMode(node) {
91381                 context.enter(
91382                     modeSelect(context, [node.id]).newFeature(true)
91383                 );
91384             }
91385
91386
91387             function addNode(node) {
91388                 if (Object.keys(defaultTags).length === 0) {
91389                     enterSelectMode(node);
91390                     return;
91391                 }
91392
91393                 var tags = Object.assign({}, node.tags);  // shallow copy
91394                 for (var key in defaultTags) {
91395                     tags[key] = defaultTags[key];
91396                 }
91397
91398                 context.perform(
91399                     actionChangeTags(node.id, tags),
91400                     _t('operations.add.annotation.point')
91401                 );
91402
91403                 enterSelectMode(node);
91404             }
91405
91406
91407             function cancel() {
91408                 context.enter(modeBrowse(context));
91409             }
91410
91411
91412             mode.enter = function() {
91413                 context.install(behavior);
91414             };
91415
91416
91417             mode.exit = function() {
91418                 context.uninstall(behavior);
91419             };
91420
91421
91422             return mode;
91423         }
91424
91425         function modeAddNote(context) {
91426             var mode = {
91427                 id: 'add-note',
91428                 button: 'note',
91429                 title: _t('modes.add_note.title'),
91430                 description: _t('modes.add_note.description'),
91431                 key: _t('modes.add_note.key')
91432             };
91433
91434             var behavior = behaviorDraw(context)
91435                 .on('click', add)
91436                 .on('cancel', cancel)
91437                 .on('finish', cancel);
91438
91439
91440             function add(loc) {
91441                 var osm = services.osm;
91442                 if (!osm) return;
91443
91444                 var note = osmNote({ loc: loc, status: 'open', comments: [] });
91445                 osm.replaceNote(note);
91446
91447                 // force a reraw (there is no history change that would otherwise do this)
91448                 context.map().pan([0,0]);
91449
91450                 context
91451                     .selectedNoteID(note.id)
91452                     .enter(modeSelectNote(context, note.id).newFeature(true));
91453             }
91454
91455
91456             function cancel() {
91457                 context.enter(modeBrowse(context));
91458             }
91459
91460
91461             mode.enter = function() {
91462                 context.install(behavior);
91463             };
91464
91465
91466             mode.exit = function() {
91467                 context.uninstall(behavior);
91468             };
91469
91470
91471             return mode;
91472         }
91473
91474         function uiConflicts(context) {
91475             var dispatch$1 = dispatch('cancel', 'save');
91476             var keybinding = utilKeybinding('conflicts');
91477             var _origChanges;
91478             var _conflictList;
91479             var _shownConflictIndex;
91480
91481
91482             function keybindingOn() {
91483                 select(document)
91484                     .call(keybinding.on('⎋', cancel, true));
91485             }
91486
91487             function keybindingOff() {
91488                 select(document)
91489                     .call(keybinding.unbind);
91490             }
91491
91492             function tryAgain() {
91493                 keybindingOff();
91494                 dispatch$1.call('save');
91495             }
91496
91497             function cancel() {
91498                 keybindingOff();
91499                 dispatch$1.call('cancel');
91500             }
91501
91502
91503             function conflicts(selection) {
91504                 keybindingOn();
91505
91506                 var headerEnter = selection.selectAll('.header')
91507                     .data([0])
91508                     .enter()
91509                     .append('div')
91510                     .attr('class', 'header fillL');
91511
91512                 headerEnter
91513                     .append('button')
91514                     .attr('class', 'fr')
91515                     .on('click', cancel)
91516                     .call(svgIcon('#iD-icon-close'));
91517
91518                 headerEnter
91519                     .append('h3')
91520                     .text(_t('save.conflict.header'));
91521
91522                 var bodyEnter = selection.selectAll('.body')
91523                     .data([0])
91524                     .enter()
91525                     .append('div')
91526                     .attr('class', 'body fillL');
91527
91528                 var conflictsHelpEnter = bodyEnter
91529                     .append('div')
91530                     .attr('class', 'conflicts-help')
91531                     .text(_t('save.conflict.help'));
91532
91533
91534                 // Download changes link
91535                 var detected = utilDetect();
91536                 var changeset = new osmChangeset();
91537
91538                 delete changeset.id;  // Export without changeset_id
91539
91540                 var data = JXON.stringify(changeset.osmChangeJXON(_origChanges));
91541                 var blob = new Blob([data], { type: 'text/xml;charset=utf-8;' });
91542                 var fileName = 'changes.osc';
91543
91544                 var linkEnter = conflictsHelpEnter.selectAll('.download-changes')
91545                     .append('a')
91546                     .attr('class', 'download-changes');
91547
91548                 if (detected.download) {      // All except IE11 and Edge
91549                     linkEnter                 // download the data as a file
91550                         .attr('href', window.URL.createObjectURL(blob))
91551                         .attr('download', fileName);
91552
91553                 } else {                      // IE11 and Edge
91554                     linkEnter                 // open data uri in a new tab
91555                         .attr('target', '_blank')
91556                         .on('click.download', function() {
91557                             navigator.msSaveBlob(blob, fileName);
91558                         });
91559                 }
91560
91561                 linkEnter
91562                     .call(svgIcon('#iD-icon-load', 'inline'))
91563                     .append('span')
91564                     .text(_t('save.conflict.download_changes'));
91565
91566
91567                 bodyEnter
91568                     .append('div')
91569                     .attr('class', 'conflict-container fillL3')
91570                     .call(showConflict, 0);
91571
91572                 bodyEnter
91573                     .append('div')
91574                     .attr('class', 'conflicts-done')
91575                     .attr('opacity', 0)
91576                     .style('display', 'none')
91577                     .text(_t('save.conflict.done'));
91578
91579                 var buttonsEnter = bodyEnter
91580                     .append('div')
91581                     .attr('class','buttons col12 joined conflicts-buttons');
91582
91583                 buttonsEnter
91584                     .append('button')
91585                     .attr('disabled', _conflictList.length > 1)
91586                     .attr('class', 'action conflicts-button col6')
91587                     .text(_t('save.title'))
91588                     .on('click.try_again', tryAgain);
91589
91590                 buttonsEnter
91591                     .append('button')
91592                     .attr('class', 'secondary-action conflicts-button col6')
91593                     .text(_t('confirm.cancel'))
91594                     .on('click.cancel', cancel);
91595             }
91596
91597
91598             function showConflict(selection, index) {
91599                 index = utilWrap(index, _conflictList.length);
91600                 _shownConflictIndex = index;
91601
91602                 var parent = select(selection.node().parentNode);
91603
91604                 // enable save button if this is the last conflict being reviewed..
91605                 if (index === _conflictList.length - 1) {
91606                     window.setTimeout(function() {
91607                         parent.select('.conflicts-button')
91608                             .attr('disabled', null);
91609
91610                         parent.select('.conflicts-done')
91611                             .transition()
91612                             .attr('opacity', 1)
91613                             .style('display', 'block');
91614                     }, 250);
91615                 }
91616
91617                 var conflict = selection
91618                     .selectAll('.conflict')
91619                     .data([_conflictList[index]]);
91620
91621                 conflict.exit()
91622                     .remove();
91623
91624                 var conflictEnter = conflict.enter()
91625                     .append('div')
91626                     .attr('class', 'conflict');
91627
91628                 conflictEnter
91629                     .append('h4')
91630                     .attr('class', 'conflict-count')
91631                     .text(_t('save.conflict.count', { num: index + 1, total: _conflictList.length }));
91632
91633                 conflictEnter
91634                     .append('a')
91635                     .attr('class', 'conflict-description')
91636                     .attr('href', '#')
91637                     .text(function(d) { return d.name; })
91638                     .on('click', function(d) {
91639                         event.preventDefault();
91640                         zoomToEntity(d.id);
91641                     });
91642
91643                 var details = conflictEnter
91644                     .append('div')
91645                     .attr('class', 'conflict-detail-container');
91646
91647                 details
91648                     .append('ul')
91649                     .attr('class', 'conflict-detail-list')
91650                     .selectAll('li')
91651                     .data(function(d) { return d.details || []; })
91652                     .enter()
91653                     .append('li')
91654                     .attr('class', 'conflict-detail-item')
91655                     .html(function(d) { return d; });
91656
91657                 details
91658                     .append('div')
91659                     .attr('class', 'conflict-choices')
91660                     .call(addChoices);
91661
91662                 details
91663                     .append('div')
91664                     .attr('class', 'conflict-nav-buttons joined cf')
91665                     .selectAll('button')
91666                     .data(['previous', 'next'])
91667                     .enter()
91668                     .append('button')
91669                     .text(function(d) { return _t('save.conflict.' + d); })
91670                     .attr('class', 'conflict-nav-button action col6')
91671                     .attr('disabled', function(d, i) {
91672                         return (i === 0 && index === 0) ||
91673                             (i === 1 && index === _conflictList.length - 1) || null;
91674                     })
91675                     .on('click', function(d, i) {
91676                         event.preventDefault();
91677
91678                         var container = parent.selectAll('.conflict-container');
91679                         var sign = (i === 0 ? -1 : 1);
91680
91681                         container
91682                             .selectAll('.conflict')
91683                             .remove();
91684
91685                         container
91686                             .call(showConflict, index + sign);
91687                     });
91688
91689             }
91690
91691
91692             function addChoices(selection) {
91693                 var choices = selection
91694                     .append('ul')
91695                     .attr('class', 'layer-list')
91696                     .selectAll('li')
91697                     .data(function(d) { return d.choices || []; });
91698
91699                 // enter
91700                 var choicesEnter = choices.enter()
91701                     .append('li')
91702                     .attr('class', 'layer');
91703
91704                 var labelEnter = choicesEnter
91705                     .append('label');
91706
91707                 labelEnter
91708                     .append('input')
91709                     .attr('type', 'radio')
91710                     .attr('name', function(d) { return d.id; })
91711                     .on('change', function(d, i) {
91712                         var ul = this.parentNode.parentNode.parentNode;
91713                         ul.__data__.chosen = i;
91714                         choose(ul, d);
91715                     });
91716
91717                 labelEnter
91718                     .append('span')
91719                     .text(function(d) { return d.text; });
91720
91721                 // update
91722                 choicesEnter
91723                     .merge(choices)
91724                     .each(function(d, i) {
91725                         var ul = this.parentNode;
91726                         if (ul.__data__.chosen === i) {
91727                             choose(ul, d);
91728                         }
91729                     });
91730             }
91731
91732
91733             function choose(ul, datum) {
91734                 if (event) event.preventDefault();
91735
91736                 select(ul)
91737                     .selectAll('li')
91738                     .classed('active', function(d) { return d === datum; })
91739                     .selectAll('input')
91740                     .property('checked', function(d) { return d === datum; });
91741
91742                 var extent = geoExtent();
91743                 var entity;
91744
91745                 entity = context.graph().hasEntity(datum.id);
91746                 if (entity) extent._extend(entity.extent(context.graph()));
91747
91748                 datum.action();
91749
91750                 entity = context.graph().hasEntity(datum.id);
91751                 if (entity) extent._extend(entity.extent(context.graph()));
91752
91753                 zoomToEntity(datum.id, extent);
91754             }
91755
91756
91757             function zoomToEntity(id, extent) {
91758                 context.surface().selectAll('.hover')
91759                     .classed('hover', false);
91760
91761                 var entity = context.graph().hasEntity(id);
91762                 if (entity) {
91763                     if (extent) {
91764                         context.map().trimmedExtent(extent);
91765                     } else {
91766                         context.map().zoomToEase(entity);
91767                     }
91768                     context.surface().selectAll(utilEntityOrMemberSelector([entity.id], context.graph()))
91769                         .classed('hover', true);
91770                 }
91771             }
91772
91773
91774             // The conflict list should be an array of objects like:
91775             // {
91776             //     id: id,
91777             //     name: entityName(local),
91778             //     details: merge.conflicts(),
91779             //     chosen: 1,
91780             //     choices: [
91781             //         choice(id, keepMine, forceLocal),
91782             //         choice(id, keepTheirs, forceRemote)
91783             //     ]
91784             // }
91785             conflicts.conflictList = function(_) {
91786                 if (!arguments.length) return _conflictList;
91787                 _conflictList = _;
91788                 return conflicts;
91789             };
91790
91791
91792             conflicts.origChanges = function(_) {
91793                 if (!arguments.length) return _origChanges;
91794                 _origChanges = _;
91795                 return conflicts;
91796             };
91797
91798
91799             conflicts.shownEntityIds = function() {
91800                 if (_conflictList && typeof _shownConflictIndex === 'number') {
91801                     return [_conflictList[_shownConflictIndex].id];
91802                 }
91803                 return [];
91804             };
91805
91806
91807             return utilRebind(conflicts, dispatch$1, 'on');
91808         }
91809
91810         function uiConfirm(selection) {
91811             var modalSelection = uiModal(selection);
91812
91813             modalSelection.select('.modal')
91814                 .classed('modal-alert', true);
91815
91816             var section = modalSelection.select('.content');
91817
91818             section.append('div')
91819                 .attr('class', 'modal-section header');
91820
91821             section.append('div')
91822                 .attr('class', 'modal-section message-text');
91823
91824             var buttons = section.append('div')
91825                 .attr('class', 'modal-section buttons cf');
91826
91827
91828             modalSelection.okButton = function() {
91829                 buttons
91830                     .append('button')
91831                     .attr('class', 'button ok-button action')
91832                     .on('click.confirm', function() {
91833                         modalSelection.remove();
91834                     })
91835                     .text(_t('confirm.okay'))
91836                     .node()
91837                     .focus();
91838
91839                 return modalSelection;
91840             };
91841
91842
91843             return modalSelection;
91844         }
91845
91846         function uiChangesetEditor(context) {
91847             var dispatch$1 = dispatch('change');
91848             var formFields = uiFormFields(context);
91849             var commentCombo = uiCombobox(context, 'comment').caseSensitive(true);
91850             var _fieldsArr;
91851             var _tags;
91852             var _changesetID;
91853
91854
91855             function changesetEditor(selection) {
91856                 render(selection);
91857             }
91858
91859
91860             function render(selection) {
91861                 var initial = false;
91862
91863                 if (!_fieldsArr) {
91864                     initial = true;
91865                     var presets = _mainPresetIndex;
91866
91867                     _fieldsArr = [
91868                         uiField(context, presets.field('comment'), null, { show: true, revert: false }),
91869                         uiField(context, presets.field('source'), null, { show: false, revert: false }),
91870                         uiField(context, presets.field('hashtags'), null, { show: false, revert: false }),
91871                     ];
91872
91873                     _fieldsArr.forEach(function(field) {
91874                         field
91875                             .on('change', function(t, onInput) {
91876                                 dispatch$1.call('change', field, undefined, t, onInput);
91877                             });
91878                     });
91879                 }
91880
91881                 _fieldsArr.forEach(function(field) {
91882                     field
91883                         .tags(_tags);
91884                 });
91885
91886
91887                 selection
91888                     .call(formFields.fieldsArr(_fieldsArr));
91889
91890
91891                 if (initial) {
91892                     var commentField = selection.select('.form-field-comment textarea');
91893                     var commentNode = commentField.node();
91894
91895                     if (commentNode) {
91896                         commentNode.focus();
91897                         commentNode.select();
91898                     }
91899
91900                     // trigger a 'blur' event so that comment field can be cleaned
91901                     // and checked for hashtags, even if retrieved from localstorage
91902                     utilTriggerEvent(commentField, 'blur');
91903
91904                     var osm = context.connection();
91905                     if (osm) {
91906                         osm.userChangesets(function (err, changesets) {
91907                             if (err) return;
91908
91909                             var comments = changesets.map(function(changeset) {
91910                                 var comment = changeset.tags.comment;
91911                                 return comment ? { title: comment, value: comment } : null;
91912                             }).filter(Boolean);
91913
91914                             commentField
91915                                 .call(commentCombo
91916                                     .data(utilArrayUniqBy(comments, 'title'))
91917                                 );
91918                         });
91919                     }
91920                 }
91921
91922                 // Add warning if comment mentions Google
91923                 var hasGoogle = _tags.comment.match(/google/i);
91924                 var commentWarning = selection.select('.form-field-comment').selectAll('.comment-warning')
91925                     .data(hasGoogle ? [0] : []);
91926
91927                 commentWarning.exit()
91928                     .transition()
91929                     .duration(200)
91930                     .style('opacity', 0)
91931                     .remove();
91932
91933                 var commentEnter = commentWarning.enter()
91934                     .insert('div', '.tag-reference-body')
91935                     .attr('class', 'field-warning comment-warning')
91936                     .style('opacity', 0);
91937
91938                 commentEnter
91939                     .append('a')
91940                     .attr('target', '_blank')
91941                     .attr('tabindex', -1)
91942                     .call(svgIcon('#iD-icon-alert', 'inline'))
91943                     .attr('href', _t('commit.google_warning_link'))
91944                     .append('span')
91945                     .text(_t('commit.google_warning'));
91946
91947                 commentEnter
91948                     .transition()
91949                     .duration(200)
91950                     .style('opacity', 1);
91951             }
91952
91953
91954             changesetEditor.tags = function(_) {
91955                 if (!arguments.length) return _tags;
91956                 _tags = _;
91957                 // Don't reset _fieldsArr here.
91958                 return changesetEditor;
91959             };
91960
91961
91962             changesetEditor.changesetID = function(_) {
91963                 if (!arguments.length) return _changesetID;
91964                 if (_changesetID === _) return changesetEditor;
91965                 _changesetID = _;
91966                 _fieldsArr = null;
91967                 return changesetEditor;
91968             };
91969
91970
91971             return utilRebind(changesetEditor, dispatch$1, 'on');
91972         }
91973
91974         function uiSectionChanges(context) {
91975             var detected = utilDetect();
91976
91977             var _discardTags = {};
91978             _mainFileFetcher.get('discarded')
91979                 .then(function(d) { _discardTags = d; })
91980                 .catch(function() { /* ignore */ });
91981
91982             var section = uiSection('changes-list', context)
91983                 .title(function() {
91984                     var history = context.history();
91985                     var summary = history.difference().summary();
91986                     return _t('commit.changes', { count: summary.length });
91987                 })
91988                 .disclosureContent(renderDisclosureContent);
91989
91990             function renderDisclosureContent(selection) {
91991                 var history = context.history();
91992                 var summary = history.difference().summary();
91993
91994                 var container = selection.selectAll('.commit-section')
91995                     .data([0]);
91996
91997                 var containerEnter = container.enter()
91998                     .append('div')
91999                     .attr('class', 'commit-section');
92000
92001                 containerEnter
92002                     .append('ul')
92003                     .attr('class', 'changeset-list');
92004
92005                 container = containerEnter
92006                     .merge(container);
92007
92008
92009                 var items = container.select('ul').selectAll('li')
92010                     .data(summary);
92011
92012                 var itemsEnter = items.enter()
92013                     .append('li')
92014                     .attr('class', 'change-item');
92015
92016                 itemsEnter
92017                     .each(function(d) {
92018                         select(this)
92019                             .call(svgIcon('#iD-icon-' + d.entity.geometry(d.graph), 'pre-text ' + d.changeType));
92020                     });
92021
92022                 itemsEnter
92023                     .append('span')
92024                     .attr('class', 'change-type')
92025                     .text(function(d) { return _t('commit.' + d.changeType) + ' '; });
92026
92027                 itemsEnter
92028                     .append('strong')
92029                     .attr('class', 'entity-type')
92030                     .text(function(d) {
92031                         var matched = _mainPresetIndex.match(d.entity, d.graph);
92032                         return (matched && matched.name()) || utilDisplayType(d.entity.id);
92033                     });
92034
92035                 itemsEnter
92036                     .append('span')
92037                     .attr('class', 'entity-name')
92038                     .text(function(d) {
92039                         var name = utilDisplayName(d.entity) || '',
92040                             string = '';
92041                         if (name !== '') {
92042                             string += ':';
92043                         }
92044                         return string += ' ' + name;
92045                     });
92046
92047                 itemsEnter
92048                     .style('opacity', 0)
92049                     .transition()
92050                     .style('opacity', 1);
92051
92052                 items = itemsEnter
92053                     .merge(items);
92054
92055                 items
92056                     .on('mouseover', mouseover)
92057                     .on('mouseout', mouseout)
92058                     .on('click', click);
92059
92060
92061                 // Download changeset link
92062                 var changeset = new osmChangeset().update({ id: undefined });
92063                 var changes = history.changes(actionDiscardTags(history.difference(), _discardTags));
92064
92065                 delete changeset.id;  // Export without chnageset_id
92066
92067                 var data = JXON.stringify(changeset.osmChangeJXON(changes));
92068                 var blob = new Blob([data], {type: 'text/xml;charset=utf-8;'});
92069                 var fileName = 'changes.osc';
92070
92071                 var linkEnter = container.selectAll('.download-changes')
92072                     .data([0])
92073                     .enter()
92074                     .append('a')
92075                     .attr('class', 'download-changes');
92076
92077                 if (detected.download) {      // All except IE11 and Edge
92078                     linkEnter                 // download the data as a file
92079                         .attr('href', window.URL.createObjectURL(blob))
92080                         .attr('download', fileName);
92081
92082                 } else {                      // IE11 and Edge
92083                     linkEnter                 // open data uri in a new tab
92084                         .attr('target', '_blank')
92085                         .on('click.download', function() {
92086                             navigator.msSaveBlob(blob, fileName);
92087                         });
92088                 }
92089
92090                 linkEnter
92091                     .call(svgIcon('#iD-icon-load', 'inline'))
92092                     .append('span')
92093                     .text(_t('commit.download_changes'));
92094
92095
92096                 function mouseover(d) {
92097                     if (d.entity) {
92098                         context.surface().selectAll(
92099                             utilEntityOrMemberSelector([d.entity.id], context.graph())
92100                         ).classed('hover', true);
92101                     }
92102                 }
92103
92104
92105                 function mouseout() {
92106                     context.surface().selectAll('.hover')
92107                         .classed('hover', false);
92108                 }
92109
92110
92111                 function click(change) {
92112                     if (change.changeType !== 'deleted') {
92113                         var entity = change.entity;
92114                         context.map().zoomToEase(entity);
92115                         context.surface().selectAll(utilEntityOrMemberSelector([entity.id], context.graph()))
92116                             .classed('hover', true);
92117                     }
92118                 }
92119             }
92120
92121             return section;
92122         }
92123
92124         function uiCommitWarnings(context) {
92125
92126             function commitWarnings(selection) {
92127                 var issuesBySeverity = context.validator()
92128                     .getIssuesBySeverity({ what: 'edited', where: 'all', includeDisabledRules: true });
92129
92130                 for (var severity in issuesBySeverity) {
92131                     var issues = issuesBySeverity[severity];
92132                     var section = severity + '-section';
92133                     var issueItem = severity + '-item';
92134
92135                     var container = selection.selectAll('.' + section)
92136                         .data(issues.length ? [0] : []);
92137
92138                     container.exit()
92139                         .remove();
92140
92141                     var containerEnter = container.enter()
92142                         .append('div')
92143                         .attr('class', 'modal-section ' + section + ' fillL2');
92144
92145                     containerEnter
92146                         .append('h3')
92147                         .text(severity === 'warning' ? _t('commit.warnings') : _t('commit.errors'));
92148
92149                     containerEnter
92150                         .append('ul')
92151                         .attr('class', 'changeset-list');
92152
92153                     container = containerEnter
92154                         .merge(container);
92155
92156
92157                     var items = container.select('ul').selectAll('li')
92158                         .data(issues, function(d) { return d.id; });
92159
92160                     items.exit()
92161                         .remove();
92162
92163                     var itemsEnter = items.enter()
92164                         .append('li')
92165                         .attr('class', issueItem);
92166
92167                     itemsEnter
92168                         .call(svgIcon('#iD-icon-alert', 'pre-text'));
92169
92170                     itemsEnter
92171                         .append('strong')
92172                         .attr('class', 'issue-message');
92173
92174                     itemsEnter.filter(function(d) { return d.tooltip; })
92175                         .call(uiTooltip()
92176                             .title(function(d) { return d.tooltip; })
92177                             .placement('top')
92178                         );
92179
92180                     items = itemsEnter
92181                         .merge(items);
92182
92183                     items.selectAll('.issue-message')
92184                         .text(function(d) {
92185                             return d.message(context);
92186                         });
92187
92188                     items
92189                         .on('mouseover', function(d) {
92190                             if (d.entityIds) {
92191                                 context.surface().selectAll(
92192                                     utilEntityOrMemberSelector(
92193                                         d.entityIds,
92194                                         context.graph()
92195                                     )
92196                                 ).classed('hover', true);
92197                             }
92198                         })
92199                         .on('mouseout', function() {
92200                             context.surface().selectAll('.hover')
92201                                 .classed('hover', false);
92202                         })
92203                         .on('click', function(d) {
92204                             context.validator().focusIssue(d);
92205                         });
92206                 }
92207             }
92208
92209
92210             return commitWarnings;
92211         }
92212
92213         var readOnlyTags = [
92214             /^changesets_count$/,
92215             /^created_by$/,
92216             /^ideditor:/,
92217             /^imagery_used$/,
92218             /^host$/,
92219             /^locale$/,
92220             /^warnings:/,
92221             /^resolved:/,
92222             /^closed:note$/,
92223             /^closed:keepright$/,
92224             /^closed:improveosm:/,
92225             /^closed:osmose:/
92226         ];
92227
92228         // treat most punctuation (except -, _, +, &) as hashtag delimiters - #4398
92229         // from https://stackoverflow.com/a/25575009
92230         var hashtagRegex = /(#[^\u2000-\u206F\u2E00-\u2E7F\s\\'!"#$%()*,.\/:;<=>?@\[\]^`{|}~]+)/g;
92231
92232
92233         function uiCommit(context) {
92234             var dispatch$1 = dispatch('cancel');
92235             var _userDetails;
92236             var _selection;
92237
92238             var changesetEditor = uiChangesetEditor(context)
92239                 .on('change', changeTags);
92240             var rawTagEditor = uiSectionRawTagEditor('changeset-tag-editor', context)
92241                 .on('change', changeTags)
92242                 .readOnlyTags(readOnlyTags);
92243             var commitChanges = uiSectionChanges(context);
92244             var commitWarnings = uiCommitWarnings(context);
92245
92246
92247             function commit(selection) {
92248                 _selection = selection;
92249
92250                 // Initialize changeset if one does not exist yet.
92251                 if (!context.changeset) initChangeset();
92252
92253                 loadDerivedChangesetTags();
92254
92255                 selection.call(render);
92256             }
92257
92258             function initChangeset() {
92259
92260                 // expire stored comment, hashtags, source after cutoff datetime - #3947 #4899
92261                 var commentDate = +corePreferences('commentDate') || 0;
92262                 var currDate = Date.now();
92263                 var cutoff = 2 * 86400 * 1000;   // 2 days
92264                 if (commentDate > currDate || currDate - commentDate > cutoff) {
92265                     corePreferences('comment', null);
92266                     corePreferences('hashtags', null);
92267                     corePreferences('source', null);
92268                 }
92269
92270                 // load in explicitly-set values, if any
92271                 if (context.defaultChangesetComment()) {
92272                     corePreferences('comment', context.defaultChangesetComment());
92273                     corePreferences('commentDate', Date.now());
92274                 }
92275                 if (context.defaultChangesetSource()) {
92276                     corePreferences('source', context.defaultChangesetSource());
92277                     corePreferences('commentDate', Date.now());
92278                 }
92279                 if (context.defaultChangesetHashtags()) {
92280                     corePreferences('hashtags', context.defaultChangesetHashtags());
92281                     corePreferences('commentDate', Date.now());
92282                 }
92283
92284                 var detected = utilDetect();
92285                 var tags = {
92286                     comment: corePreferences('comment') || '',
92287                     created_by: context.cleanTagValue('iD ' + context.version),
92288                     host: context.cleanTagValue(detected.host),
92289                     locale: context.cleanTagValue(_mainLocalizer.localeCode())
92290                 };
92291
92292                 // call findHashtags initially - this will remove stored
92293                 // hashtags if any hashtags are found in the comment - #4304
92294                 findHashtags(tags, true);
92295
92296                 var hashtags = corePreferences('hashtags');
92297                 if (hashtags) {
92298                     tags.hashtags = hashtags;
92299                 }
92300
92301                 var source = corePreferences('source');
92302                 if (source) {
92303                     tags.source = source;
92304                 }
92305                 var photoOverlaysUsed = context.history().photoOverlaysUsed();
92306                 if (photoOverlaysUsed.length) {
92307                     var sources = (tags.source || '').split(';');
92308
92309                     // include this tag for any photo layer
92310                     if (sources.indexOf('streetlevel imagery') === -1) {
92311                         sources.push('streetlevel imagery');
92312                     }
92313
92314                     // add the photo overlays used during editing as sources
92315                     photoOverlaysUsed.forEach(function(photoOverlay) {
92316                         if (sources.indexOf(photoOverlay) === -1) {
92317                             sources.push(photoOverlay);
92318                         }
92319                     });
92320
92321                     tags.source = context.cleanTagValue(sources.join(';'));
92322                 }
92323
92324                 context.changeset = new osmChangeset({ tags: tags });
92325             }
92326
92327             // Calculates read-only metadata tags based on the user's editing session and applies
92328             // them to the changeset.
92329             function loadDerivedChangesetTags() {
92330
92331                 var osm = context.connection();
92332                 if (!osm) return;
92333
92334                 var tags = Object.assign({}, context.changeset.tags);   // shallow copy
92335
92336                 // assign tags for imagery used
92337                 var imageryUsed = context.cleanTagValue(context.history().imageryUsed().join(';'));
92338                 tags.imagery_used = imageryUsed || 'None';
92339
92340                 // assign tags for closed issues and notes
92341                 var osmClosed = osm.getClosedIDs();
92342                 var itemType;
92343                 if (osmClosed.length) {
92344                     tags['closed:note'] = context.cleanTagValue(osmClosed.join(';'));
92345                 }
92346                 if (services.keepRight) {
92347                     var krClosed = services.keepRight.getClosedIDs();
92348                     if (krClosed.length) {
92349                         tags['closed:keepright'] = context.cleanTagValue(krClosed.join(';'));
92350                     }
92351                 }
92352                 if (services.improveOSM) {
92353                     var iOsmClosed = services.improveOSM.getClosedCounts();
92354                     for (itemType in iOsmClosed) {
92355                         tags['closed:improveosm:' + itemType] = context.cleanTagValue(iOsmClosed[itemType].toString());
92356                     }
92357                 }
92358                 if (services.osmose) {
92359                     var osmoseClosed = services.osmose.getClosedCounts();
92360                     for (itemType in osmoseClosed) {
92361                         tags['closed:osmose:' + itemType] = context.cleanTagValue(osmoseClosed[itemType].toString());
92362                     }
92363                 }
92364
92365                 // remove existing issue counts
92366                 for (var key in tags) {
92367                     if (key.match(/(^warnings:)|(^resolved:)/)) {
92368                         delete tags[key];
92369                     }
92370                 }
92371
92372                 function addIssueCounts(issues, prefix) {
92373                     var issuesByType = utilArrayGroupBy(issues, 'type');
92374                     for (var issueType in issuesByType) {
92375                         var issuesOfType = issuesByType[issueType];
92376                         if (issuesOfType[0].subtype) {
92377                             var issuesBySubtype = utilArrayGroupBy(issuesOfType, 'subtype');
92378                             for (var issueSubtype in issuesBySubtype) {
92379                                 var issuesOfSubtype = issuesBySubtype[issueSubtype];
92380                                 tags[prefix + ':' + issueType + ':' + issueSubtype] = context.cleanTagValue(issuesOfSubtype.length.toString());
92381                             }
92382                         } else {
92383                             tags[prefix + ':' + issueType] = context.cleanTagValue(issuesOfType.length.toString());
92384                         }
92385                     }
92386                 }
92387
92388                 // add counts of warnings generated by the user's edits
92389                 var warnings = context.validator()
92390                     .getIssuesBySeverity({ what: 'edited', where: 'all', includeIgnored: true, includeDisabledRules: true }).warning;
92391                 addIssueCounts(warnings, 'warnings');
92392
92393                 // add counts of issues resolved by the user's edits
92394                 var resolvedIssues = context.validator().getResolvedIssues();
92395                 addIssueCounts(resolvedIssues, 'resolved');
92396
92397                 context.changeset = context.changeset.update({ tags: tags });
92398             }
92399
92400             function render(selection) {
92401
92402                 var osm = context.connection();
92403                 if (!osm) return;
92404
92405                 var header = selection.selectAll('.header')
92406                     .data([0]);
92407
92408                 var headerTitle = header.enter()
92409                     .append('div')
92410                     .attr('class', 'header fillL header-container');
92411
92412                 headerTitle
92413                     .append('div')
92414                     .attr('class', 'header-block header-block-outer');
92415
92416                 headerTitle
92417                     .append('div')
92418                     .attr('class', 'header-block')
92419                     .append('h3')
92420                     .text(_t('commit.title'));
92421
92422                 headerTitle
92423                     .append('div')
92424                     .attr('class', 'header-block header-block-outer header-block-close')
92425                     .append('button')
92426                     .attr('class', 'close')
92427                     .on('click', function() {
92428                         dispatch$1.call('cancel', this);
92429                     })
92430                     .call(svgIcon('#iD-icon-close'));
92431
92432                 var body = selection.selectAll('.body')
92433                     .data([0]);
92434
92435                 body = body.enter()
92436                     .append('div')
92437                     .attr('class', 'body')
92438                     .merge(body);
92439
92440
92441                 // Changeset Section
92442                 var changesetSection = body.selectAll('.changeset-editor')
92443                     .data([0]);
92444
92445                 changesetSection = changesetSection.enter()
92446                     .append('div')
92447                     .attr('class', 'modal-section changeset-editor')
92448                     .merge(changesetSection);
92449
92450                 changesetSection
92451                     .call(changesetEditor
92452                         .changesetID(context.changeset.id)
92453                         .tags(context.changeset.tags)
92454                     );
92455
92456
92457                 // Warnings
92458                 body.call(commitWarnings);
92459
92460
92461                 // Upload Explanation
92462                 var saveSection = body.selectAll('.save-section')
92463                     .data([0]);
92464
92465                 saveSection = saveSection.enter()
92466                     .append('div')
92467                     .attr('class','modal-section save-section fillL')
92468                     .merge(saveSection);
92469
92470                 var prose = saveSection.selectAll('.commit-info')
92471                     .data([0]);
92472
92473                 if (prose.enter().size()) {   // first time, make sure to update user details in prose
92474                     _userDetails = null;
92475                 }
92476
92477                 prose = prose.enter()
92478                     .append('p')
92479                     .attr('class', 'commit-info')
92480                     .text(_t('commit.upload_explanation'))
92481                     .merge(prose);
92482
92483                 // always check if this has changed, but only update prose.html()
92484                 // if needed, because it can trigger a style recalculation
92485                 osm.userDetails(function(err, user) {
92486                     if (err) return;
92487
92488                     if (_userDetails === user) return;  // no change
92489                     _userDetails = user;
92490
92491                     var userLink = select(document.createElement('div'));
92492
92493                     if (user.image_url) {
92494                         userLink
92495                             .append('img')
92496                             .attr('src', user.image_url)
92497                             .attr('class', 'icon pre-text user-icon');
92498                     }
92499
92500                     userLink
92501                         .append('a')
92502                         .attr('class', 'user-info')
92503                         .text(user.display_name)
92504                         .attr('href', osm.userURL(user.display_name))
92505                         .attr('target', '_blank');
92506
92507                     prose
92508                         .html(_t('commit.upload_explanation_with_user', { user: userLink.html() }));
92509                 });
92510
92511
92512                 // Request Review
92513                 var requestReview = saveSection.selectAll('.request-review')
92514                     .data([0]);
92515
92516                 // Enter
92517                 var requestReviewEnter = requestReview.enter()
92518                     .append('div')
92519                     .attr('class', 'request-review');
92520
92521                 var requestReviewDomId = utilUniqueDomId('commit-input-request-review');
92522
92523                 var labelEnter = requestReviewEnter
92524                     .append('label')
92525                     .attr('for', requestReviewDomId);
92526
92527                 labelEnter
92528                     .append('input')
92529                     .attr('type', 'checkbox')
92530                     .attr('id', requestReviewDomId);
92531
92532                 labelEnter
92533                     .append('span')
92534                     .text(_t('commit.request_review'));
92535
92536                 // Update
92537                 requestReview = requestReview
92538                     .merge(requestReviewEnter);
92539
92540                 var requestReviewInput = requestReview.selectAll('input')
92541                     .property('checked', isReviewRequested(context.changeset.tags))
92542                     .on('change', toggleRequestReview);
92543
92544
92545                 // Buttons
92546                 var buttonSection = saveSection.selectAll('.buttons')
92547                     .data([0]);
92548
92549                 // enter
92550                 var buttonEnter = buttonSection.enter()
92551                     .append('div')
92552                     .attr('class', 'buttons fillL');
92553
92554                 buttonEnter
92555                     .append('button')
92556                     .attr('class', 'secondary-action button cancel-button')
92557                     .append('span')
92558                     .attr('class', 'label')
92559                     .text(_t('commit.cancel'));
92560
92561                 var uploadButton = buttonEnter
92562                     .append('button')
92563                     .attr('class', 'action button save-button');
92564
92565                 uploadButton.append('span')
92566                     .attr('class', 'label')
92567                     .text(_t('commit.save'));
92568
92569                 var uploadBlockerTooltipText = getUploadBlockerMessage();
92570
92571                 // update
92572                 buttonSection = buttonSection
92573                     .merge(buttonEnter);
92574
92575                 buttonSection.selectAll('.cancel-button')
92576                     .on('click.cancel', function() {
92577                         dispatch$1.call('cancel', this);
92578                     });
92579
92580                 buttonSection.selectAll('.save-button')
92581                     .classed('disabled', uploadBlockerTooltipText !== null)
92582                     .on('click.save', function() {
92583                         if (!select(this).classed('disabled')) {
92584                             this.blur();    // avoid keeping focus on the button - #4641
92585                             context.uploader().save(context.changeset);
92586                         }
92587                     });
92588
92589                 // remove any existing tooltip
92590                 uiTooltip().destroyAny(buttonSection.selectAll('.save-button'));
92591
92592                 if (uploadBlockerTooltipText) {
92593                     buttonSection.selectAll('.save-button')
92594                         .call(uiTooltip().title(uploadBlockerTooltipText).placement('top'));
92595                 }
92596
92597                 // Raw Tag Editor
92598                 var tagSection = body.selectAll('.tag-section.raw-tag-editor')
92599                     .data([0]);
92600
92601                 tagSection = tagSection.enter()
92602                     .append('div')
92603                     .attr('class', 'modal-section tag-section raw-tag-editor')
92604                     .merge(tagSection);
92605
92606                 tagSection
92607                     .call(rawTagEditor
92608                         .tags(Object.assign({}, context.changeset.tags))   // shallow copy
92609                         .render
92610                     );
92611
92612                 var changesSection = body.selectAll('.commit-changes-section')
92613                     .data([0]);
92614
92615                 changesSection = changesSection.enter()
92616                     .append('div')
92617                     .attr('class', 'modal-section commit-changes-section')
92618                     .merge(changesSection);
92619
92620                 // Change summary
92621                 changesSection.call(commitChanges.render);
92622
92623
92624                 function toggleRequestReview() {
92625                     var rr = requestReviewInput.property('checked');
92626                     updateChangeset({ review_requested: (rr ? 'yes' : undefined) });
92627
92628                     tagSection
92629                         .call(rawTagEditor
92630                             .tags(Object.assign({}, context.changeset.tags))   // shallow copy
92631                             .render
92632                         );
92633                 }
92634             }
92635
92636
92637             function getUploadBlockerMessage() {
92638                 var errors = context.validator()
92639                     .getIssuesBySeverity({ what: 'edited', where: 'all' }).error;
92640
92641                 if (errors.length) {
92642                     return _t('commit.outstanding_errors_message', { count: errors.length });
92643
92644                 } else {
92645                     var hasChangesetComment = context.changeset && context.changeset.tags.comment && context.changeset.tags.comment.trim().length;
92646                     if (!hasChangesetComment) {
92647                         return _t('commit.comment_needed_message');
92648                     }
92649                 }
92650                 return null;
92651             }
92652
92653
92654             function changeTags(_, changed, onInput) {
92655                 if (changed.hasOwnProperty('comment')) {
92656                     if (changed.comment === undefined) {
92657                         changed.comment = '';
92658                     }
92659                     if (!onInput) {
92660                         corePreferences('comment', changed.comment);
92661                         corePreferences('commentDate', Date.now());
92662                     }
92663                 }
92664                 if (changed.hasOwnProperty('source')) {
92665                     if (changed.source === undefined) {
92666                         corePreferences('source', null);
92667                     } else if (!onInput) {
92668                         corePreferences('source', changed.source);
92669                         corePreferences('commentDate', Date.now());
92670                     }
92671                 }
92672                 // no need to update `prefs` for `hashtags` here since it's done in `updateChangeset`
92673
92674                 updateChangeset(changed, onInput);
92675
92676                 if (_selection) {
92677                     _selection.call(render);
92678                 }
92679             }
92680
92681
92682             function findHashtags(tags, commentOnly) {
92683                 var detectedHashtags = commentHashtags();
92684
92685                 if (detectedHashtags.length) {
92686                     // always remove stored hashtags if there are hashtags in the comment - #4304
92687                     corePreferences('hashtags', null);
92688                 }
92689                 if (!detectedHashtags.length || !commentOnly) {
92690                     detectedHashtags = detectedHashtags.concat(hashtagHashtags());
92691                 }
92692
92693                 var allLowerCase = new Set();
92694                 return detectedHashtags.filter(function(hashtag) {
92695                     // Compare tags as lowercase strings, but keep original case tags
92696                     var lowerCase = hashtag.toLowerCase();
92697                     if (!allLowerCase.has(lowerCase)) {
92698                         allLowerCase.add(lowerCase);
92699                         return true;
92700                     }
92701                     return false;
92702                 });
92703
92704                 // Extract hashtags from `comment`
92705                 function commentHashtags() {
92706                     var matches = (tags.comment || '')
92707                         .replace(/http\S*/g, '')  // drop anything that looks like a URL - #4289
92708                         .match(hashtagRegex);
92709
92710                     return matches || [];
92711                 }
92712
92713                 // Extract and clean hashtags from `hashtags`
92714                 function hashtagHashtags() {
92715                     var matches = (tags.hashtags || '')
92716                         .split(/[,;\s]+/)
92717                         .map(function (s) {
92718                             if (s[0] !== '#') { s = '#' + s; }    // prepend '#'
92719                             var matched = s.match(hashtagRegex);
92720                             return matched && matched[0];
92721                         }).filter(Boolean);                       // exclude falsy
92722
92723                     return matches || [];
92724                 }
92725             }
92726
92727
92728             function isReviewRequested(tags) {
92729                 var rr = tags.review_requested;
92730                 if (rr === undefined) return false;
92731                 rr = rr.trim().toLowerCase();
92732                 return !(rr === '' || rr === 'no');
92733             }
92734
92735
92736             function updateChangeset(changed, onInput) {
92737                 var tags = Object.assign({}, context.changeset.tags);   // shallow copy
92738
92739                 Object.keys(changed).forEach(function(k) {
92740                     var v = changed[k];
92741                     k = context.cleanTagKey(k);
92742                     if (readOnlyTags.indexOf(k) !== -1) return;
92743
92744                     if (k !== '' && v !== undefined) {
92745                         if (onInput) {
92746                             tags[k] = v;
92747                         } else {
92748                             tags[k] = context.cleanTagValue(v);
92749                         }
92750                     } else {
92751                         delete tags[k];
92752                     }
92753                 });
92754
92755                 if (!onInput) {
92756                     // when changing the comment, override hashtags with any found in comment.
92757                     var commentOnly = changed.hasOwnProperty('comment') && (changed.comment !== '');
92758                     var arr = findHashtags(tags, commentOnly);
92759                     if (arr.length) {
92760                         tags.hashtags = context.cleanTagValue(arr.join(';'));
92761                         corePreferences('hashtags', tags.hashtags);
92762                     } else {
92763                         delete tags.hashtags;
92764                         corePreferences('hashtags', null);
92765                     }
92766                 }
92767
92768                 // always update userdetails, just in case user reauthenticates as someone else
92769                 if (_userDetails && _userDetails.changesets_count !== undefined) {
92770                     var changesetsCount = parseInt(_userDetails.changesets_count, 10) + 1;  // #4283
92771                     tags.changesets_count = String(changesetsCount);
92772
92773                     // first 100 edits - new user
92774                     if (changesetsCount <= 100) {
92775                         var s;
92776                         s = corePreferences('walkthrough_completed');
92777                         if (s) {
92778                             tags['ideditor:walkthrough_completed'] = s;
92779                         }
92780
92781                         s = corePreferences('walkthrough_progress');
92782                         if (s) {
92783                             tags['ideditor:walkthrough_progress'] = s;
92784                         }
92785
92786                         s = corePreferences('walkthrough_started');
92787                         if (s) {
92788                             tags['ideditor:walkthrough_started'] = s;
92789                         }
92790                     }
92791                 } else {
92792                     delete tags.changesets_count;
92793                 }
92794
92795                 if (!fastDeepEqual(context.changeset.tags, tags)) {
92796                     context.changeset = context.changeset.update({ tags: tags });
92797                 }
92798             }
92799
92800
92801             commit.reset = function() {
92802                 context.changeset = null;
92803             };
92804
92805
92806             return utilRebind(commit, dispatch$1, 'on');
92807         }
92808
92809         var RADIUS = 6378137;
92810         var FLATTENING = 1/298.257223563;
92811         var POLAR_RADIUS$1 = 6356752.3142;
92812
92813         var wgs84 = {
92814                 RADIUS: RADIUS,
92815                 FLATTENING: FLATTENING,
92816                 POLAR_RADIUS: POLAR_RADIUS$1
92817         };
92818
92819         var geometry_1 = geometry;
92820         var ring = ringArea;
92821
92822         function geometry(_) {
92823             var area = 0, i;
92824             switch (_.type) {
92825                 case 'Polygon':
92826                     return polygonArea(_.coordinates);
92827                 case 'MultiPolygon':
92828                     for (i = 0; i < _.coordinates.length; i++) {
92829                         area += polygonArea(_.coordinates[i]);
92830                     }
92831                     return area;
92832                 case 'Point':
92833                 case 'MultiPoint':
92834                 case 'LineString':
92835                 case 'MultiLineString':
92836                     return 0;
92837                 case 'GeometryCollection':
92838                     for (i = 0; i < _.geometries.length; i++) {
92839                         area += geometry(_.geometries[i]);
92840                     }
92841                     return area;
92842             }
92843         }
92844
92845         function polygonArea(coords) {
92846             var area = 0;
92847             if (coords && coords.length > 0) {
92848                 area += Math.abs(ringArea(coords[0]));
92849                 for (var i = 1; i < coords.length; i++) {
92850                     area -= Math.abs(ringArea(coords[i]));
92851                 }
92852             }
92853             return area;
92854         }
92855
92856         /**
92857          * Calculate the approximate area of the polygon were it projected onto
92858          *     the earth.  Note that this area will be positive if ring is oriented
92859          *     clockwise, otherwise it will be negative.
92860          *
92861          * Reference:
92862          * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
92863          *     Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
92864          *     Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
92865          *
92866          * Returns:
92867          * {float} The approximate signed geodesic area of the polygon in square
92868          *     meters.
92869          */
92870
92871         function ringArea(coords) {
92872             var p1, p2, p3, lowerIndex, middleIndex, upperIndex, i,
92873             area = 0,
92874             coordsLength = coords.length;
92875
92876             if (coordsLength > 2) {
92877                 for (i = 0; i < coordsLength; i++) {
92878                     if (i === coordsLength - 2) {// i = N-2
92879                         lowerIndex = coordsLength - 2;
92880                         middleIndex = coordsLength -1;
92881                         upperIndex = 0;
92882                     } else if (i === coordsLength - 1) {// i = N-1
92883                         lowerIndex = coordsLength - 1;
92884                         middleIndex = 0;
92885                         upperIndex = 1;
92886                     } else { // i = 0 to N-3
92887                         lowerIndex = i;
92888                         middleIndex = i+1;
92889                         upperIndex = i+2;
92890                     }
92891                     p1 = coords[lowerIndex];
92892                     p2 = coords[middleIndex];
92893                     p3 = coords[upperIndex];
92894                     area += ( rad(p3[0]) - rad(p1[0]) ) * Math.sin( rad(p2[1]));
92895                 }
92896
92897                 area = area * wgs84.RADIUS * wgs84.RADIUS / 2;
92898             }
92899
92900             return area;
92901         }
92902
92903         function rad(_) {
92904             return _ * Math.PI / 180;
92905         }
92906
92907         var geojsonArea = {
92908                 geometry: geometry_1,
92909                 ring: ring
92910         };
92911
92912         function toRadians(angleInDegrees) {
92913           return (angleInDegrees * Math.PI) / 180;
92914         }
92915
92916         function toDegrees(angleInRadians) {
92917           return (angleInRadians * 180) / Math.PI;
92918         }
92919
92920         function offset(c1, distance, bearing) {
92921           var lat1 = toRadians(c1[1]);
92922           var lon1 = toRadians(c1[0]);
92923           var dByR = distance / 6378137; // distance divided by 6378137 (radius of the earth) wgs84
92924           var lat = Math.asin(
92925             Math.sin(lat1) * Math.cos(dByR) +
92926               Math.cos(lat1) * Math.sin(dByR) * Math.cos(bearing)
92927           );
92928           var lon =
92929             lon1 +
92930             Math.atan2(
92931               Math.sin(bearing) * Math.sin(dByR) * Math.cos(lat1),
92932               Math.cos(dByR) - Math.sin(lat1) * Math.sin(lat)
92933             );
92934           return [toDegrees(lon), toDegrees(lat)];
92935         }
92936
92937         function validateCenter(center) {
92938           const validCenterLengths = [2, 3];
92939           if (!Array.isArray(center) || !validCenterLengths.includes(center.length)) {
92940             throw new Error("ERROR! Center has to be an array of length two or three");
92941           }
92942           const [lng, lat] = center;
92943           if (typeof lng !== "number" || typeof lat !== "number") {
92944             throw new Error(
92945               `ERROR! Longitude and Latitude has to be numbers but where ${typeof lng} and ${typeof lat}`
92946             );
92947           }
92948           if (lng > 180 || lng < -180) {
92949             throw new Error(
92950               `ERROR! Longitude has to be between -180 and 180 but was ${lng}`
92951             );
92952           }
92953
92954           if (lat > 90 || lat < -90) {
92955             throw new Error(
92956               `ERROR! Latitude has to be between -90 and 90 but was ${lat}`
92957             );
92958           }
92959         }
92960
92961         function validateRadius(radius) {
92962           if (typeof radius !== "number") {
92963             throw new Error(
92964               `ERROR! Radius has to be a positive number but was: ${typeof radius}`
92965             );
92966           }
92967
92968           if (radius <= 0) {
92969             throw new Error(
92970               `ERROR! Radius has to be a positive number but was: ${radius}`
92971             );
92972           }
92973         }
92974
92975         function validateNumberOfSegments(numberOfSegments) {
92976           if (typeof numberOfSegments !== "number" && numberOfSegments !== undefined) {
92977             throw new Error(
92978               `ERROR! Number of segments has to be a number but was: ${typeof numberOfSegments}`
92979             );
92980           }
92981
92982           if (numberOfSegments < 3) {
92983             throw new Error(
92984               `ERROR! Number of segments has to be at least 3 but was: ${numberOfSegments}`
92985             );
92986           }
92987         }
92988
92989         function validateInput({ center, radius, numberOfSegments }) {
92990           validateCenter(center);
92991           validateRadius(radius);
92992           validateNumberOfSegments(numberOfSegments);
92993         }
92994
92995         var circleToPolygon = function circleToPolygon(center, radius, numberOfSegments) {
92996           var n = numberOfSegments ? numberOfSegments : 32;
92997
92998           // validateInput() throws error on invalid input and do nothing on valid input
92999           validateInput({ center, radius, numberOfSegments });
93000
93001           var coordinates = [];
93002           for (var i = 0; i < n; ++i) {
93003             coordinates.push(offset(center, radius, (2 * Math.PI * -i) / n));
93004           }
93005           coordinates.push(coordinates[0]);
93006
93007           return {
93008             type: "Polygon",
93009             coordinates: [coordinates]
93010           };
93011         };
93012
93013         var geojsonPrecision = createCommonjsModule(function (module) {
93014         (function() {
93015
93016           function parse(t, coordinatePrecision, extrasPrecision) {
93017
93018             function point(p) {
93019               return p.map(function(e, index) {
93020                 if (index < 2) {
93021                     return 1 * e.toFixed(coordinatePrecision);
93022                 } else {
93023                     return 1 * e.toFixed(extrasPrecision);
93024                 }
93025               });
93026             }
93027
93028             function multi(l) {
93029               return l.map(point);
93030             }
93031
93032             function poly(p) {
93033               return p.map(multi);
93034             }
93035
93036             function multiPoly(m) {
93037               return m.map(poly);
93038             }
93039
93040             function geometry(obj) {
93041               if (!obj) {
93042                 return {};
93043               }
93044               
93045               switch (obj.type) {
93046                 case "Point":
93047                   obj.coordinates = point(obj.coordinates);
93048                   return obj;
93049                 case "LineString":
93050                 case "MultiPoint":
93051                   obj.coordinates = multi(obj.coordinates);
93052                   return obj;
93053                 case "Polygon":
93054                 case "MultiLineString":
93055                   obj.coordinates = poly(obj.coordinates);
93056                   return obj;
93057                 case "MultiPolygon":
93058                   obj.coordinates = multiPoly(obj.coordinates);
93059                   return obj;
93060                 case "GeometryCollection":
93061                   obj.geometries = obj.geometries.map(geometry);
93062                   return obj;
93063                 default :
93064                   return {};
93065               }
93066             }
93067
93068             function feature(obj) {
93069               obj.geometry = geometry(obj.geometry);
93070               return obj
93071             }
93072
93073             function featureCollection(f) {
93074               f.features = f.features.map(feature);
93075               return f;
93076             }
93077
93078             function geometryCollection(g) {
93079               g.geometries = g.geometries.map(geometry);
93080               return g;
93081             }
93082
93083             if (!t) {
93084               return t;
93085             }
93086
93087             switch (t.type) {
93088               case "Feature":
93089                 return feature(t);
93090               case "GeometryCollection" :
93091                 return geometryCollection(t);
93092               case "FeatureCollection" :
93093                 return featureCollection(t);
93094               case "Point":
93095               case "LineString":
93096               case "Polygon":
93097               case "MultiPoint":
93098               case "MultiPolygon":
93099               case "MultiLineString":
93100                 return geometry(t);
93101               default :
93102                 return t;
93103             }
93104               
93105           }
93106
93107           module.exports = parse;
93108           module.exports.parse = parse;
93109
93110         }());
93111         });
93112
93113         /* Polyfill service v3.13.0
93114          * For detailed credits and licence information see http://github.com/financial-times/polyfill-service
93115          *
93116          * - Array.prototype.fill, License: CC0 */
93117
93118         if (!('fill' in Array.prototype)) {
93119           Object.defineProperty(Array.prototype, 'fill', {
93120             configurable: true,
93121             value: function fill (value) {
93122               if (this === undefined || this === null) {
93123                 throw new TypeError(this + ' is not an object')
93124               }
93125
93126               var arrayLike = Object(this);
93127
93128               var length = Math.max(Math.min(arrayLike.length, 9007199254740991), 0) || 0;
93129
93130               var relativeStart = 1 in arguments ? parseInt(Number(arguments[1]), 10) || 0 : 0;
93131
93132               relativeStart = relativeStart < 0 ? Math.max(length + relativeStart, 0) : Math.min(relativeStart, length);
93133
93134               var relativeEnd = 2 in arguments && arguments[2] !== undefined ? parseInt(Number(arguments[2]), 10) || 0 : length;
93135
93136               relativeEnd = relativeEnd < 0 ? Math.max(length + arguments[2], 0) : Math.min(relativeEnd, length);
93137
93138               while (relativeStart < relativeEnd) {
93139                 arrayLike[relativeStart] = value;
93140
93141                 ++relativeStart;
93142               }
93143
93144               return arrayLike
93145             },
93146             writable: true
93147           });
93148         }
93149
93150         /**
93151          * Polyfill for IE support
93152          */
93153         Number.isFinite = Number.isFinite || function (value) {
93154           return typeof value === 'number' && isFinite(value)
93155         };
93156
93157         Number.isInteger = Number.isInteger || function (val) {
93158           return typeof val === 'number' &&
93159           isFinite(val) &&
93160           Math.floor(val) === val
93161         };
93162
93163         Number.parseFloat = Number.parseFloat || parseFloat;
93164
93165         Number.isNaN = Number.isNaN || function (value) {
93166           return value !== value // eslint-disable-line
93167         };
93168
93169         /**
93170          * Polyfill for IE support
93171          */
93172         Math.trunc = Math.trunc || function (x) {
93173           return x < 0 ? Math.ceil(x) : Math.floor(x)
93174         };
93175
93176         var NumberUtil = function NumberUtil () {};
93177
93178         NumberUtil.prototype.interfaces_ = function interfaces_ () {
93179           return []
93180         };
93181         NumberUtil.prototype.getClass = function getClass () {
93182           return NumberUtil
93183         };
93184         NumberUtil.prototype.equalsWithTolerance = function equalsWithTolerance (x1, x2, tolerance) {
93185           return Math.abs(x1 - x2) <= tolerance
93186         };
93187
93188         var IllegalArgumentException = (function (Error) {
93189                 function IllegalArgumentException (message) {
93190                         Error.call(this, message);
93191                         this.name = 'IllegalArgumentException';
93192                         this.message = message;
93193                         this.stack = (new Error()).stack;
93194                 }
93195
93196                 if ( Error ) IllegalArgumentException.__proto__ = Error;
93197                 IllegalArgumentException.prototype = Object.create( Error && Error.prototype );
93198                 IllegalArgumentException.prototype.constructor = IllegalArgumentException;
93199
93200                 return IllegalArgumentException;
93201         }(Error));
93202
93203         var Double = function Double () {};
93204
93205         var staticAccessors$1 = { MAX_VALUE: { configurable: true } };
93206
93207         Double.isNaN = function isNaN (n) { return Number.isNaN(n) };
93208         Double.doubleToLongBits = function doubleToLongBits (n) { return n };
93209         Double.longBitsToDouble = function longBitsToDouble (n) { return n };
93210         Double.isInfinite = function isInfinite (n) { return !Number.isFinite(n) };
93211         staticAccessors$1.MAX_VALUE.get = function () { return Number.MAX_VALUE };
93212
93213         Object.defineProperties( Double, staticAccessors$1 );
93214
93215         var Comparable = function Comparable () {};
93216
93217         var Clonable = function Clonable () {};
93218
93219         var Comparator = function Comparator () {};
93220
93221         function Serializable () {}
93222
93223         // import Assert from '../util/Assert'
93224
93225         var Coordinate = function Coordinate () {
93226           this.x = null;
93227           this.y = null;
93228           this.z = null;
93229           if (arguments.length === 0) {
93230             this.x = 0.0;
93231             this.y = 0.0;
93232             this.z = Coordinate.NULL_ORDINATE;
93233           } else if (arguments.length === 1) {
93234             var c = arguments[0];
93235             this.x = c.x;
93236             this.y = c.y;
93237             this.z = c.z;
93238           } else if (arguments.length === 2) {
93239             this.x = arguments[0];
93240             this.y = arguments[1];
93241             this.z = Coordinate.NULL_ORDINATE;
93242           } else if (arguments.length === 3) {
93243             this.x = arguments[0];
93244             this.y = arguments[1];
93245             this.z = arguments[2];
93246           }
93247         };
93248
93249         var staticAccessors = { DimensionalComparator: { configurable: true },serialVersionUID: { configurable: true },NULL_ORDINATE: { configurable: true },X: { configurable: true },Y: { configurable: true },Z: { configurable: true } };
93250         Coordinate.prototype.setOrdinate = function setOrdinate (ordinateIndex, value) {
93251           switch (ordinateIndex) {
93252             case Coordinate.X:
93253               this.x = value;
93254               break
93255             case Coordinate.Y:
93256               this.y = value;
93257               break
93258             case Coordinate.Z:
93259               this.z = value;
93260               break
93261             default:
93262               throw new IllegalArgumentException('Invalid ordinate index: ' + ordinateIndex)
93263           }
93264         };
93265         Coordinate.prototype.equals2D = function equals2D () {
93266           if (arguments.length === 1) {
93267             var other = arguments[0];
93268             if (this.x !== other.x) {
93269               return false
93270             }
93271             if (this.y !== other.y) {
93272               return false
93273             }
93274             return true
93275           } else if (arguments.length === 2) {
93276             var c = arguments[0];
93277             var tolerance = arguments[1];
93278             if (!NumberUtil.equalsWithTolerance(this.x, c.x, tolerance)) {
93279               return false
93280             }
93281             if (!NumberUtil.equalsWithTolerance(this.y, c.y, tolerance)) {
93282               return false
93283             }
93284             return true
93285           }
93286         };
93287         Coordinate.prototype.getOrdinate = function getOrdinate (ordinateIndex) {
93288           switch (ordinateIndex) {
93289             case Coordinate.X:
93290               return this.x
93291             case Coordinate.Y:
93292               return this.y
93293             case Coordinate.Z:
93294               return this.z
93295           }
93296           throw new IllegalArgumentException('Invalid ordinate index: ' + ordinateIndex)
93297         };
93298         Coordinate.prototype.equals3D = function equals3D (other) {
93299           return this.x === other.x &&
93300                  this.y === other.y &&
93301                  ((this.z === other.z || Double.isNaN(this.z)) &&
93302                  Double.isNaN(other.z))
93303         };
93304         Coordinate.prototype.equals = function equals (other) {
93305           if (!(other instanceof Coordinate)) {
93306             return false
93307           }
93308           return this.equals2D(other)
93309         };
93310         Coordinate.prototype.equalInZ = function equalInZ (c, tolerance) {
93311           return NumberUtil.equalsWithTolerance(this.z, c.z, tolerance)
93312         };
93313         Coordinate.prototype.compareTo = function compareTo (o) {
93314           var other = o;
93315           if (this.x < other.x) { return -1 }
93316           if (this.x > other.x) { return 1 }
93317           if (this.y < other.y) { return -1 }
93318           if (this.y > other.y) { return 1 }
93319           return 0
93320         };
93321         Coordinate.prototype.clone = function clone () {
93322           // try {
93323           // var coord = null
93324           // return coord
93325           // } catch (e) {
93326           // if (e instanceof CloneNotSupportedException) {
93327           //   Assert.shouldNeverReachHere("this shouldn't happen because this class is Cloneable")
93328           //   return null
93329           // } else throw e
93330           // } finally {}
93331         };
93332         Coordinate.prototype.copy = function copy () {
93333           return new Coordinate(this)
93334         };
93335         Coordinate.prototype.toString = function toString () {
93336           return '(' + this.x + ', ' + this.y + ', ' + this.z + ')'
93337         };
93338         Coordinate.prototype.distance3D = function distance3D (c) {
93339           var dx = this.x - c.x;
93340           var dy = this.y - c.y;
93341           var dz = this.z - c.z;
93342           return Math.sqrt(dx * dx + dy * dy + dz * dz)
93343         };
93344         Coordinate.prototype.distance = function distance (c) {
93345           var dx = this.x - c.x;
93346           var dy = this.y - c.y;
93347           return Math.sqrt(dx * dx + dy * dy)
93348         };
93349         Coordinate.prototype.hashCode = function hashCode () {
93350           var result = 17;
93351           result = 37 * result + Coordinate.hashCode(this.x);
93352           result = 37 * result + Coordinate.hashCode(this.y);
93353           return result
93354         };
93355         Coordinate.prototype.setCoordinate = function setCoordinate (other) {
93356           this.x = other.x;
93357           this.y = other.y;
93358           this.z = other.z;
93359         };
93360         Coordinate.prototype.interfaces_ = function interfaces_ () {
93361           return [Comparable, Clonable, Serializable]
93362         };
93363         Coordinate.prototype.getClass = function getClass () {
93364           return Coordinate
93365         };
93366         Coordinate.hashCode = function hashCode () {
93367           if (arguments.length === 1) {
93368             var x = arguments[0];
93369             var f = Double.doubleToLongBits(x);
93370             return Math.trunc((f ^ f) >>> 32)
93371           }
93372         };
93373         staticAccessors.DimensionalComparator.get = function () { return DimensionalComparator };
93374         staticAccessors.serialVersionUID.get = function () { return 6683108902428366910 };
93375         staticAccessors.NULL_ORDINATE.get = function () { return Double.NaN };
93376         staticAccessors.X.get = function () { return 0 };
93377         staticAccessors.Y.get = function () { return 1 };
93378         staticAccessors.Z.get = function () { return 2 };
93379
93380         Object.defineProperties( Coordinate, staticAccessors );
93381
93382         var DimensionalComparator = function DimensionalComparator (dimensionsToTest) {
93383           this._dimensionsToTest = 2;
93384           if (arguments.length === 0) ; else if (arguments.length === 1) {
93385             var dimensionsToTest$1 = arguments[0];
93386             if (dimensionsToTest$1 !== 2 && dimensionsToTest$1 !== 3) { throw new IllegalArgumentException('only 2 or 3 dimensions may be specified') }
93387             this._dimensionsToTest = dimensionsToTest$1;
93388           }
93389         };
93390         DimensionalComparator.prototype.compare = function compare (o1, o2) {
93391           var c1 = o1;
93392           var c2 = o2;
93393           var compX = DimensionalComparator.compare(c1.x, c2.x);
93394           if (compX !== 0) { return compX }
93395           var compY = DimensionalComparator.compare(c1.y, c2.y);
93396           if (compY !== 0) { return compY }
93397           if (this._dimensionsToTest <= 2) { return 0 }
93398           var compZ = DimensionalComparator.compare(c1.z, c2.z);
93399           return compZ
93400         };
93401         DimensionalComparator.prototype.interfaces_ = function interfaces_ () {
93402           return [Comparator]
93403         };
93404         DimensionalComparator.prototype.getClass = function getClass () {
93405           return DimensionalComparator
93406         };
93407         DimensionalComparator.compare = function compare (a, b) {
93408           if (a < b) { return -1 }
93409           if (a > b) { return 1 }
93410           if (Double.isNaN(a)) {
93411             if (Double.isNaN(b)) { return 0 }
93412             return -1
93413           }
93414           if (Double.isNaN(b)) { return 1 }
93415           return 0
93416         };
93417
93418         // import hasInterface from '../../../../hasInterface'
93419         // import CoordinateSequence from './CoordinateSequence'
93420
93421         var CoordinateSequenceFactory = function CoordinateSequenceFactory () {};
93422
93423         CoordinateSequenceFactory.prototype.create = function create () {
93424           // if (arguments.length === 1) {
93425           // if (arguments[0] instanceof Array) {
93426           //   let coordinates = arguments[0]
93427           // } else if (hasInterface(arguments[0], CoordinateSequence)) {
93428           //   let coordSeq = arguments[0]
93429           // }
93430           // } else if (arguments.length === 2) {
93431           // let size = arguments[0]
93432           // let dimension = arguments[1]
93433           // }
93434         };
93435         CoordinateSequenceFactory.prototype.interfaces_ = function interfaces_ () {
93436           return []
93437         };
93438         CoordinateSequenceFactory.prototype.getClass = function getClass () {
93439           return CoordinateSequenceFactory
93440         };
93441
93442         var Location = function Location () {};
93443
93444         var staticAccessors$4 = { INTERIOR: { configurable: true },BOUNDARY: { configurable: true },EXTERIOR: { configurable: true },NONE: { configurable: true } };
93445
93446         Location.prototype.interfaces_ = function interfaces_ () {
93447           return []
93448         };
93449         Location.prototype.getClass = function getClass () {
93450           return Location
93451         };
93452         Location.toLocationSymbol = function toLocationSymbol (locationValue) {
93453           switch (locationValue) {
93454             case Location.EXTERIOR:
93455               return 'e'
93456             case Location.BOUNDARY:
93457               return 'b'
93458             case Location.INTERIOR:
93459               return 'i'
93460             case Location.NONE:
93461               return '-'
93462           }
93463           throw new IllegalArgumentException('Unknown location value: ' + locationValue)
93464         };
93465         staticAccessors$4.INTERIOR.get = function () { return 0 };
93466         staticAccessors$4.BOUNDARY.get = function () { return 1 };
93467         staticAccessors$4.EXTERIOR.get = function () { return 2 };
93468         staticAccessors$4.NONE.get = function () { return -1 };
93469
93470         Object.defineProperties( Location, staticAccessors$4 );
93471
93472         var hasInterface = function (o, i) {
93473           return o.interfaces_ && o.interfaces_().indexOf(i) > -1
93474         };
93475
93476         var MathUtil = function MathUtil () {};
93477
93478         var staticAccessors$5 = { LOG_10: { configurable: true } };
93479
93480         MathUtil.prototype.interfaces_ = function interfaces_ () {
93481           return []
93482         };
93483         MathUtil.prototype.getClass = function getClass () {
93484           return MathUtil
93485         };
93486         MathUtil.log10 = function log10 (x) {
93487           var ln = Math.log(x);
93488           if (Double.isInfinite(ln)) { return ln }
93489           if (Double.isNaN(ln)) { return ln }
93490           return ln / MathUtil.LOG_10
93491         };
93492         MathUtil.min = function min (v1, v2, v3, v4) {
93493           var min = v1;
93494           if (v2 < min) { min = v2; }
93495           if (v3 < min) { min = v3; }
93496           if (v4 < min) { min = v4; }
93497           return min
93498         };
93499         MathUtil.clamp = function clamp () {
93500           if (typeof arguments[2] === 'number' && (typeof arguments[0] === 'number' && typeof arguments[1] === 'number')) {
93501             var x = arguments[0];
93502             var min = arguments[1];
93503             var max = arguments[2];
93504             if (x < min) { return min }
93505             if (x > max) { return max }
93506             return x
93507           } else if (Number.isInteger(arguments[2]) && (Number.isInteger(arguments[0]) && Number.isInteger(arguments[1]))) {
93508             var x$1 = arguments[0];
93509             var min$1 = arguments[1];
93510             var max$1 = arguments[2];
93511             if (x$1 < min$1) { return min$1 }
93512             if (x$1 > max$1) { return max$1 }
93513             return x$1
93514           }
93515         };
93516         MathUtil.wrap = function wrap (index, max) {
93517           if (index < 0) {
93518             return max - -index % max
93519           }
93520           return index % max
93521         };
93522         MathUtil.max = function max () {
93523           if (arguments.length === 3) {
93524             var v1 = arguments[0];
93525             var v2 = arguments[1];
93526             var v3 = arguments[2];
93527             var max = v1;
93528             if (v2 > max) { max = v2; }
93529             if (v3 > max) { max = v3; }
93530             return max
93531           } else if (arguments.length === 4) {
93532             var v1$1 = arguments[0];
93533             var v2$1 = arguments[1];
93534             var v3$1 = arguments[2];
93535             var v4 = arguments[3];
93536             var max$1 = v1$1;
93537             if (v2$1 > max$1) { max$1 = v2$1; }
93538             if (v3$1 > max$1) { max$1 = v3$1; }
93539             if (v4 > max$1) { max$1 = v4; }
93540             return max$1
93541           }
93542         };
93543         MathUtil.average = function average (x1, x2) {
93544           return (x1 + x2) / 2.0
93545         };
93546         staticAccessors$5.LOG_10.get = function () { return Math.log(10) };
93547
93548         Object.defineProperties( MathUtil, staticAccessors$5 );
93549
93550         var StringBuffer = function StringBuffer (str) {
93551           this.str = str;
93552         };
93553         StringBuffer.prototype.append = function append (e) {
93554           this.str += e;
93555         };
93556
93557         StringBuffer.prototype.setCharAt = function setCharAt (i, c) {
93558           this.str = this.str.substr(0, i) + c + this.str.substr(i + 1);
93559         };
93560
93561         StringBuffer.prototype.toString = function toString (e) {
93562           return this.str
93563         };
93564
93565         var Integer = function Integer (value) {
93566           this.value = value;
93567         };
93568         Integer.prototype.intValue = function intValue () {
93569           return this.value
93570         };
93571         Integer.prototype.compareTo = function compareTo (o) {
93572           if (this.value < o) { return -1 }
93573           if (this.value > o) { return 1 }
93574           return 0
93575         };
93576         Integer.isNaN = function isNaN (n) { return Number.isNaN(n) };
93577
93578         var Character = function Character () {};
93579
93580         Character.isWhitespace = function isWhitespace (c) { return ((c <= 32 && c >= 0) || c === 127) };
93581         Character.toUpperCase = function toUpperCase (c) { return c.toUpperCase() };
93582
93583         var DD = function DD () {
93584           this._hi = 0.0;
93585           this._lo = 0.0;
93586           if (arguments.length === 0) {
93587             this.init(0.0);
93588           } else if (arguments.length === 1) {
93589             if (typeof arguments[0] === 'number') {
93590               var x = arguments[0];
93591               this.init(x);
93592             } else if (arguments[0] instanceof DD) {
93593               var dd = arguments[0];
93594               this.init(dd);
93595             } else if (typeof arguments[0] === 'string') {
93596               var str = arguments[0];
93597               DD.call(this, DD.parse(str));
93598             }
93599           } else if (arguments.length === 2) {
93600             var hi = arguments[0];
93601             var lo = arguments[1];
93602             this.init(hi, lo);
93603           }
93604         };
93605
93606         var staticAccessors$7 = { PI: { configurable: true },TWO_PI: { configurable: true },PI_2: { configurable: true },E: { configurable: true },NaN: { configurable: true },EPS: { configurable: true },SPLIT: { configurable: true },MAX_PRINT_DIGITS: { configurable: true },TEN: { configurable: true },ONE: { configurable: true },SCI_NOT_EXPONENT_CHAR: { configurable: true },SCI_NOT_ZERO: { configurable: true } };
93607         DD.prototype.le = function le (y) {
93608           return (this._hi < y._hi || this._hi === y._hi) && this._lo <= y._lo
93609         };
93610         DD.prototype.extractSignificantDigits = function extractSignificantDigits (insertDecimalPoint, magnitude) {
93611           var y = this.abs();
93612           var mag = DD.magnitude(y._hi);
93613           var scale = DD.TEN.pow(mag);
93614           y = y.divide(scale);
93615           if (y.gt(DD.TEN)) {
93616             y = y.divide(DD.TEN);
93617             mag += 1;
93618           } else if (y.lt(DD.ONE)) {
93619             y = y.multiply(DD.TEN);
93620             mag -= 1;
93621           }
93622           var decimalPointPos = mag + 1;
93623           var buf = new StringBuffer();
93624           var numDigits = DD.MAX_PRINT_DIGITS - 1;
93625           for (var i = 0; i <= numDigits; i++) {
93626             if (insertDecimalPoint && i === decimalPointPos) {
93627               buf.append('.');
93628             }
93629             var digit = Math.trunc(y._hi);
93630             if (digit < 0) {
93631               break
93632             }
93633             var rebiasBy10 = false;
93634             var digitChar = 0;
93635             if (digit > 9) {
93636               rebiasBy10 = true;
93637               digitChar = '9';
93638             } else {
93639               digitChar = '0' + digit;
93640             }
93641             buf.append(digitChar);
93642             y = y.subtract(DD.valueOf(digit)).multiply(DD.TEN);
93643             if (rebiasBy10) { y.selfAdd(DD.TEN); }
93644             var continueExtractingDigits = true;
93645             var remMag = DD.magnitude(y._hi);
93646             if (remMag < 0 && Math.abs(remMag) >= numDigits - i) { continueExtractingDigits = false; }
93647             if (!continueExtractingDigits) { break }
93648           }
93649           magnitude[0] = mag;
93650           return buf.toString()
93651         };
93652         DD.prototype.sqr = function sqr () {
93653           return this.multiply(this)
93654         };
93655         DD.prototype.doubleValue = function doubleValue () {
93656           return this._hi + this._lo
93657         };
93658         DD.prototype.subtract = function subtract () {
93659           if (arguments[0] instanceof DD) {
93660             var y = arguments[0];
93661             return this.add(y.negate())
93662           } else if (typeof arguments[0] === 'number') {
93663             var y$1 = arguments[0];
93664             return this.add(-y$1)
93665           }
93666         };
93667         DD.prototype.equals = function equals () {
93668           if (arguments.length === 1) {
93669             var y = arguments[0];
93670             return this._hi === y._hi && this._lo === y._lo
93671           }
93672         };
93673         DD.prototype.isZero = function isZero () {
93674           return this._hi === 0.0 && this._lo === 0.0
93675         };
93676         DD.prototype.selfSubtract = function selfSubtract () {
93677           if (arguments[0] instanceof DD) {
93678             var y = arguments[0];
93679             if (this.isNaN()) { return this }
93680             return this.selfAdd(-y._hi, -y._lo)
93681           } else if (typeof arguments[0] === 'number') {
93682             var y$1 = arguments[0];
93683             if (this.isNaN()) { return this }
93684             return this.selfAdd(-y$1, 0.0)
93685           }
93686         };
93687         DD.prototype.getSpecialNumberString = function getSpecialNumberString () {
93688           if (this.isZero()) { return '0.0' }
93689           if (this.isNaN()) { return 'NaN ' }
93690           return null
93691         };
93692         DD.prototype.min = function min (x) {
93693           if (this.le(x)) {
93694             return this
93695           } else {
93696             return x
93697           }
93698         };
93699         DD.prototype.selfDivide = function selfDivide () {
93700           if (arguments.length === 1) {
93701             if (arguments[0] instanceof DD) {
93702               var y = arguments[0];
93703               return this.selfDivide(y._hi, y._lo)
93704             } else if (typeof arguments[0] === 'number') {
93705               var y$1 = arguments[0];
93706               return this.selfDivide(y$1, 0.0)
93707             }
93708           } else if (arguments.length === 2) {
93709             var yhi = arguments[0];
93710             var ylo = arguments[1];
93711             var hc = null;
93712             var tc = null;
93713             var hy = null;
93714             var ty = null;
93715             var C = null;
93716             var c = null;
93717             var U = null;
93718             var u = null;
93719             C = this._hi / yhi;
93720             c = DD.SPLIT * C;
93721             hc = c - C;
93722             u = DD.SPLIT * yhi;
93723             hc = c - hc;
93724             tc = C - hc;
93725             hy = u - yhi;
93726             U = C * yhi;
93727             hy = u - hy;
93728             ty = yhi - hy;
93729             u = hc * hy - U + hc * ty + tc * hy + tc * ty;
93730             c = (this._hi - U - u + this._lo - C * ylo) / yhi;
93731             u = C + c;
93732             this._hi = u;
93733             this._lo = C - u + c;
93734             return this
93735           }
93736         };
93737         DD.prototype.dump = function dump () {
93738           return 'DD<' + this._hi + ', ' + this._lo + '>'
93739         };
93740         DD.prototype.divide = function divide () {
93741           if (arguments[0] instanceof DD) {
93742             var y = arguments[0];
93743             var hc = null;
93744             var tc = null;
93745             var hy = null;
93746             var ty = null;
93747             var C = null;
93748             var c = null;
93749             var U = null;
93750             var u = null;
93751             C = this._hi / y._hi;
93752             c = DD.SPLIT * C;
93753             hc = c - C;
93754             u = DD.SPLIT * y._hi;
93755             hc = c - hc;
93756             tc = C - hc;
93757             hy = u - y._hi;
93758             U = C * y._hi;
93759             hy = u - hy;
93760             ty = y._hi - hy;
93761             u = hc * hy - U + hc * ty + tc * hy + tc * ty;
93762             c = (this._hi - U - u + this._lo - C * y._lo) / y._hi;
93763             u = C + c;
93764             var zhi = u;
93765             var zlo = C - u + c;
93766             return new DD(zhi, zlo)
93767           } else if (typeof arguments[0] === 'number') {
93768             var y$1 = arguments[0];
93769             if (Double.isNaN(y$1)) { return DD.createNaN() }
93770             return DD.copy(this).selfDivide(y$1, 0.0)
93771           }
93772         };
93773         DD.prototype.ge = function ge (y) {
93774           return (this._hi > y._hi || this._hi === y._hi) && this._lo >= y._lo
93775         };
93776         DD.prototype.pow = function pow (exp) {
93777           if (exp === 0.0) { return DD.valueOf(1.0) }
93778           var r = new DD(this);
93779           var s = DD.valueOf(1.0);
93780           var n = Math.abs(exp);
93781           if (n > 1) {
93782             while (n > 0) {
93783               if (n % 2 === 1) {
93784                 s.selfMultiply(r);
93785               }
93786               n /= 2;
93787               if (n > 0) { r = r.sqr(); }
93788             }
93789           } else {
93790             s = r;
93791           }
93792           if (exp < 0) { return s.reciprocal() }
93793           return s
93794         };
93795         DD.prototype.ceil = function ceil () {
93796           if (this.isNaN()) { return DD.NaN }
93797           var fhi = Math.ceil(this._hi);
93798           var flo = 0.0;
93799           if (fhi === this._hi) {
93800             flo = Math.ceil(this._lo);
93801           }
93802           return new DD(fhi, flo)
93803         };
93804         DD.prototype.compareTo = function compareTo (o) {
93805           var other = o;
93806           if (this._hi < other._hi) { return -1 }
93807           if (this._hi > other._hi) { return 1 }
93808           if (this._lo < other._lo) { return -1 }
93809           if (this._lo > other._lo) { return 1 }
93810           return 0
93811         };
93812         DD.prototype.rint = function rint () {
93813           if (this.isNaN()) { return this }
93814           var plus5 = this.add(0.5);
93815           return plus5.floor()
93816         };
93817         DD.prototype.setValue = function setValue () {
93818           if (arguments[0] instanceof DD) {
93819             var value = arguments[0];
93820             this.init(value);
93821             return this
93822           } else if (typeof arguments[0] === 'number') {
93823             var value$1 = arguments[0];
93824             this.init(value$1);
93825             return this
93826           }
93827         };
93828         DD.prototype.max = function max (x) {
93829           if (this.ge(x)) {
93830             return this
93831           } else {
93832             return x
93833           }
93834         };
93835         DD.prototype.sqrt = function sqrt () {
93836           if (this.isZero()) { return DD.valueOf(0.0) }
93837           if (this.isNegative()) {
93838             return DD.NaN
93839           }
93840           var x = 1.0 / Math.sqrt(this._hi);
93841           var ax = this._hi * x;
93842           var axdd = DD.valueOf(ax);
93843           var diffSq = this.subtract(axdd.sqr());
93844           var d2 = diffSq._hi * (x * 0.5);
93845           return axdd.add(d2)
93846         };
93847         DD.prototype.selfAdd = function selfAdd () {
93848           if (arguments.length === 1) {
93849             if (arguments[0] instanceof DD) {
93850               var y = arguments[0];
93851               return this.selfAdd(y._hi, y._lo)
93852             } else if (typeof arguments[0] === 'number') {
93853               var y$1 = arguments[0];
93854               var H = null;
93855               var h = null;
93856               var S = null;
93857               var s = null;
93858               var e = null;
93859               var f = null;
93860               S = this._hi + y$1;
93861               e = S - this._hi;
93862               s = S - e;
93863               s = y$1 - e + (this._hi - s);
93864               f = s + this._lo;
93865               H = S + f;
93866               h = f + (S - H);
93867               this._hi = H + h;
93868               this._lo = h + (H - this._hi);
93869               return this
93870             }
93871           } else if (arguments.length === 2) {
93872             var yhi = arguments[0];
93873             var ylo = arguments[1];
93874             var H$1 = null;
93875             var h$1 = null;
93876             var T = null;
93877             var t = null;
93878             var S$1 = null;
93879             var s$1 = null;
93880             var e$1 = null;
93881             var f$1 = null;
93882             S$1 = this._hi + yhi;
93883             T = this._lo + ylo;
93884             e$1 = S$1 - this._hi;
93885             f$1 = T - this._lo;
93886             s$1 = S$1 - e$1;
93887             t = T - f$1;
93888             s$1 = yhi - e$1 + (this._hi - s$1);
93889             t = ylo - f$1 + (this._lo - t);
93890             e$1 = s$1 + T;
93891             H$1 = S$1 + e$1;
93892             h$1 = e$1 + (S$1 - H$1);
93893             e$1 = t + h$1;
93894             var zhi = H$1 + e$1;
93895             var zlo = e$1 + (H$1 - zhi);
93896             this._hi = zhi;
93897             this._lo = zlo;
93898             return this
93899           }
93900         };
93901         DD.prototype.selfMultiply = function selfMultiply () {
93902           if (arguments.length === 1) {
93903             if (arguments[0] instanceof DD) {
93904               var y = arguments[0];
93905               return this.selfMultiply(y._hi, y._lo)
93906             } else if (typeof arguments[0] === 'number') {
93907               var y$1 = arguments[0];
93908               return this.selfMultiply(y$1, 0.0)
93909             }
93910           } else if (arguments.length === 2) {
93911             var yhi = arguments[0];
93912             var ylo = arguments[1];
93913             var hx = null;
93914             var tx = null;
93915             var hy = null;
93916             var ty = null;
93917             var C = null;
93918             var c = null;
93919             C = DD.SPLIT * this._hi;
93920             hx = C - this._hi;
93921             c = DD.SPLIT * yhi;
93922             hx = C - hx;
93923             tx = this._hi - hx;
93924             hy = c - yhi;
93925             C = this._hi * yhi;
93926             hy = c - hy;
93927             ty = yhi - hy;
93928             c = hx * hy - C + hx * ty + tx * hy + tx * ty + (this._hi * ylo + this._lo * yhi);
93929             var zhi = C + c;
93930             hx = C - zhi;
93931             var zlo = c + hx;
93932             this._hi = zhi;
93933             this._lo = zlo;
93934             return this
93935           }
93936         };
93937         DD.prototype.selfSqr = function selfSqr () {
93938           return this.selfMultiply(this)
93939         };
93940         DD.prototype.floor = function floor () {
93941           if (this.isNaN()) { return DD.NaN }
93942           var fhi = Math.floor(this._hi);
93943           var flo = 0.0;
93944           if (fhi === this._hi) {
93945             flo = Math.floor(this._lo);
93946           }
93947           return new DD(fhi, flo)
93948         };
93949         DD.prototype.negate = function negate () {
93950           if (this.isNaN()) { return this }
93951           return new DD(-this._hi, -this._lo)
93952         };
93953         DD.prototype.clone = function clone () {
93954           // try {
93955           // return null
93956           // } catch (ex) {
93957           // if (ex instanceof CloneNotSupportedException) {
93958           //   return null
93959           // } else throw ex
93960           // } finally {}
93961         };
93962         DD.prototype.multiply = function multiply () {
93963           if (arguments[0] instanceof DD) {
93964             var y = arguments[0];
93965             if (y.isNaN()) { return DD.createNaN() }
93966             return DD.copy(this).selfMultiply(y)
93967           } else if (typeof arguments[0] === 'number') {
93968             var y$1 = arguments[0];
93969             if (Double.isNaN(y$1)) { return DD.createNaN() }
93970             return DD.copy(this).selfMultiply(y$1, 0.0)
93971           }
93972         };
93973         DD.prototype.isNaN = function isNaN () {
93974           return Double.isNaN(this._hi)
93975         };
93976         DD.prototype.intValue = function intValue () {
93977           return Math.trunc(this._hi)
93978         };
93979         DD.prototype.toString = function toString () {
93980           var mag = DD.magnitude(this._hi);
93981           if (mag >= -3 && mag <= 20) { return this.toStandardNotation() }
93982           return this.toSciNotation()
93983         };
93984         DD.prototype.toStandardNotation = function toStandardNotation () {
93985           var specialStr = this.getSpecialNumberString();
93986           if (specialStr !== null) { return specialStr }
93987           var magnitude = new Array(1).fill(null);
93988           var sigDigits = this.extractSignificantDigits(true, magnitude);
93989           var decimalPointPos = magnitude[0] + 1;
93990           var num = sigDigits;
93991           if (sigDigits.charAt(0) === '.') {
93992             num = '0' + sigDigits;
93993           } else if (decimalPointPos < 0) {
93994             num = '0.' + DD.stringOfChar('0', -decimalPointPos) + sigDigits;
93995           } else if (sigDigits.indexOf('.') === -1) {
93996             var numZeroes = decimalPointPos - sigDigits.length;
93997             var zeroes = DD.stringOfChar('0', numZeroes);
93998             num = sigDigits + zeroes + '.0';
93999           }
94000           if (this.isNegative()) { return '-' + num }
94001           return num
94002         };
94003         DD.prototype.reciprocal = function reciprocal () {
94004           var hc = null;
94005           var tc = null;
94006           var hy = null;
94007           var ty = null;
94008           var C = null;
94009           var c = null;
94010           var U = null;
94011           var u = null;
94012           C = 1.0 / this._hi;
94013           c = DD.SPLIT * C;
94014           hc = c - C;
94015           u = DD.SPLIT * this._hi;
94016           hc = c - hc;
94017           tc = C - hc;
94018           hy = u - this._hi;
94019           U = C * this._hi;
94020           hy = u - hy;
94021           ty = this._hi - hy;
94022           u = hc * hy - U + hc * ty + tc * hy + tc * ty;
94023           c = (1.0 - U - u - C * this._lo) / this._hi;
94024           var zhi = C + c;
94025           var zlo = C - zhi + c;
94026           return new DD(zhi, zlo)
94027         };
94028         DD.prototype.toSciNotation = function toSciNotation () {
94029           if (this.isZero()) { return DD.SCI_NOT_ZERO }
94030           var specialStr = this.getSpecialNumberString();
94031           if (specialStr !== null) { return specialStr }
94032           var magnitude = new Array(1).fill(null);
94033           var digits = this.extractSignificantDigits(false, magnitude);
94034           var expStr = DD.SCI_NOT_EXPONENT_CHAR + magnitude[0];
94035           if (digits.charAt(0) === '0') {
94036             throw new Error('Found leading zero: ' + digits)
94037           }
94038           var trailingDigits = '';
94039           if (digits.length > 1) { trailingDigits = digits.substring(1); }
94040           var digitsWithDecimal = digits.charAt(0) + '.' + trailingDigits;
94041           if (this.isNegative()) { return '-' + digitsWithDecimal + expStr }
94042           return digitsWithDecimal + expStr
94043         };
94044         DD.prototype.abs = function abs () {
94045           if (this.isNaN()) { return DD.NaN }
94046           if (this.isNegative()) { return this.negate() }
94047           return new DD(this)
94048         };
94049         DD.prototype.isPositive = function isPositive () {
94050           return (this._hi > 0.0 || this._hi === 0.0) && this._lo > 0.0
94051         };
94052         DD.prototype.lt = function lt (y) {
94053           return (this._hi < y._hi || this._hi === y._hi) && this._lo < y._lo
94054         };
94055         DD.prototype.add = function add () {
94056           if (arguments[0] instanceof DD) {
94057             var y = arguments[0];
94058             return DD.copy(this).selfAdd(y)
94059           } else if (typeof arguments[0] === 'number') {
94060             var y$1 = arguments[0];
94061             return DD.copy(this).selfAdd(y$1)
94062           }
94063         };
94064         DD.prototype.init = function init () {
94065           if (arguments.length === 1) {
94066             if (typeof arguments[0] === 'number') {
94067               var x = arguments[0];
94068               this._hi = x;
94069               this._lo = 0.0;
94070             } else if (arguments[0] instanceof DD) {
94071               var dd = arguments[0];
94072               this._hi = dd._hi;
94073               this._lo = dd._lo;
94074             }
94075           } else if (arguments.length === 2) {
94076             var hi = arguments[0];
94077             var lo = arguments[1];
94078             this._hi = hi;
94079             this._lo = lo;
94080           }
94081         };
94082         DD.prototype.gt = function gt (y) {
94083           return (this._hi > y._hi || this._hi === y._hi) && this._lo > y._lo
94084         };
94085         DD.prototype.isNegative = function isNegative () {
94086           return (this._hi < 0.0 || this._hi === 0.0) && this._lo < 0.0
94087         };
94088         DD.prototype.trunc = function trunc () {
94089           if (this.isNaN()) { return DD.NaN }
94090           if (this.isPositive()) { return this.floor(); } else { return this.ceil() }
94091         };
94092         DD.prototype.signum = function signum () {
94093           if (this._hi > 0) { return 1 }
94094           if (this._hi < 0) { return -1 }
94095           if (this._lo > 0) { return 1 }
94096           if (this._lo < 0) { return -1 }
94097           return 0
94098         };
94099         DD.prototype.interfaces_ = function interfaces_ () {
94100           return [Serializable, Comparable, Clonable]
94101         };
94102         DD.prototype.getClass = function getClass () {
94103           return DD
94104         };
94105         DD.sqr = function sqr (x) {
94106           return DD.valueOf(x).selfMultiply(x)
94107         };
94108         DD.valueOf = function valueOf () {
94109           if (typeof arguments[0] === 'string') {
94110             var str = arguments[0];
94111             return DD.parse(str)
94112           } else if (typeof arguments[0] === 'number') {
94113             var x = arguments[0];
94114             return new DD(x)
94115           }
94116         };
94117         DD.sqrt = function sqrt (x) {
94118           return DD.valueOf(x).sqrt()
94119         };
94120         DD.parse = function parse (str) {
94121           var i = 0;
94122           var strlen = str.length;
94123           while (Character.isWhitespace(str.charAt(i))) { i++; }
94124           var isNegative = false;
94125           if (i < strlen) {
94126             var signCh = str.charAt(i);
94127             if (signCh === '-' || signCh === '+') {
94128               i++;
94129               if (signCh === '-') { isNegative = true; }
94130             }
94131           }
94132           var val = new DD();
94133           var numDigits = 0;
94134           var numBeforeDec = 0;
94135           var exp = 0;
94136           while (true) {
94137             if (i >= strlen) { break }
94138             var ch = str.charAt(i);
94139             i++;
94140             if (Character.isDigit(ch)) {
94141               var d = ch - '0';
94142               val.selfMultiply(DD.TEN);
94143               val.selfAdd(d);
94144               numDigits++;
94145               continue
94146             }
94147             if (ch === '.') {
94148               numBeforeDec = numDigits;
94149               continue
94150             }
94151             if (ch === 'e' || ch === 'E') {
94152               var expStr = str.substring(i);
94153               try {
94154                 exp = Integer.parseInt(expStr);
94155               } catch (ex) {
94156                 if (ex instanceof Error) {
94157                   throw new Error('Invalid exponent ' + expStr + ' in string ' + str)
94158                 } else { throw ex }
94159               } finally {}
94160               break
94161             }
94162             throw new Error("Unexpected character '" + ch + "' at position " + i + ' in string ' + str)
94163           }
94164           var val2 = val;
94165           var numDecPlaces = numDigits - numBeforeDec - exp;
94166           if (numDecPlaces === 0) {
94167             val2 = val;
94168           } else if (numDecPlaces > 0) {
94169             var scale = DD.TEN.pow(numDecPlaces);
94170             val2 = val.divide(scale);
94171           } else if (numDecPlaces < 0) {
94172             var scale$1 = DD.TEN.pow(-numDecPlaces);
94173             val2 = val.multiply(scale$1);
94174           }
94175           if (isNegative) {
94176             return val2.negate()
94177           }
94178           return val2
94179         };
94180         DD.createNaN = function createNaN () {
94181           return new DD(Double.NaN, Double.NaN)
94182         };
94183         DD.copy = function copy (dd) {
94184           return new DD(dd)
94185         };
94186         DD.magnitude = function magnitude (x) {
94187           var xAbs = Math.abs(x);
94188           var xLog10 = Math.log(xAbs) / Math.log(10);
94189           var xMag = Math.trunc(Math.floor(xLog10));
94190           var xApprox = Math.pow(10, xMag);
94191           if (xApprox * 10 <= xAbs) { xMag += 1; }
94192           return xMag
94193         };
94194         DD.stringOfChar = function stringOfChar (ch, len) {
94195           var buf = new StringBuffer();
94196           for (var i = 0; i < len; i++) {
94197             buf.append(ch);
94198           }
94199           return buf.toString()
94200         };
94201         staticAccessors$7.PI.get = function () { return new DD(3.141592653589793116e+00, 1.224646799147353207e-16) };
94202         staticAccessors$7.TWO_PI.get = function () { return new DD(6.283185307179586232e+00, 2.449293598294706414e-16) };
94203         staticAccessors$7.PI_2.get = function () { return new DD(1.570796326794896558e+00, 6.123233995736766036e-17) };
94204         staticAccessors$7.E.get = function () { return new DD(2.718281828459045091e+00, 1.445646891729250158e-16) };
94205         staticAccessors$7.NaN.get = function () { return new DD(Double.NaN, Double.NaN) };
94206         staticAccessors$7.EPS.get = function () { return 1.23259516440783e-32 };
94207         staticAccessors$7.SPLIT.get = function () { return 134217729.0 };
94208         staticAccessors$7.MAX_PRINT_DIGITS.get = function () { return 32 };
94209         staticAccessors$7.TEN.get = function () { return DD.valueOf(10.0) };
94210         staticAccessors$7.ONE.get = function () { return DD.valueOf(1.0) };
94211         staticAccessors$7.SCI_NOT_EXPONENT_CHAR.get = function () { return 'E' };
94212         staticAccessors$7.SCI_NOT_ZERO.get = function () { return '0.0E0' };
94213
94214         Object.defineProperties( DD, staticAccessors$7 );
94215
94216         var CGAlgorithmsDD = function CGAlgorithmsDD () {};
94217
94218         var staticAccessors$6 = { DP_SAFE_EPSILON: { configurable: true } };
94219
94220         CGAlgorithmsDD.prototype.interfaces_ = function interfaces_ () {
94221           return []
94222         };
94223         CGAlgorithmsDD.prototype.getClass = function getClass () {
94224           return CGAlgorithmsDD
94225         };
94226         CGAlgorithmsDD.orientationIndex = function orientationIndex (p1, p2, q) {
94227           var index = CGAlgorithmsDD.orientationIndexFilter(p1, p2, q);
94228           if (index <= 1) { return index }
94229           var dx1 = DD.valueOf(p2.x).selfAdd(-p1.x);
94230           var dy1 = DD.valueOf(p2.y).selfAdd(-p1.y);
94231           var dx2 = DD.valueOf(q.x).selfAdd(-p2.x);
94232           var dy2 = DD.valueOf(q.y).selfAdd(-p2.y);
94233           return dx1.selfMultiply(dy2).selfSubtract(dy1.selfMultiply(dx2)).signum()
94234         };
94235         CGAlgorithmsDD.signOfDet2x2 = function signOfDet2x2 (x1, y1, x2, y2) {
94236           var det = x1.multiply(y2).selfSubtract(y1.multiply(x2));
94237           return det.signum()
94238         };
94239         CGAlgorithmsDD.intersection = function intersection (p1, p2, q1, q2) {
94240           var denom1 = DD.valueOf(q2.y).selfSubtract(q1.y).selfMultiply(DD.valueOf(p2.x).selfSubtract(p1.x));
94241           var denom2 = DD.valueOf(q2.x).selfSubtract(q1.x).selfMultiply(DD.valueOf(p2.y).selfSubtract(p1.y));
94242           var denom = denom1.subtract(denom2);
94243           var numx1 = DD.valueOf(q2.x).selfSubtract(q1.x).selfMultiply(DD.valueOf(p1.y).selfSubtract(q1.y));
94244           var numx2 = DD.valueOf(q2.y).selfSubtract(q1.y).selfMultiply(DD.valueOf(p1.x).selfSubtract(q1.x));
94245           var numx = numx1.subtract(numx2);
94246           var fracP = numx.selfDivide(denom).doubleValue();
94247           var x = DD.valueOf(p1.x).selfAdd(DD.valueOf(p2.x).selfSubtract(p1.x).selfMultiply(fracP)).doubleValue();
94248           var numy1 = DD.valueOf(p2.x).selfSubtract(p1.x).selfMultiply(DD.valueOf(p1.y).selfSubtract(q1.y));
94249           var numy2 = DD.valueOf(p2.y).selfSubtract(p1.y).selfMultiply(DD.valueOf(p1.x).selfSubtract(q1.x));
94250           var numy = numy1.subtract(numy2);
94251           var fracQ = numy.selfDivide(denom).doubleValue();
94252           var y = DD.valueOf(q1.y).selfAdd(DD.valueOf(q2.y).selfSubtract(q1.y).selfMultiply(fracQ)).doubleValue();
94253           return new Coordinate(x, y)
94254         };
94255         CGAlgorithmsDD.orientationIndexFilter = function orientationIndexFilter (pa, pb, pc) {
94256           var detsum = null;
94257           var detleft = (pa.x - pc.x) * (pb.y - pc.y);
94258           var detright = (pa.y - pc.y) * (pb.x - pc.x);
94259           var det = detleft - detright;
94260           if (detleft > 0.0) {
94261             if (detright <= 0.0) {
94262               return CGAlgorithmsDD.signum(det)
94263             } else {
94264               detsum = detleft + detright;
94265             }
94266           } else if (detleft < 0.0) {
94267             if (detright >= 0.0) {
94268               return CGAlgorithmsDD.signum(det)
94269             } else {
94270               detsum = -detleft - detright;
94271             }
94272           } else {
94273             return CGAlgorithmsDD.signum(det)
94274           }
94275           var errbound = CGAlgorithmsDD.DP_SAFE_EPSILON * detsum;
94276           if (det >= errbound || -det >= errbound) {
94277             return CGAlgorithmsDD.signum(det)
94278           }
94279           return 2
94280         };
94281         CGAlgorithmsDD.signum = function signum (x) {
94282           if (x > 0) { return 1 }
94283           if (x < 0) { return -1 }
94284           return 0
94285         };
94286         staticAccessors$6.DP_SAFE_EPSILON.get = function () { return 1e-15 };
94287
94288         Object.defineProperties( CGAlgorithmsDD, staticAccessors$6 );
94289
94290         var CoordinateSequence = function CoordinateSequence () {};
94291
94292         var staticAccessors$8 = { X: { configurable: true },Y: { configurable: true },Z: { configurable: true },M: { configurable: true } };
94293
94294         staticAccessors$8.X.get = function () { return 0 };
94295         staticAccessors$8.Y.get = function () { return 1 };
94296         staticAccessors$8.Z.get = function () { return 2 };
94297         staticAccessors$8.M.get = function () { return 3 };
94298         CoordinateSequence.prototype.setOrdinate = function setOrdinate (index, ordinateIndex, value) {};
94299         CoordinateSequence.prototype.size = function size () {};
94300         CoordinateSequence.prototype.getOrdinate = function getOrdinate (index, ordinateIndex) {};
94301         CoordinateSequence.prototype.getCoordinate = function getCoordinate () {};
94302         CoordinateSequence.prototype.getCoordinateCopy = function getCoordinateCopy (i) {};
94303         CoordinateSequence.prototype.getDimension = function getDimension () {};
94304         CoordinateSequence.prototype.getX = function getX (index) {};
94305         CoordinateSequence.prototype.clone = function clone () {};
94306         CoordinateSequence.prototype.expandEnvelope = function expandEnvelope (env) {};
94307         CoordinateSequence.prototype.copy = function copy () {};
94308         CoordinateSequence.prototype.getY = function getY (index) {};
94309         CoordinateSequence.prototype.toCoordinateArray = function toCoordinateArray () {};
94310         CoordinateSequence.prototype.interfaces_ = function interfaces_ () {
94311           return [Clonable]
94312         };
94313         CoordinateSequence.prototype.getClass = function getClass () {
94314           return CoordinateSequence
94315         };
94316
94317         Object.defineProperties( CoordinateSequence, staticAccessors$8 );
94318
94319         var Exception = function Exception () {};
94320
94321         var NotRepresentableException = (function (Exception$$1) {
94322           function NotRepresentableException () {
94323             Exception$$1.call(this, 'Projective point not representable on the Cartesian plane.');
94324           }
94325
94326           if ( Exception$$1 ) NotRepresentableException.__proto__ = Exception$$1;
94327           NotRepresentableException.prototype = Object.create( Exception$$1 && Exception$$1.prototype );
94328           NotRepresentableException.prototype.constructor = NotRepresentableException;
94329           NotRepresentableException.prototype.interfaces_ = function interfaces_ () {
94330             return []
94331           };
94332           NotRepresentableException.prototype.getClass = function getClass () {
94333             return NotRepresentableException
94334           };
94335
94336           return NotRepresentableException;
94337         }(Exception));
94338
94339         var System = function System () {};
94340
94341         System.arraycopy = function arraycopy (src, srcPos, dest, destPos, len) {
94342           var c = 0;
94343           for (var i = srcPos; i < srcPos + len; i++) {
94344             dest[destPos + c] = src[i];
94345             c++;
94346           }
94347         };
94348
94349         System.getProperty = function getProperty (name) {
94350           return {
94351             'line.separator': '\n'
94352           }[name]
94353         };
94354
94355         var HCoordinate = function HCoordinate () {
94356           this.x = null;
94357           this.y = null;
94358           this.w = null;
94359           if (arguments.length === 0) {
94360             this.x = 0.0;
94361             this.y = 0.0;
94362             this.w = 1.0;
94363           } else if (arguments.length === 1) {
94364             var p = arguments[0];
94365             this.x = p.x;
94366             this.y = p.y;
94367             this.w = 1.0;
94368           } else if (arguments.length === 2) {
94369             if (typeof arguments[0] === 'number' && typeof arguments[1] === 'number') {
94370               var _x = arguments[0];
94371               var _y = arguments[1];
94372               this.x = _x;
94373               this.y = _y;
94374               this.w = 1.0;
94375             } else if (arguments[0] instanceof HCoordinate && arguments[1] instanceof HCoordinate) {
94376               var p1 = arguments[0];
94377               var p2 = arguments[1];
94378               this.x = p1.y * p2.w - p2.y * p1.w;
94379               this.y = p2.x * p1.w - p1.x * p2.w;
94380               this.w = p1.x * p2.y - p2.x * p1.y;
94381             } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof Coordinate) {
94382               var p1$1 = arguments[0];
94383               var p2$1 = arguments[1];
94384               this.x = p1$1.y - p2$1.y;
94385               this.y = p2$1.x - p1$1.x;
94386               this.w = p1$1.x * p2$1.y - p2$1.x * p1$1.y;
94387             }
94388           } else if (arguments.length === 3) {
94389             var _x$1 = arguments[0];
94390             var _y$1 = arguments[1];
94391             var _w = arguments[2];
94392             this.x = _x$1;
94393             this.y = _y$1;
94394             this.w = _w;
94395           } else if (arguments.length === 4) {
94396             var p1$2 = arguments[0];
94397             var p2$2 = arguments[1];
94398             var q1 = arguments[2];
94399             var q2 = arguments[3];
94400             var px = p1$2.y - p2$2.y;
94401             var py = p2$2.x - p1$2.x;
94402             var pw = p1$2.x * p2$2.y - p2$2.x * p1$2.y;
94403             var qx = q1.y - q2.y;
94404             var qy = q2.x - q1.x;
94405             var qw = q1.x * q2.y - q2.x * q1.y;
94406             this.x = py * qw - qy * pw;
94407             this.y = qx * pw - px * qw;
94408             this.w = px * qy - qx * py;
94409           }
94410         };
94411         HCoordinate.prototype.getY = function getY () {
94412           var a = this.y / this.w;
94413           if (Double.isNaN(a) || Double.isInfinite(a)) {
94414             throw new NotRepresentableException()
94415           }
94416           return a
94417         };
94418         HCoordinate.prototype.getX = function getX () {
94419           var a = this.x / this.w;
94420           if (Double.isNaN(a) || Double.isInfinite(a)) {
94421             throw new NotRepresentableException()
94422           }
94423           return a
94424         };
94425         HCoordinate.prototype.getCoordinate = function getCoordinate () {
94426           var p = new Coordinate();
94427           p.x = this.getX();
94428           p.y = this.getY();
94429           return p
94430         };
94431         HCoordinate.prototype.interfaces_ = function interfaces_ () {
94432           return []
94433         };
94434         HCoordinate.prototype.getClass = function getClass () {
94435           return HCoordinate
94436         };
94437         HCoordinate.intersection = function intersection (p1, p2, q1, q2) {
94438           var px = p1.y - p2.y;
94439           var py = p2.x - p1.x;
94440           var pw = p1.x * p2.y - p2.x * p1.y;
94441           var qx = q1.y - q2.y;
94442           var qy = q2.x - q1.x;
94443           var qw = q1.x * q2.y - q2.x * q1.y;
94444           var x = py * qw - qy * pw;
94445           var y = qx * pw - px * qw;
94446           var w = px * qy - qx * py;
94447           var xInt = x / w;
94448           var yInt = y / w;
94449           if (Double.isNaN(xInt) || (Double.isInfinite(xInt) || Double.isNaN(yInt)) || Double.isInfinite(yInt)) {
94450             throw new NotRepresentableException()
94451           }
94452           return new Coordinate(xInt, yInt)
94453         };
94454
94455         var Envelope = function Envelope () {
94456           this._minx = null;
94457           this._maxx = null;
94458           this._miny = null;
94459           this._maxy = null;
94460           if (arguments.length === 0) {
94461             this.init();
94462           } else if (arguments.length === 1) {
94463             if (arguments[0] instanceof Coordinate) {
94464               var p = arguments[0];
94465               this.init(p.x, p.x, p.y, p.y);
94466             } else if (arguments[0] instanceof Envelope) {
94467               var env = arguments[0];
94468               this.init(env);
94469             }
94470           } else if (arguments.length === 2) {
94471             var p1 = arguments[0];
94472             var p2 = arguments[1];
94473             this.init(p1.x, p2.x, p1.y, p2.y);
94474           } else if (arguments.length === 4) {
94475             var x1 = arguments[0];
94476             var x2 = arguments[1];
94477             var y1 = arguments[2];
94478             var y2 = arguments[3];
94479             this.init(x1, x2, y1, y2);
94480           }
94481         };
94482
94483         var staticAccessors$9 = { serialVersionUID: { configurable: true } };
94484         Envelope.prototype.getArea = function getArea () {
94485           return this.getWidth() * this.getHeight()
94486         };
94487         Envelope.prototype.equals = function equals (other) {
94488           if (!(other instanceof Envelope)) {
94489             return false
94490           }
94491           var otherEnvelope = other;
94492           if (this.isNull()) {
94493             return otherEnvelope.isNull()
94494           }
94495           return this._maxx === otherEnvelope.getMaxX() && this._maxy === otherEnvelope.getMaxY() && this._minx === otherEnvelope.getMinX() && this._miny === otherEnvelope.getMinY()
94496         };
94497         Envelope.prototype.intersection = function intersection (env) {
94498           if (this.isNull() || env.isNull() || !this.intersects(env)) { return new Envelope() }
94499           var intMinX = this._minx > env._minx ? this._minx : env._minx;
94500           var intMinY = this._miny > env._miny ? this._miny : env._miny;
94501           var intMaxX = this._maxx < env._maxx ? this._maxx : env._maxx;
94502           var intMaxY = this._maxy < env._maxy ? this._maxy : env._maxy;
94503           return new Envelope(intMinX, intMaxX, intMinY, intMaxY)
94504         };
94505         Envelope.prototype.isNull = function isNull () {
94506           return this._maxx < this._minx
94507         };
94508         Envelope.prototype.getMaxX = function getMaxX () {
94509           return this._maxx
94510         };
94511         Envelope.prototype.covers = function covers () {
94512           if (arguments.length === 1) {
94513             if (arguments[0] instanceof Coordinate) {
94514               var p = arguments[0];
94515               return this.covers(p.x, p.y)
94516             } else if (arguments[0] instanceof Envelope) {
94517               var other = arguments[0];
94518               if (this.isNull() || other.isNull()) {
94519                 return false
94520               }
94521               return other.getMinX() >= this._minx && other.getMaxX() <= this._maxx && other.getMinY() >= this._miny && other.getMaxY() <= this._maxy
94522             }
94523           } else if (arguments.length === 2) {
94524             var x = arguments[0];
94525             var y = arguments[1];
94526             if (this.isNull()) { return false }
94527             return x >= this._minx && x <= this._maxx && y >= this._miny && y <= this._maxy
94528           }
94529         };
94530         Envelope.prototype.intersects = function intersects () {
94531           if (arguments.length === 1) {
94532             if (arguments[0] instanceof Envelope) {
94533               var other = arguments[0];
94534               if (this.isNull() || other.isNull()) {
94535                 return false
94536               }
94537               return !(other._minx > this._maxx || other._maxx < this._minx || other._miny > this._maxy || other._maxy < this._miny)
94538             } else if (arguments[0] instanceof Coordinate) {
94539               var p = arguments[0];
94540               return this.intersects(p.x, p.y)
94541             }
94542           } else if (arguments.length === 2) {
94543             var x = arguments[0];
94544             var y = arguments[1];
94545             if (this.isNull()) { return false }
94546             return !(x > this._maxx || x < this._minx || y > this._maxy || y < this._miny)
94547           }
94548         };
94549         Envelope.prototype.getMinY = function getMinY () {
94550           return this._miny
94551         };
94552         Envelope.prototype.getMinX = function getMinX () {
94553           return this._minx
94554         };
94555         Envelope.prototype.expandToInclude = function expandToInclude () {
94556           if (arguments.length === 1) {
94557             if (arguments[0] instanceof Coordinate) {
94558               var p = arguments[0];
94559               this.expandToInclude(p.x, p.y);
94560             } else if (arguments[0] instanceof Envelope) {
94561               var other = arguments[0];
94562               if (other.isNull()) {
94563                 return null
94564               }
94565               if (this.isNull()) {
94566                 this._minx = other.getMinX();
94567                 this._maxx = other.getMaxX();
94568                 this._miny = other.getMinY();
94569                 this._maxy = other.getMaxY();
94570               } else {
94571                 if (other._minx < this._minx) {
94572                   this._minx = other._minx;
94573                 }
94574                 if (other._maxx > this._maxx) {
94575                   this._maxx = other._maxx;
94576                 }
94577                 if (other._miny < this._miny) {
94578                   this._miny = other._miny;
94579                 }
94580                 if (other._maxy > this._maxy) {
94581                   this._maxy = other._maxy;
94582                 }
94583               }
94584             }
94585           } else if (arguments.length === 2) {
94586             var x = arguments[0];
94587             var y = arguments[1];
94588             if (this.isNull()) {
94589               this._minx = x;
94590               this._maxx = x;
94591               this._miny = y;
94592               this._maxy = y;
94593             } else {
94594               if (x < this._minx) {
94595                 this._minx = x;
94596               }
94597               if (x > this._maxx) {
94598                 this._maxx = x;
94599               }
94600               if (y < this._miny) {
94601                 this._miny = y;
94602               }
94603               if (y > this._maxy) {
94604                 this._maxy = y;
94605               }
94606             }
94607           }
94608         };
94609         Envelope.prototype.minExtent = function minExtent () {
94610           if (this.isNull()) { return 0.0 }
94611           var w = this.getWidth();
94612           var h = this.getHeight();
94613           if (w < h) { return w }
94614           return h
94615         };
94616         Envelope.prototype.getWidth = function getWidth () {
94617           if (this.isNull()) {
94618             return 0
94619           }
94620           return this._maxx - this._minx
94621         };
94622         Envelope.prototype.compareTo = function compareTo (o) {
94623           var env = o;
94624           if (this.isNull()) {
94625             if (env.isNull()) { return 0 }
94626             return -1
94627           } else {
94628             if (env.isNull()) { return 1 }
94629           }
94630           if (this._minx < env._minx) { return -1 }
94631           if (this._minx > env._minx) { return 1 }
94632           if (this._miny < env._miny) { return -1 }
94633           if (this._miny > env._miny) { return 1 }
94634           if (this._maxx < env._maxx) { return -1 }
94635           if (this._maxx > env._maxx) { return 1 }
94636           if (this._maxy < env._maxy) { return -1 }
94637           if (this._maxy > env._maxy) { return 1 }
94638           return 0
94639         };
94640         Envelope.prototype.translate = function translate (transX, transY) {
94641           if (this.isNull()) {
94642             return null
94643           }
94644           this.init(this.getMinX() + transX, this.getMaxX() + transX, this.getMinY() + transY, this.getMaxY() + transY);
94645         };
94646         Envelope.prototype.toString = function toString () {
94647           return 'Env[' + this._minx + ' : ' + this._maxx + ', ' + this._miny + ' : ' + this._maxy + ']'
94648         };
94649         Envelope.prototype.setToNull = function setToNull () {
94650           this._minx = 0;
94651           this._maxx = -1;
94652           this._miny = 0;
94653           this._maxy = -1;
94654         };
94655         Envelope.prototype.getHeight = function getHeight () {
94656           if (this.isNull()) {
94657             return 0
94658           }
94659           return this._maxy - this._miny
94660         };
94661         Envelope.prototype.maxExtent = function maxExtent () {
94662           if (this.isNull()) { return 0.0 }
94663           var w = this.getWidth();
94664           var h = this.getHeight();
94665           if (w > h) { return w }
94666           return h
94667         };
94668         Envelope.prototype.expandBy = function expandBy () {
94669           if (arguments.length === 1) {
94670             var distance = arguments[0];
94671             this.expandBy(distance, distance);
94672           } else if (arguments.length === 2) {
94673             var deltaX = arguments[0];
94674             var deltaY = arguments[1];
94675             if (this.isNull()) { return null }
94676             this._minx -= deltaX;
94677             this._maxx += deltaX;
94678             this._miny -= deltaY;
94679             this._maxy += deltaY;
94680             if (this._minx > this._maxx || this._miny > this._maxy) { this.setToNull(); }
94681           }
94682         };
94683         Envelope.prototype.contains = function contains () {
94684           if (arguments.length === 1) {
94685             if (arguments[0] instanceof Envelope) {
94686               var other = arguments[0];
94687               return this.covers(other)
94688             } else if (arguments[0] instanceof Coordinate) {
94689               var p = arguments[0];
94690               return this.covers(p)
94691             }
94692           } else if (arguments.length === 2) {
94693             var x = arguments[0];
94694             var y = arguments[1];
94695             return this.covers(x, y)
94696           }
94697         };
94698         Envelope.prototype.centre = function centre () {
94699           if (this.isNull()) { return null }
94700           return new Coordinate((this.getMinX() + this.getMaxX()) / 2.0, (this.getMinY() + this.getMaxY()) / 2.0)
94701         };
94702         Envelope.prototype.init = function init () {
94703           if (arguments.length === 0) {
94704             this.setToNull();
94705           } else if (arguments.length === 1) {
94706             if (arguments[0] instanceof Coordinate) {
94707               var p = arguments[0];
94708               this.init(p.x, p.x, p.y, p.y);
94709             } else if (arguments[0] instanceof Envelope) {
94710               var env = arguments[0];
94711               this._minx = env._minx;
94712               this._maxx = env._maxx;
94713               this._miny = env._miny;
94714               this._maxy = env._maxy;
94715             }
94716           } else if (arguments.length === 2) {
94717             var p1 = arguments[0];
94718             var p2 = arguments[1];
94719             this.init(p1.x, p2.x, p1.y, p2.y);
94720           } else if (arguments.length === 4) {
94721             var x1 = arguments[0];
94722             var x2 = arguments[1];
94723             var y1 = arguments[2];
94724             var y2 = arguments[3];
94725             if (x1 < x2) {
94726               this._minx = x1;
94727               this._maxx = x2;
94728             } else {
94729               this._minx = x2;
94730               this._maxx = x1;
94731             }
94732             if (y1 < y2) {
94733               this._miny = y1;
94734               this._maxy = y2;
94735             } else {
94736               this._miny = y2;
94737               this._maxy = y1;
94738             }
94739           }
94740         };
94741         Envelope.prototype.getMaxY = function getMaxY () {
94742           return this._maxy
94743         };
94744         Envelope.prototype.distance = function distance (env) {
94745           if (this.intersects(env)) { return 0 }
94746           var dx = 0.0;
94747           if (this._maxx < env._minx) { dx = env._minx - this._maxx; } else if (this._minx > env._maxx) { dx = this._minx - env._maxx; }
94748           var dy = 0.0;
94749           if (this._maxy < env._miny) { dy = env._miny - this._maxy; } else if (this._miny > env._maxy) { dy = this._miny - env._maxy; }
94750           if (dx === 0.0) { return dy }
94751           if (dy === 0.0) { return dx }
94752           return Math.sqrt(dx * dx + dy * dy)
94753         };
94754         Envelope.prototype.hashCode = function hashCode () {
94755           var result = 17;
94756           result = 37 * result + Coordinate.hashCode(this._minx);
94757           result = 37 * result + Coordinate.hashCode(this._maxx);
94758           result = 37 * result + Coordinate.hashCode(this._miny);
94759           result = 37 * result + Coordinate.hashCode(this._maxy);
94760           return result
94761         };
94762         Envelope.prototype.interfaces_ = function interfaces_ () {
94763           return [Comparable, Serializable]
94764         };
94765         Envelope.prototype.getClass = function getClass () {
94766           return Envelope
94767         };
94768         Envelope.intersects = function intersects () {
94769           if (arguments.length === 3) {
94770             var p1 = arguments[0];
94771             var p2 = arguments[1];
94772             var q = arguments[2];
94773             if (q.x >= (p1.x < p2.x ? p1.x : p2.x) && q.x <= (p1.x > p2.x ? p1.x : p2.x) && (q.y >= (p1.y < p2.y ? p1.y : p2.y) && q.y <= (p1.y > p2.y ? p1.y : p2.y))) {
94774               return true
94775             }
94776             return false
94777           } else if (arguments.length === 4) {
94778             var p1$1 = arguments[0];
94779             var p2$1 = arguments[1];
94780             var q1 = arguments[2];
94781             var q2 = arguments[3];
94782             var minq = Math.min(q1.x, q2.x);
94783             var maxq = Math.max(q1.x, q2.x);
94784             var minp = Math.min(p1$1.x, p2$1.x);
94785             var maxp = Math.max(p1$1.x, p2$1.x);
94786             if (minp > maxq) { return false }
94787             if (maxp < minq) { return false }
94788             minq = Math.min(q1.y, q2.y);
94789             maxq = Math.max(q1.y, q2.y);
94790             minp = Math.min(p1$1.y, p2$1.y);
94791             maxp = Math.max(p1$1.y, p2$1.y);
94792             if (minp > maxq) { return false }
94793             if (maxp < minq) { return false }
94794             return true
94795           }
94796         };
94797         staticAccessors$9.serialVersionUID.get = function () { return 5873921885273102420 };
94798
94799         Object.defineProperties( Envelope, staticAccessors$9 );
94800
94801         var regExes = {
94802           'typeStr': /^\s*(\w+)\s*\(\s*(.*)\s*\)\s*$/,
94803           'emptyTypeStr': /^\s*(\w+)\s*EMPTY\s*$/,
94804           'spaces': /\s+/,
94805           'parenComma': /\)\s*,\s*\(/,
94806           'doubleParenComma': /\)\s*\)\s*,\s*\(\s*\(/, // can't use {2} here
94807           'trimParens': /^\s*\(?(.*?)\)?\s*$/
94808         };
94809
94810         /**
94811          * Class for reading and writing Well-Known Text.
94812          *
94813          * NOTE: Adapted from OpenLayers 2.11 implementation.
94814          */
94815
94816         /** Create a new parser for WKT
94817          *
94818          * @param {GeometryFactory} geometryFactory
94819          * @return An instance of WKTParser.
94820          * @constructor
94821          * @private
94822          */
94823         var WKTParser = function WKTParser (geometryFactory) {
94824           this.geometryFactory = geometryFactory || new GeometryFactory();
94825         };
94826         /**
94827          * Deserialize a WKT string and return a geometry. Supports WKT for POINT,
94828          * MULTIPOINT, LINESTRING, LINEARRING, MULTILINESTRING, POLYGON, MULTIPOLYGON,
94829          * and GEOMETRYCOLLECTION.
94830          *
94831          * @param {String} wkt A WKT string.
94832          * @return {Geometry} A geometry instance.
94833          * @private
94834          */
94835         WKTParser.prototype.read = function read (wkt) {
94836           var geometry, type, str;
94837           wkt = wkt.replace(/[\n\r]/g, ' ');
94838           var matches = regExes.typeStr.exec(wkt);
94839           if (wkt.search('EMPTY') !== -1) {
94840             matches = regExes.emptyTypeStr.exec(wkt);
94841             matches[2] = undefined;
94842           }
94843           if (matches) {
94844             type = matches[1].toLowerCase();
94845             str = matches[2];
94846             if (parse$1[type]) {
94847               geometry = parse$1[type].apply(this, [str]);
94848             }
94849           }
94850
94851           if (geometry === undefined) { throw new Error('Could not parse WKT ' + wkt) }
94852
94853           return geometry
94854         };
94855
94856         /**
94857          * Serialize a geometry into a WKT string.
94858          *
94859          * @param {Geometry} geometry A feature or array of features.
94860          * @return {String} The WKT string representation of the input geometries.
94861          * @private
94862          */
94863         WKTParser.prototype.write = function write (geometry) {
94864           return this.extractGeometry(geometry)
94865         };
94866
94867         /**
94868          * Entry point to construct the WKT for a single Geometry object.
94869          *
94870          * @param {Geometry} geometry
94871          * @return {String} A WKT string of representing the geometry.
94872          * @private
94873          */
94874         WKTParser.prototype.extractGeometry = function extractGeometry (geometry) {
94875           var type = geometry.getGeometryType().toLowerCase();
94876           if (!extract$1[type]) {
94877             return null
94878           }
94879           var wktType = type.toUpperCase();
94880           var data;
94881           if (geometry.isEmpty()) {
94882             data = wktType + ' EMPTY';
94883           } else {
94884             data = wktType + '(' + extract$1[type].apply(this, [geometry]) + ')';
94885           }
94886           return data
94887         };
94888
94889         /**
94890          * Object with properties corresponding to the geometry types. Property values
94891          * are functions that do the actual data extraction.
94892          * @private
94893          */
94894         var extract$1 = {
94895           coordinate: function coordinate (coordinate$1) {
94896             return coordinate$1.x + ' ' + coordinate$1.y
94897           },
94898
94899           /**
94900            * Return a space delimited string of point coordinates.
94901            *
94902            * @param {Point}
94903            *          point
94904            * @return {String} A string of coordinates representing the point.
94905            */
94906           point: function point (point$1) {
94907             return extract$1.coordinate.call(this, point$1._coordinates._coordinates[0])
94908           },
94909
94910           /**
94911            * Return a comma delimited string of point coordinates from a multipoint.
94912            *
94913            * @param {MultiPoint}
94914            *          multipoint
94915            * @return {String} A string of point coordinate strings representing the
94916            *         multipoint.
94917            */
94918           multipoint: function multipoint (multipoint$1) {
94919             var this$1 = this;
94920
94921             var array = [];
94922             for (var i = 0, len = multipoint$1._geometries.length; i < len; ++i) {
94923               array.push('(' + extract$1.point.apply(this$1, [multipoint$1._geometries[i]]) + ')');
94924             }
94925             return array.join(',')
94926           },
94927
94928           /**
94929            * Return a comma delimited string of point coordinates from a line.
94930            *
94931            * @param {LineString} linestring
94932            * @return {String} A string of point coordinate strings representing the linestring.
94933            */
94934           linestring: function linestring (linestring$1) {
94935             var this$1 = this;
94936
94937             var array = [];
94938             for (var i = 0, len = linestring$1._points._coordinates.length; i < len; ++i) {
94939               array.push(extract$1.coordinate.apply(this$1, [linestring$1._points._coordinates[i]]));
94940             }
94941             return array.join(',')
94942           },
94943
94944           linearring: function linearring (linearring$1) {
94945             var this$1 = this;
94946
94947             var array = [];
94948             for (var i = 0, len = linearring$1._points._coordinates.length; i < len; ++i) {
94949               array.push(extract$1.coordinate.apply(this$1, [linearring$1._points._coordinates[i]]));
94950             }
94951             return array.join(',')
94952           },
94953
94954           /**
94955            * Return a comma delimited string of linestring strings from a
94956            * multilinestring.
94957            *
94958            * @param {MultiLineString} multilinestring
94959            * @return {String} A string of of linestring strings representing the multilinestring.
94960            */
94961           multilinestring: function multilinestring (multilinestring$1) {
94962             var this$1 = this;
94963
94964             var array = [];
94965             for (var i = 0, len = multilinestring$1._geometries.length; i < len; ++i) {
94966               array.push('(' +
94967                 extract$1.linestring.apply(this$1, [multilinestring$1._geometries[i]]) +
94968                 ')');
94969             }
94970             return array.join(',')
94971           },
94972
94973           /**
94974            * Return a comma delimited string of linear ring arrays from a polygon.
94975            *
94976            * @param {Polygon} polygon
94977            * @return {String} An array of linear ring arrays representing the polygon.
94978            */
94979           polygon: function polygon (polygon$1) {
94980             var this$1 = this;
94981
94982             var array = [];
94983             array.push('(' + extract$1.linestring.apply(this, [polygon$1._shell]) + ')');
94984             for (var i = 0, len = polygon$1._holes.length; i < len; ++i) {
94985               array.push('(' + extract$1.linestring.apply(this$1, [polygon$1._holes[i]]) + ')');
94986             }
94987             return array.join(',')
94988           },
94989
94990           /**
94991            * Return an array of polygon arrays from a multipolygon.
94992            *
94993            * @param {MultiPolygon} multipolygon
94994            * @return {String} An array of polygon arrays representing the multipolygon.
94995            */
94996           multipolygon: function multipolygon (multipolygon$1) {
94997             var this$1 = this;
94998
94999             var array = [];
95000             for (var i = 0, len = multipolygon$1._geometries.length; i < len; ++i) {
95001               array.push('(' + extract$1.polygon.apply(this$1, [multipolygon$1._geometries[i]]) + ')');
95002             }
95003             return array.join(',')
95004           },
95005
95006           /**
95007            * Return the WKT portion between 'GEOMETRYCOLLECTION(' and ')' for an
95008            * geometrycollection.
95009            *
95010            * @param {GeometryCollection} collection
95011            * @return {String} internal WKT representation of the collection.
95012            */
95013           geometrycollection: function geometrycollection (collection) {
95014             var this$1 = this;
95015
95016             var array = [];
95017             for (var i = 0, len = collection._geometries.length; i < len; ++i) {
95018               array.push(this$1.extractGeometry(collection._geometries[i]));
95019             }
95020             return array.join(',')
95021           }
95022         };
95023
95024         /**
95025          * Object with properties corresponding to the geometry types. Property values
95026          * are functions that do the actual parsing.
95027          * @private
95028          */
95029         var parse$1 = {
95030           /**
95031            * Return point geometry given a point WKT fragment.
95032            *
95033            * @param {String} str A WKT fragment representing the point.
95034            * @return {Point} A point geometry.
95035            * @private
95036            */
95037           point: function point (str) {
95038             if (str === undefined) {
95039               return this.geometryFactory.createPoint()
95040             }
95041
95042             var coords = str.trim().split(regExes.spaces);
95043             return this.geometryFactory.createPoint(new Coordinate(Number.parseFloat(coords[0]),
95044               Number.parseFloat(coords[1])))
95045           },
95046
95047           /**
95048            * Return a multipoint geometry given a multipoint WKT fragment.
95049            *
95050            * @param {String} str A WKT fragment representing the multipoint.
95051            * @return {Point} A multipoint feature.
95052            * @private
95053            */
95054           multipoint: function multipoint (str) {
95055             var this$1 = this;
95056
95057             if (str === undefined) {
95058               return this.geometryFactory.createMultiPoint()
95059             }
95060
95061             var point;
95062             var points = str.trim().split(',');
95063             var components = [];
95064             for (var i = 0, len = points.length; i < len; ++i) {
95065               point = points[i].replace(regExes.trimParens, '$1');
95066               components.push(parse$1.point.apply(this$1, [point]));
95067             }
95068             return this.geometryFactory.createMultiPoint(components)
95069           },
95070
95071           /**
95072            * Return a linestring geometry given a linestring WKT fragment.
95073            *
95074            * @param {String} str A WKT fragment representing the linestring.
95075            * @return {LineString} A linestring geometry.
95076            * @private
95077            */
95078           linestring: function linestring (str) {
95079             if (str === undefined) {
95080               return this.geometryFactory.createLineString()
95081             }
95082
95083             var points = str.trim().split(',');
95084             var components = [];
95085             var coords;
95086             for (var i = 0, len = points.length; i < len; ++i) {
95087               coords = points[i].trim().split(regExes.spaces);
95088               components.push(new Coordinate(Number.parseFloat(coords[0]), Number.parseFloat(coords[1])));
95089             }
95090             return this.geometryFactory.createLineString(components)
95091           },
95092
95093           /**
95094            * Return a linearring geometry given a linearring WKT fragment.
95095            *
95096            * @param {String} str A WKT fragment representing the linearring.
95097            * @return {LinearRing} A linearring geometry.
95098            * @private
95099            */
95100           linearring: function linearring (str) {
95101             if (str === undefined) {
95102               return this.geometryFactory.createLinearRing()
95103             }
95104
95105             var points = str.trim().split(',');
95106             var components = [];
95107             var coords;
95108             for (var i = 0, len = points.length; i < len; ++i) {
95109               coords = points[i].trim().split(regExes.spaces);
95110               components.push(new Coordinate(Number.parseFloat(coords[0]), Number.parseFloat(coords[1])));
95111             }
95112             return this.geometryFactory.createLinearRing(components)
95113           },
95114
95115           /**
95116            * Return a multilinestring geometry given a multilinestring WKT fragment.
95117            *
95118            * @param {String} str A WKT fragment representing the multilinestring.
95119            * @return {MultiLineString} A multilinestring geometry.
95120            * @private
95121            */
95122           multilinestring: function multilinestring (str) {
95123             var this$1 = this;
95124
95125             if (str === undefined) {
95126               return this.geometryFactory.createMultiLineString()
95127             }
95128
95129             var line;
95130             var lines = str.trim().split(regExes.parenComma);
95131             var components = [];
95132             for (var i = 0, len = lines.length; i < len; ++i) {
95133               line = lines[i].replace(regExes.trimParens, '$1');
95134               components.push(parse$1.linestring.apply(this$1, [line]));
95135             }
95136             return this.geometryFactory.createMultiLineString(components)
95137           },
95138
95139           /**
95140            * Return a polygon geometry given a polygon WKT fragment.
95141            *
95142            * @param {String} str A WKT fragment representing the polygon.
95143            * @return {Polygon} A polygon geometry.
95144            * @private
95145            */
95146           polygon: function polygon (str) {
95147             var this$1 = this;
95148
95149             if (str === undefined) {
95150               return this.geometryFactory.createPolygon()
95151             }
95152
95153             var ring, linestring, linearring;
95154             var rings = str.trim().split(regExes.parenComma);
95155             var shell;
95156             var holes = [];
95157             for (var i = 0, len = rings.length; i < len; ++i) {
95158               ring = rings[i].replace(regExes.trimParens, '$1');
95159               linestring = parse$1.linestring.apply(this$1, [ring]);
95160               linearring = this$1.geometryFactory.createLinearRing(linestring._points);
95161               if (i === 0) {
95162                 shell = linearring;
95163               } else {
95164                 holes.push(linearring);
95165               }
95166             }
95167             return this.geometryFactory.createPolygon(shell, holes)
95168           },
95169
95170           /**
95171            * Return a multipolygon geometry given a multipolygon WKT fragment.
95172            *
95173            * @param {String} str A WKT fragment representing the multipolygon.
95174            * @return {MultiPolygon} A multipolygon geometry.
95175            * @private
95176            */
95177           multipolygon: function multipolygon (str) {
95178             var this$1 = this;
95179
95180             if (str === undefined) {
95181               return this.geometryFactory.createMultiPolygon()
95182             }
95183
95184             var polygon;
95185             var polygons = str.trim().split(regExes.doubleParenComma);
95186             var components = [];
95187             for (var i = 0, len = polygons.length; i < len; ++i) {
95188               polygon = polygons[i].replace(regExes.trimParens, '$1');
95189               components.push(parse$1.polygon.apply(this$1, [polygon]));
95190             }
95191             return this.geometryFactory.createMultiPolygon(components)
95192           },
95193
95194           /**
95195            * Return a geometrycollection given a geometrycollection WKT fragment.
95196            *
95197            * @param {String} str A WKT fragment representing the geometrycollection.
95198            * @return {GeometryCollection}
95199            * @private
95200            */
95201           geometrycollection: function geometrycollection (str) {
95202             var this$1 = this;
95203
95204             if (str === undefined) {
95205               return this.geometryFactory.createGeometryCollection()
95206             }
95207
95208             // separate components of the collection with |
95209             str = str.replace(/,\s*([A-Za-z])/g, '|$1');
95210             var wktArray = str.trim().split('|');
95211             var components = [];
95212             for (var i = 0, len = wktArray.length; i < len; ++i) {
95213               components.push(this$1.read(wktArray[i]));
95214             }
95215             return this.geometryFactory.createGeometryCollection(components)
95216           }
95217         };
95218
95219         /**
95220          * Writes the Well-Known Text representation of a {@link Geometry}. The
95221          * Well-Known Text format is defined in the <A
95222          * HREF="http://www.opengis.org/techno/specs.htm"> OGC Simple Features
95223          * Specification for SQL</A>.
95224          * <p>
95225          * The <code>WKTWriter</code> outputs coordinates rounded to the precision
95226          * model. Only the maximum number of decimal places necessary to represent the
95227          * ordinates to the required precision will be output.
95228          * <p>
95229          * The SFS WKT spec does not define a special tag for {@link LinearRing}s.
95230          * Under the spec, rings are output as <code>LINESTRING</code>s.
95231          */
95232
95233         /**
95234          * @param {GeometryFactory} geometryFactory
95235          * @constructor
95236          */
95237         var WKTWriter = function WKTWriter (geometryFactory) {
95238           this.parser = new WKTParser(geometryFactory);
95239         };
95240
95241         /**
95242          * Converts a <code>Geometry</code> to its Well-known Text representation.
95243          *
95244          * @param {Geometry} geometry a <code>Geometry</code> to process.
95245          * @return {string} a <Geometry Tagged Text> string (see the OpenGIS Simple
95246          *       Features Specification).
95247          * @memberof WKTWriter
95248          */
95249         WKTWriter.prototype.write = function write (geometry) {
95250           return this.parser.write(geometry)
95251         };
95252         /**
95253          * Generates the WKT for a <tt>LINESTRING</tt> specified by two
95254          * {@link Coordinate}s.
95255          *
95256          * @param p0 the first coordinate.
95257          * @param p1 the second coordinate.
95258          *
95259          * @return the WKT.
95260          * @private
95261          */
95262         WKTWriter.toLineString = function toLineString (p0, p1) {
95263           if (arguments.length !== 2) {
95264             throw new Error('Not implemented')
95265           }
95266           return 'LINESTRING ( ' + p0.x + ' ' + p0.y + ', ' + p1.x + ' ' + p1.y + ' )'
95267         };
95268
95269         var RuntimeException = (function (Error) {
95270           function RuntimeException (message) {
95271             Error.call(this, message);
95272             this.name = 'RuntimeException';
95273             this.message = message;
95274             this.stack = (new Error()).stack;
95275           }
95276
95277           if ( Error ) RuntimeException.__proto__ = Error;
95278           RuntimeException.prototype = Object.create( Error && Error.prototype );
95279           RuntimeException.prototype.constructor = RuntimeException;
95280
95281           return RuntimeException;
95282         }(Error));
95283
95284         var AssertionFailedException = (function (RuntimeException$$1) {
95285           function AssertionFailedException () {
95286             RuntimeException$$1.call(this);
95287             if (arguments.length === 0) {
95288               RuntimeException$$1.call(this);
95289             } else if (arguments.length === 1) {
95290               var message = arguments[0];
95291               RuntimeException$$1.call(this, message);
95292             }
95293           }
95294
95295           if ( RuntimeException$$1 ) AssertionFailedException.__proto__ = RuntimeException$$1;
95296           AssertionFailedException.prototype = Object.create( RuntimeException$$1 && RuntimeException$$1.prototype );
95297           AssertionFailedException.prototype.constructor = AssertionFailedException;
95298           AssertionFailedException.prototype.interfaces_ = function interfaces_ () {
95299             return []
95300           };
95301           AssertionFailedException.prototype.getClass = function getClass () {
95302             return AssertionFailedException
95303           };
95304
95305           return AssertionFailedException;
95306         }(RuntimeException));
95307
95308         var Assert = function Assert () {};
95309
95310         Assert.prototype.interfaces_ = function interfaces_ () {
95311           return []
95312         };
95313         Assert.prototype.getClass = function getClass () {
95314           return Assert
95315         };
95316         Assert.shouldNeverReachHere = function shouldNeverReachHere () {
95317           if (arguments.length === 0) {
95318             Assert.shouldNeverReachHere(null);
95319           } else if (arguments.length === 1) {
95320             var message = arguments[0];
95321             throw new AssertionFailedException('Should never reach here' + (message !== null ? ': ' + message : ''))
95322           }
95323         };
95324         Assert.isTrue = function isTrue () {
95325           var assertion;
95326           var message;
95327           if (arguments.length === 1) {
95328             assertion = arguments[0];
95329             Assert.isTrue(assertion, null);
95330           } else if (arguments.length === 2) {
95331             assertion = arguments[0];
95332             message = arguments[1];
95333             if (!assertion) {
95334               if (message === null) {
95335                 throw new AssertionFailedException()
95336               } else {
95337                 throw new AssertionFailedException(message)
95338               }
95339             }
95340           }
95341         };
95342         Assert.equals = function equals () {
95343           var expectedValue;
95344           var actualValue;
95345           var message;
95346           if (arguments.length === 2) {
95347             expectedValue = arguments[0];
95348             actualValue = arguments[1];
95349             Assert.equals(expectedValue, actualValue, null);
95350           } else if (arguments.length === 3) {
95351             expectedValue = arguments[0];
95352             actualValue = arguments[1];
95353             message = arguments[2];
95354             if (!actualValue.equals(expectedValue)) {
95355               throw new AssertionFailedException('Expected ' + expectedValue + ' but encountered ' + actualValue + (message !== null ? ': ' + message : ''))
95356             }
95357           }
95358         };
95359
95360         var LineIntersector = function LineIntersector () {
95361           this._result = null;
95362           this._inputLines = Array(2).fill().map(function () { return Array(2); });
95363           this._intPt = new Array(2).fill(null);
95364           this._intLineIndex = null;
95365           this._isProper = null;
95366           this._pa = null;
95367           this._pb = null;
95368           this._precisionModel = null;
95369           this._intPt[0] = new Coordinate();
95370           this._intPt[1] = new Coordinate();
95371           this._pa = this._intPt[0];
95372           this._pb = this._intPt[1];
95373           this._result = 0;
95374         };
95375
95376         var staticAccessors$10 = { DONT_INTERSECT: { configurable: true },DO_INTERSECT: { configurable: true },COLLINEAR: { configurable: true },NO_INTERSECTION: { configurable: true },POINT_INTERSECTION: { configurable: true },COLLINEAR_INTERSECTION: { configurable: true } };
95377         LineIntersector.prototype.getIndexAlongSegment = function getIndexAlongSegment (segmentIndex, intIndex) {
95378           this.computeIntLineIndex();
95379           return this._intLineIndex[segmentIndex][intIndex]
95380         };
95381         LineIntersector.prototype.getTopologySummary = function getTopologySummary () {
95382           var catBuf = new StringBuffer();
95383           if (this.isEndPoint()) { catBuf.append(' endpoint'); }
95384           if (this._isProper) { catBuf.append(' proper'); }
95385           if (this.isCollinear()) { catBuf.append(' collinear'); }
95386           return catBuf.toString()
95387         };
95388         LineIntersector.prototype.computeIntersection = function computeIntersection (p1, p2, p3, p4) {
95389           this._inputLines[0][0] = p1;
95390           this._inputLines[0][1] = p2;
95391           this._inputLines[1][0] = p3;
95392           this._inputLines[1][1] = p4;
95393           this._result = this.computeIntersect(p1, p2, p3, p4);
95394         };
95395         LineIntersector.prototype.getIntersectionNum = function getIntersectionNum () {
95396           return this._result
95397         };
95398         LineIntersector.prototype.computeIntLineIndex = function computeIntLineIndex () {
95399           if (arguments.length === 0) {
95400             if (this._intLineIndex === null) {
95401               this._intLineIndex = Array(2).fill().map(function () { return Array(2); });
95402               this.computeIntLineIndex(0);
95403               this.computeIntLineIndex(1);
95404             }
95405           } else if (arguments.length === 1) {
95406             var segmentIndex = arguments[0];
95407             var dist0 = this.getEdgeDistance(segmentIndex, 0);
95408             var dist1 = this.getEdgeDistance(segmentIndex, 1);
95409             if (dist0 > dist1) {
95410               this._intLineIndex[segmentIndex][0] = 0;
95411               this._intLineIndex[segmentIndex][1] = 1;
95412             } else {
95413               this._intLineIndex[segmentIndex][0] = 1;
95414               this._intLineIndex[segmentIndex][1] = 0;
95415             }
95416           }
95417         };
95418         LineIntersector.prototype.isProper = function isProper () {
95419           return this.hasIntersection() && this._isProper
95420         };
95421         LineIntersector.prototype.setPrecisionModel = function setPrecisionModel (precisionModel) {
95422           this._precisionModel = precisionModel;
95423         };
95424         LineIntersector.prototype.isInteriorIntersection = function isInteriorIntersection () {
95425             var this$1 = this;
95426
95427           if (arguments.length === 0) {
95428             if (this.isInteriorIntersection(0)) { return true }
95429             if (this.isInteriorIntersection(1)) { return true }
95430             return false
95431           } else if (arguments.length === 1) {
95432             var inputLineIndex = arguments[0];
95433             for (var i = 0; i < this._result; i++) {
95434               if (!(this$1._intPt[i].equals2D(this$1._inputLines[inputLineIndex][0]) || this$1._intPt[i].equals2D(this$1._inputLines[inputLineIndex][1]))) {
95435                 return true
95436               }
95437             }
95438             return false
95439           }
95440         };
95441         LineIntersector.prototype.getIntersection = function getIntersection (intIndex) {
95442           return this._intPt[intIndex]
95443         };
95444         LineIntersector.prototype.isEndPoint = function isEndPoint () {
95445           return this.hasIntersection() && !this._isProper
95446         };
95447         LineIntersector.prototype.hasIntersection = function hasIntersection () {
95448           return this._result !== LineIntersector.NO_INTERSECTION
95449         };
95450         LineIntersector.prototype.getEdgeDistance = function getEdgeDistance (segmentIndex, intIndex) {
95451           var dist = LineIntersector.computeEdgeDistance(this._intPt[intIndex], this._inputLines[segmentIndex][0], this._inputLines[segmentIndex][1]);
95452           return dist
95453         };
95454         LineIntersector.prototype.isCollinear = function isCollinear () {
95455           return this._result === LineIntersector.COLLINEAR_INTERSECTION
95456         };
95457         LineIntersector.prototype.toString = function toString () {
95458           return WKTWriter.toLineString(this._inputLines[0][0], this._inputLines[0][1]) + ' - ' + WKTWriter.toLineString(this._inputLines[1][0], this._inputLines[1][1]) + this.getTopologySummary()
95459         };
95460         LineIntersector.prototype.getEndpoint = function getEndpoint (segmentIndex, ptIndex) {
95461           return this._inputLines[segmentIndex][ptIndex]
95462         };
95463         LineIntersector.prototype.isIntersection = function isIntersection (pt) {
95464             var this$1 = this;
95465
95466           for (var i = 0; i < this._result; i++) {
95467             if (this$1._intPt[i].equals2D(pt)) {
95468               return true
95469             }
95470           }
95471           return false
95472         };
95473         LineIntersector.prototype.getIntersectionAlongSegment = function getIntersectionAlongSegment (segmentIndex, intIndex) {
95474           this.computeIntLineIndex();
95475           return this._intPt[this._intLineIndex[segmentIndex][intIndex]]
95476         };
95477         LineIntersector.prototype.interfaces_ = function interfaces_ () {
95478           return []
95479         };
95480         LineIntersector.prototype.getClass = function getClass () {
95481           return LineIntersector
95482         };
95483         LineIntersector.computeEdgeDistance = function computeEdgeDistance (p, p0, p1) {
95484           var dx = Math.abs(p1.x - p0.x);
95485           var dy = Math.abs(p1.y - p0.y);
95486           var dist = -1.0;
95487           if (p.equals(p0)) {
95488             dist = 0.0;
95489           } else if (p.equals(p1)) {
95490             if (dx > dy) { dist = dx; } else { dist = dy; }
95491           } else {
95492             var pdx = Math.abs(p.x - p0.x);
95493             var pdy = Math.abs(p.y - p0.y);
95494             if (dx > dy) { dist = pdx; } else { dist = pdy; }
95495             if (dist === 0.0 && !p.equals(p0)) {
95496               dist = Math.max(pdx, pdy);
95497             }
95498           }
95499           Assert.isTrue(!(dist === 0.0 && !p.equals(p0)), 'Bad distance calculation');
95500           return dist
95501         };
95502         LineIntersector.nonRobustComputeEdgeDistance = function nonRobustComputeEdgeDistance (p, p1, p2) {
95503           var dx = p.x - p1.x;
95504           var dy = p.y - p1.y;
95505           var dist = Math.sqrt(dx * dx + dy * dy);
95506           Assert.isTrue(!(dist === 0.0 && !p.equals(p1)), 'Invalid distance calculation');
95507           return dist
95508         };
95509         staticAccessors$10.DONT_INTERSECT.get = function () { return 0 };
95510         staticAccessors$10.DO_INTERSECT.get = function () { return 1 };
95511         staticAccessors$10.COLLINEAR.get = function () { return 2 };
95512         staticAccessors$10.NO_INTERSECTION.get = function () { return 0 };
95513         staticAccessors$10.POINT_INTERSECTION.get = function () { return 1 };
95514         staticAccessors$10.COLLINEAR_INTERSECTION.get = function () { return 2 };
95515
95516         Object.defineProperties( LineIntersector, staticAccessors$10 );
95517
95518         var RobustLineIntersector = (function (LineIntersector$$1) {
95519           function RobustLineIntersector () {
95520             LineIntersector$$1.apply(this, arguments);
95521           }
95522
95523           if ( LineIntersector$$1 ) RobustLineIntersector.__proto__ = LineIntersector$$1;
95524           RobustLineIntersector.prototype = Object.create( LineIntersector$$1 && LineIntersector$$1.prototype );
95525           RobustLineIntersector.prototype.constructor = RobustLineIntersector;
95526
95527           RobustLineIntersector.prototype.isInSegmentEnvelopes = function isInSegmentEnvelopes (intPt) {
95528             var env0 = new Envelope(this._inputLines[0][0], this._inputLines[0][1]);
95529             var env1 = new Envelope(this._inputLines[1][0], this._inputLines[1][1]);
95530             return env0.contains(intPt) && env1.contains(intPt)
95531           };
95532           RobustLineIntersector.prototype.computeIntersection = function computeIntersection () {
95533             if (arguments.length === 3) {
95534               var p = arguments[0];
95535               var p1 = arguments[1];
95536               var p2 = arguments[2];
95537               this._isProper = false;
95538               if (Envelope.intersects(p1, p2, p)) {
95539                 if (CGAlgorithms.orientationIndex(p1, p2, p) === 0 && CGAlgorithms.orientationIndex(p2, p1, p) === 0) {
95540                   this._isProper = true;
95541                   if (p.equals(p1) || p.equals(p2)) {
95542                     this._isProper = false;
95543                   }
95544                   this._result = LineIntersector$$1.POINT_INTERSECTION;
95545                   return null
95546                 }
95547               }
95548               this._result = LineIntersector$$1.NO_INTERSECTION;
95549             } else { return LineIntersector$$1.prototype.computeIntersection.apply(this, arguments) }
95550           };
95551           RobustLineIntersector.prototype.normalizeToMinimum = function normalizeToMinimum (n1, n2, n3, n4, normPt) {
95552             normPt.x = this.smallestInAbsValue(n1.x, n2.x, n3.x, n4.x);
95553             normPt.y = this.smallestInAbsValue(n1.y, n2.y, n3.y, n4.y);
95554             n1.x -= normPt.x;
95555             n1.y -= normPt.y;
95556             n2.x -= normPt.x;
95557             n2.y -= normPt.y;
95558             n3.x -= normPt.x;
95559             n3.y -= normPt.y;
95560             n4.x -= normPt.x;
95561             n4.y -= normPt.y;
95562           };
95563           RobustLineIntersector.prototype.safeHCoordinateIntersection = function safeHCoordinateIntersection (p1, p2, q1, q2) {
95564             var intPt = null;
95565             try {
95566               intPt = HCoordinate.intersection(p1, p2, q1, q2);
95567             } catch (e) {
95568               if (e instanceof NotRepresentableException) {
95569                 intPt = RobustLineIntersector.nearestEndpoint(p1, p2, q1, q2);
95570               } else { throw e }
95571             } finally {}
95572             return intPt
95573           };
95574           RobustLineIntersector.prototype.intersection = function intersection (p1, p2, q1, q2) {
95575             var intPt = this.intersectionWithNormalization(p1, p2, q1, q2);
95576             if (!this.isInSegmentEnvelopes(intPt)) {
95577               intPt = new Coordinate(RobustLineIntersector.nearestEndpoint(p1, p2, q1, q2));
95578             }
95579             if (this._precisionModel !== null) {
95580               this._precisionModel.makePrecise(intPt);
95581             }
95582             return intPt
95583           };
95584           RobustLineIntersector.prototype.smallestInAbsValue = function smallestInAbsValue (x1, x2, x3, x4) {
95585             var x = x1;
95586             var xabs = Math.abs(x);
95587             if (Math.abs(x2) < xabs) {
95588               x = x2;
95589               xabs = Math.abs(x2);
95590             }
95591             if (Math.abs(x3) < xabs) {
95592               x = x3;
95593               xabs = Math.abs(x3);
95594             }
95595             if (Math.abs(x4) < xabs) {
95596               x = x4;
95597             }
95598             return x
95599           };
95600           RobustLineIntersector.prototype.checkDD = function checkDD (p1, p2, q1, q2, intPt) {
95601             var intPtDD = CGAlgorithmsDD.intersection(p1, p2, q1, q2);
95602             var isIn = this.isInSegmentEnvelopes(intPtDD);
95603             System.out.println('DD in env = ' + isIn + '  --------------------- ' + intPtDD);
95604             if (intPt.distance(intPtDD) > 0.0001) {
95605               System.out.println('Distance = ' + intPt.distance(intPtDD));
95606             }
95607           };
95608           RobustLineIntersector.prototype.intersectionWithNormalization = function intersectionWithNormalization (p1, p2, q1, q2) {
95609             var n1 = new Coordinate(p1);
95610             var n2 = new Coordinate(p2);
95611             var n3 = new Coordinate(q1);
95612             var n4 = new Coordinate(q2);
95613             var normPt = new Coordinate();
95614             this.normalizeToEnvCentre(n1, n2, n3, n4, normPt);
95615             var intPt = this.safeHCoordinateIntersection(n1, n2, n3, n4);
95616             intPt.x += normPt.x;
95617             intPt.y += normPt.y;
95618             return intPt
95619           };
95620           RobustLineIntersector.prototype.computeCollinearIntersection = function computeCollinearIntersection (p1, p2, q1, q2) {
95621             var p1q1p2 = Envelope.intersects(p1, p2, q1);
95622             var p1q2p2 = Envelope.intersects(p1, p2, q2);
95623             var q1p1q2 = Envelope.intersects(q1, q2, p1);
95624             var q1p2q2 = Envelope.intersects(q1, q2, p2);
95625             if (p1q1p2 && p1q2p2) {
95626               this._intPt[0] = q1;
95627               this._intPt[1] = q2;
95628               return LineIntersector$$1.COLLINEAR_INTERSECTION
95629             }
95630             if (q1p1q2 && q1p2q2) {
95631               this._intPt[0] = p1;
95632               this._intPt[1] = p2;
95633               return LineIntersector$$1.COLLINEAR_INTERSECTION
95634             }
95635             if (p1q1p2 && q1p1q2) {
95636               this._intPt[0] = q1;
95637               this._intPt[1] = p1;
95638               return q1.equals(p1) && !p1q2p2 && !q1p2q2 ? LineIntersector$$1.POINT_INTERSECTION : LineIntersector$$1.COLLINEAR_INTERSECTION
95639             }
95640             if (p1q1p2 && q1p2q2) {
95641               this._intPt[0] = q1;
95642               this._intPt[1] = p2;
95643               return q1.equals(p2) && !p1q2p2 && !q1p1q2 ? LineIntersector$$1.POINT_INTERSECTION : LineIntersector$$1.COLLINEAR_INTERSECTION
95644             }
95645             if (p1q2p2 && q1p1q2) {
95646               this._intPt[0] = q2;
95647               this._intPt[1] = p1;
95648               return q2.equals(p1) && !p1q1p2 && !q1p2q2 ? LineIntersector$$1.POINT_INTERSECTION : LineIntersector$$1.COLLINEAR_INTERSECTION
95649             }
95650             if (p1q2p2 && q1p2q2) {
95651               this._intPt[0] = q2;
95652               this._intPt[1] = p2;
95653               return q2.equals(p2) && !p1q1p2 && !q1p1q2 ? LineIntersector$$1.POINT_INTERSECTION : LineIntersector$$1.COLLINEAR_INTERSECTION
95654             }
95655             return LineIntersector$$1.NO_INTERSECTION
95656           };
95657           RobustLineIntersector.prototype.normalizeToEnvCentre = function normalizeToEnvCentre (n00, n01, n10, n11, normPt) {
95658             var minX0 = n00.x < n01.x ? n00.x : n01.x;
95659             var minY0 = n00.y < n01.y ? n00.y : n01.y;
95660             var maxX0 = n00.x > n01.x ? n00.x : n01.x;
95661             var maxY0 = n00.y > n01.y ? n00.y : n01.y;
95662             var minX1 = n10.x < n11.x ? n10.x : n11.x;
95663             var minY1 = n10.y < n11.y ? n10.y : n11.y;
95664             var maxX1 = n10.x > n11.x ? n10.x : n11.x;
95665             var maxY1 = n10.y > n11.y ? n10.y : n11.y;
95666             var intMinX = minX0 > minX1 ? minX0 : minX1;
95667             var intMaxX = maxX0 < maxX1 ? maxX0 : maxX1;
95668             var intMinY = minY0 > minY1 ? minY0 : minY1;
95669             var intMaxY = maxY0 < maxY1 ? maxY0 : maxY1;
95670             var intMidX = (intMinX + intMaxX) / 2.0;
95671             var intMidY = (intMinY + intMaxY) / 2.0;
95672             normPt.x = intMidX;
95673             normPt.y = intMidY;
95674             n00.x -= normPt.x;
95675             n00.y -= normPt.y;
95676             n01.x -= normPt.x;
95677             n01.y -= normPt.y;
95678             n10.x -= normPt.x;
95679             n10.y -= normPt.y;
95680             n11.x -= normPt.x;
95681             n11.y -= normPt.y;
95682           };
95683           RobustLineIntersector.prototype.computeIntersect = function computeIntersect (p1, p2, q1, q2) {
95684             this._isProper = false;
95685             if (!Envelope.intersects(p1, p2, q1, q2)) { return LineIntersector$$1.NO_INTERSECTION }
95686             var Pq1 = CGAlgorithms.orientationIndex(p1, p2, q1);
95687             var Pq2 = CGAlgorithms.orientationIndex(p1, p2, q2);
95688             if ((Pq1 > 0 && Pq2 > 0) || (Pq1 < 0 && Pq2 < 0)) {
95689               return LineIntersector$$1.NO_INTERSECTION
95690             }
95691             var Qp1 = CGAlgorithms.orientationIndex(q1, q2, p1);
95692             var Qp2 = CGAlgorithms.orientationIndex(q1, q2, p2);
95693             if ((Qp1 > 0 && Qp2 > 0) || (Qp1 < 0 && Qp2 < 0)) {
95694               return LineIntersector$$1.NO_INTERSECTION
95695             }
95696             var collinear = Pq1 === 0 && Pq2 === 0 && Qp1 === 0 && Qp2 === 0;
95697             if (collinear) {
95698               return this.computeCollinearIntersection(p1, p2, q1, q2)
95699             }
95700             if (Pq1 === 0 || Pq2 === 0 || Qp1 === 0 || Qp2 === 0) {
95701               this._isProper = false;
95702               if (p1.equals2D(q1) || p1.equals2D(q2)) {
95703                 this._intPt[0] = p1;
95704               } else if (p2.equals2D(q1) || p2.equals2D(q2)) {
95705                 this._intPt[0] = p2;
95706               } else if (Pq1 === 0) {
95707                 this._intPt[0] = new Coordinate(q1);
95708               } else if (Pq2 === 0) {
95709                 this._intPt[0] = new Coordinate(q2);
95710               } else if (Qp1 === 0) {
95711                 this._intPt[0] = new Coordinate(p1);
95712               } else if (Qp2 === 0) {
95713                 this._intPt[0] = new Coordinate(p2);
95714               }
95715             } else {
95716               this._isProper = true;
95717               this._intPt[0] = this.intersection(p1, p2, q1, q2);
95718             }
95719             return LineIntersector$$1.POINT_INTERSECTION
95720           };
95721           RobustLineIntersector.prototype.interfaces_ = function interfaces_ () {
95722             return []
95723           };
95724           RobustLineIntersector.prototype.getClass = function getClass () {
95725             return RobustLineIntersector
95726           };
95727           RobustLineIntersector.nearestEndpoint = function nearestEndpoint (p1, p2, q1, q2) {
95728             var nearestPt = p1;
95729             var minDist = CGAlgorithms.distancePointLine(p1, q1, q2);
95730             var dist = CGAlgorithms.distancePointLine(p2, q1, q2);
95731             if (dist < minDist) {
95732               minDist = dist;
95733               nearestPt = p2;
95734             }
95735             dist = CGAlgorithms.distancePointLine(q1, p1, p2);
95736             if (dist < minDist) {
95737               minDist = dist;
95738               nearestPt = q1;
95739             }
95740             dist = CGAlgorithms.distancePointLine(q2, p1, p2);
95741             if (dist < minDist) {
95742               minDist = dist;
95743               nearestPt = q2;
95744             }
95745             return nearestPt
95746           };
95747
95748           return RobustLineIntersector;
95749         }(LineIntersector));
95750
95751         var RobustDeterminant = function RobustDeterminant () {};
95752
95753         RobustDeterminant.prototype.interfaces_ = function interfaces_ () {
95754           return []
95755         };
95756         RobustDeterminant.prototype.getClass = function getClass () {
95757           return RobustDeterminant
95758         };
95759         RobustDeterminant.orientationIndex = function orientationIndex (p1, p2, q) {
95760           var dx1 = p2.x - p1.x;
95761           var dy1 = p2.y - p1.y;
95762           var dx2 = q.x - p2.x;
95763           var dy2 = q.y - p2.y;
95764           return RobustDeterminant.signOfDet2x2(dx1, dy1, dx2, dy2)
95765         };
95766         RobustDeterminant.signOfDet2x2 = function signOfDet2x2 (x1, y1, x2, y2) {
95767           var sign = null;
95768           var swap = null;
95769           var k = null;
95770           sign = 1;
95771           if (x1 === 0.0 || y2 === 0.0) {
95772             if (y1 === 0.0 || x2 === 0.0) {
95773               return 0
95774             } else if (y1 > 0) {
95775               if (x2 > 0) {
95776                 return -sign
95777               } else {
95778                 return sign
95779               }
95780             } else {
95781               if (x2 > 0) {
95782                 return sign
95783               } else {
95784                 return -sign
95785               }
95786             }
95787           }
95788           if (y1 === 0.0 || x2 === 0.0) {
95789             if (y2 > 0) {
95790               if (x1 > 0) {
95791                 return sign
95792               } else {
95793                 return -sign
95794               }
95795             } else {
95796               if (x1 > 0) {
95797                 return -sign
95798               } else {
95799                 return sign
95800               }
95801             }
95802           }
95803           if (y1 > 0.0) {
95804             if (y2 > 0.0) {
95805               if (y1 <= y2) ; else {
95806                 sign = -sign;
95807                 swap = x1;
95808                 x1 = x2;
95809                 x2 = swap;
95810                 swap = y1;
95811                 y1 = y2;
95812                 y2 = swap;
95813               }
95814             } else {
95815               if (y1 <= -y2) {
95816                 sign = -sign;
95817                 x2 = -x2;
95818                 y2 = -y2;
95819               } else {
95820                 swap = x1;
95821                 x1 = -x2;
95822                 x2 = swap;
95823                 swap = y1;
95824                 y1 = -y2;
95825                 y2 = swap;
95826               }
95827             }
95828           } else {
95829             if (y2 > 0.0) {
95830               if (-y1 <= y2) {
95831                 sign = -sign;
95832                 x1 = -x1;
95833                 y1 = -y1;
95834               } else {
95835                 swap = -x1;
95836                 x1 = x2;
95837                 x2 = swap;
95838                 swap = -y1;
95839                 y1 = y2;
95840                 y2 = swap;
95841               }
95842             } else {
95843               if (y1 >= y2) {
95844                 x1 = -x1;
95845                 y1 = -y1;
95846                 x2 = -x2;
95847                 y2 = -y2;
95848               } else {
95849                 sign = -sign;
95850                 swap = -x1;
95851                 x1 = -x2;
95852                 x2 = swap;
95853                 swap = -y1;
95854                 y1 = -y2;
95855                 y2 = swap;
95856               }
95857             }
95858           }
95859           if (x1 > 0.0) {
95860             if (x2 > 0.0) {
95861               if (x1 <= x2) ; else {
95862                 return sign
95863               }
95864             } else {
95865               return sign
95866             }
95867           } else {
95868             if (x2 > 0.0) {
95869               return -sign
95870             } else {
95871               if (x1 >= x2) {
95872                 sign = -sign;
95873                 x1 = -x1;
95874                 x2 = -x2;
95875               } else {
95876                 return -sign
95877               }
95878             }
95879           }
95880           while (true) {
95881             k = Math.floor(x2 / x1);
95882             x2 = x2 - k * x1;
95883             y2 = y2 - k * y1;
95884             if (y2 < 0.0) {
95885               return -sign
95886             }
95887             if (y2 > y1) {
95888               return sign
95889             }
95890             if (x1 > x2 + x2) {
95891               if (y1 < y2 + y2) {
95892                 return sign
95893               }
95894             } else {
95895               if (y1 > y2 + y2) {
95896                 return -sign
95897               } else {
95898                 x2 = x1 - x2;
95899                 y2 = y1 - y2;
95900                 sign = -sign;
95901               }
95902             }
95903             if (y2 === 0.0) {
95904               if (x2 === 0.0) {
95905                 return 0
95906               } else {
95907                 return -sign
95908               }
95909             }
95910             if (x2 === 0.0) {
95911               return sign
95912             }
95913             k = Math.floor(x1 / x2);
95914             x1 = x1 - k * x2;
95915             y1 = y1 - k * y2;
95916             if (y1 < 0.0) {
95917               return sign
95918             }
95919             if (y1 > y2) {
95920               return -sign
95921             }
95922             if (x2 > x1 + x1) {
95923               if (y2 < y1 + y1) {
95924                 return -sign
95925               }
95926             } else {
95927               if (y2 > y1 + y1) {
95928                 return sign
95929               } else {
95930                 x1 = x2 - x1;
95931                 y1 = y2 - y1;
95932                 sign = -sign;
95933               }
95934             }
95935             if (y1 === 0.0) {
95936               if (x1 === 0.0) {
95937                 return 0
95938               } else {
95939                 return sign
95940               }
95941             }
95942             if (x1 === 0.0) {
95943               return -sign
95944             }
95945           }
95946         };
95947
95948         var RayCrossingCounter = function RayCrossingCounter () {
95949           this._p = null;
95950           this._crossingCount = 0;
95951           this._isPointOnSegment = false;
95952           var p = arguments[0];
95953           this._p = p;
95954         };
95955         RayCrossingCounter.prototype.countSegment = function countSegment (p1, p2) {
95956           if (p1.x < this._p.x && p2.x < this._p.x) { return null }
95957           if (this._p.x === p2.x && this._p.y === p2.y) {
95958             this._isPointOnSegment = true;
95959             return null
95960           }
95961           if (p1.y === this._p.y && p2.y === this._p.y) {
95962             var minx = p1.x;
95963             var maxx = p2.x;
95964             if (minx > maxx) {
95965               minx = p2.x;
95966               maxx = p1.x;
95967             }
95968             if (this._p.x >= minx && this._p.x <= maxx) {
95969               this._isPointOnSegment = true;
95970             }
95971             return null
95972           }
95973           if ((p1.y > this._p.y && p2.y <= this._p.y) || (p2.y > this._p.y && p1.y <= this._p.y)) {
95974             var x1 = p1.x - this._p.x;
95975             var y1 = p1.y - this._p.y;
95976             var x2 = p2.x - this._p.x;
95977             var y2 = p2.y - this._p.y;
95978             var xIntSign = RobustDeterminant.signOfDet2x2(x1, y1, x2, y2);
95979             if (xIntSign === 0.0) {
95980               this._isPointOnSegment = true;
95981               return null
95982             }
95983             if (y2 < y1) { xIntSign = -xIntSign; }
95984             if (xIntSign > 0.0) {
95985               this._crossingCount++;
95986             }
95987           }
95988         };
95989         RayCrossingCounter.prototype.isPointInPolygon = function isPointInPolygon () {
95990           return this.getLocation() !== Location.EXTERIOR
95991         };
95992         RayCrossingCounter.prototype.getLocation = function getLocation () {
95993           if (this._isPointOnSegment) { return Location.BOUNDARY }
95994           if (this._crossingCount % 2 === 1) {
95995             return Location.INTERIOR
95996           }
95997           return Location.EXTERIOR
95998         };
95999         RayCrossingCounter.prototype.isOnSegment = function isOnSegment () {
96000           return this._isPointOnSegment
96001         };
96002         RayCrossingCounter.prototype.interfaces_ = function interfaces_ () {
96003           return []
96004         };
96005         RayCrossingCounter.prototype.getClass = function getClass () {
96006           return RayCrossingCounter
96007         };
96008         RayCrossingCounter.locatePointInRing = function locatePointInRing () {
96009           if (arguments[0] instanceof Coordinate && hasInterface(arguments[1], CoordinateSequence)) {
96010             var p = arguments[0];
96011             var ring = arguments[1];
96012             var counter = new RayCrossingCounter(p);
96013             var p1 = new Coordinate();
96014             var p2 = new Coordinate();
96015             for (var i = 1; i < ring.size(); i++) {
96016               ring.getCoordinate(i, p1);
96017               ring.getCoordinate(i - 1, p2);
96018               counter.countSegment(p1, p2);
96019               if (counter.isOnSegment()) { return counter.getLocation() }
96020             }
96021             return counter.getLocation()
96022           } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof Array) {
96023             var p$1 = arguments[0];
96024             var ring$1 = arguments[1];
96025             var counter$1 = new RayCrossingCounter(p$1);
96026             for (var i$1 = 1; i$1 < ring$1.length; i$1++) {
96027               var p1$1 = ring$1[i$1];
96028               var p2$1 = ring$1[i$1 - 1];
96029               counter$1.countSegment(p1$1, p2$1);
96030               if (counter$1.isOnSegment()) { return counter$1.getLocation() }
96031             }
96032             return counter$1.getLocation()
96033           }
96034         };
96035
96036         var CGAlgorithms = function CGAlgorithms () {};
96037
96038         var staticAccessors$3 = { CLOCKWISE: { configurable: true },RIGHT: { configurable: true },COUNTERCLOCKWISE: { configurable: true },LEFT: { configurable: true },COLLINEAR: { configurable: true },STRAIGHT: { configurable: true } };
96039
96040         CGAlgorithms.prototype.interfaces_ = function interfaces_ () {
96041           return []
96042         };
96043         CGAlgorithms.prototype.getClass = function getClass () {
96044           return CGAlgorithms
96045         };
96046         CGAlgorithms.orientationIndex = function orientationIndex (p1, p2, q) {
96047           return CGAlgorithmsDD.orientationIndex(p1, p2, q)
96048         };
96049         CGAlgorithms.signedArea = function signedArea () {
96050           if (arguments[0] instanceof Array) {
96051             var ring = arguments[0];
96052             if (ring.length < 3) { return 0.0 }
96053             var sum = 0.0;
96054             var x0 = ring[0].x;
96055             for (var i = 1; i < ring.length - 1; i++) {
96056               var x = ring[i].x - x0;
96057               var y1 = ring[i + 1].y;
96058               var y2 = ring[i - 1].y;
96059               sum += x * (y2 - y1);
96060             }
96061             return sum / 2.0
96062           } else if (hasInterface(arguments[0], CoordinateSequence)) {
96063             var ring$1 = arguments[0];
96064             var n = ring$1.size();
96065             if (n < 3) { return 0.0 }
96066             var p0 = new Coordinate();
96067             var p1 = new Coordinate();
96068             var p2 = new Coordinate();
96069             ring$1.getCoordinate(0, p1);
96070             ring$1.getCoordinate(1, p2);
96071             var x0$1 = p1.x;
96072             p2.x -= x0$1;
96073             var sum$1 = 0.0;
96074             for (var i$1 = 1; i$1 < n - 1; i$1++) {
96075               p0.y = p1.y;
96076               p1.x = p2.x;
96077               p1.y = p2.y;
96078               ring$1.getCoordinate(i$1 + 1, p2);
96079               p2.x -= x0$1;
96080               sum$1 += p1.x * (p0.y - p2.y);
96081             }
96082             return sum$1 / 2.0
96083           }
96084         };
96085         CGAlgorithms.distanceLineLine = function distanceLineLine (A, B, C, D) {
96086           if (A.equals(B)) { return CGAlgorithms.distancePointLine(A, C, D) }
96087           if (C.equals(D)) { return CGAlgorithms.distancePointLine(D, A, B) }
96088           var noIntersection = false;
96089           if (!Envelope.intersects(A, B, C, D)) {
96090             noIntersection = true;
96091           } else {
96092             var denom = (B.x - A.x) * (D.y - C.y) - (B.y - A.y) * (D.x - C.x);
96093             if (denom === 0) {
96094               noIntersection = true;
96095             } else {
96096               var rNumb = (A.y - C.y) * (D.x - C.x) - (A.x - C.x) * (D.y - C.y);
96097               var sNum = (A.y - C.y) * (B.x - A.x) - (A.x - C.x) * (B.y - A.y);
96098               var s = sNum / denom;
96099               var r = rNumb / denom;
96100               if (r < 0 || r > 1 || s < 0 || s > 1) {
96101                 noIntersection = true;
96102               }
96103             }
96104           }
96105           if (noIntersection) {
96106             return MathUtil.min(CGAlgorithms.distancePointLine(A, C, D), CGAlgorithms.distancePointLine(B, C, D), CGAlgorithms.distancePointLine(C, A, B), CGAlgorithms.distancePointLine(D, A, B))
96107           }
96108           return 0.0
96109         };
96110         CGAlgorithms.isPointInRing = function isPointInRing (p, ring) {
96111           return CGAlgorithms.locatePointInRing(p, ring) !== Location.EXTERIOR
96112         };
96113         CGAlgorithms.computeLength = function computeLength (pts) {
96114           var n = pts.size();
96115           if (n <= 1) { return 0.0 }
96116           var len = 0.0;
96117           var p = new Coordinate();
96118           pts.getCoordinate(0, p);
96119           var x0 = p.x;
96120           var y0 = p.y;
96121           for (var i = 1; i < n; i++) {
96122             pts.getCoordinate(i, p);
96123             var x1 = p.x;
96124             var y1 = p.y;
96125             var dx = x1 - x0;
96126             var dy = y1 - y0;
96127             len += Math.sqrt(dx * dx + dy * dy);
96128             x0 = x1;
96129             y0 = y1;
96130           }
96131           return len
96132         };
96133         CGAlgorithms.isCCW = function isCCW (ring) {
96134           var nPts = ring.length - 1;
96135           if (nPts < 3) { throw new IllegalArgumentException('Ring has fewer than 4 points, so orientation cannot be determined') }
96136           var hiPt = ring[0];
96137           var hiIndex = 0;
96138           for (var i = 1; i <= nPts; i++) {
96139             var p = ring[i];
96140             if (p.y > hiPt.y) {
96141               hiPt = p;
96142               hiIndex = i;
96143             }
96144           }
96145           var iPrev = hiIndex;
96146           do {
96147             iPrev = iPrev - 1;
96148             if (iPrev < 0) { iPrev = nPts; }
96149           } while (ring[iPrev].equals2D(hiPt) && iPrev !== hiIndex)
96150           var iNext = hiIndex;
96151           do {
96152             iNext = (iNext + 1) % nPts;
96153           } while (ring[iNext].equals2D(hiPt) && iNext !== hiIndex)
96154           var prev = ring[iPrev];
96155           var next = ring[iNext];
96156           if (prev.equals2D(hiPt) || next.equals2D(hiPt) || prev.equals2D(next)) { return false }
96157           var disc = CGAlgorithms.computeOrientation(prev, hiPt, next);
96158           var isCCW = false;
96159           if (disc === 0) {
96160             isCCW = prev.x > next.x;
96161           } else {
96162             isCCW = disc > 0;
96163           }
96164           return isCCW
96165         };
96166         CGAlgorithms.locatePointInRing = function locatePointInRing (p, ring) {
96167           return RayCrossingCounter.locatePointInRing(p, ring)
96168         };
96169         CGAlgorithms.distancePointLinePerpendicular = function distancePointLinePerpendicular (p, A, B) {
96170           var len2 = (B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y);
96171           var s = ((A.y - p.y) * (B.x - A.x) - (A.x - p.x) * (B.y - A.y)) / len2;
96172           return Math.abs(s) * Math.sqrt(len2)
96173         };
96174         CGAlgorithms.computeOrientation = function computeOrientation (p1, p2, q) {
96175           return CGAlgorithms.orientationIndex(p1, p2, q)
96176         };
96177         CGAlgorithms.distancePointLine = function distancePointLine () {
96178           if (arguments.length === 2) {
96179             var p = arguments[0];
96180             var line = arguments[1];
96181             if (line.length === 0) { throw new IllegalArgumentException('Line array must contain at least one vertex') }
96182             var minDistance = p.distance(line[0]);
96183             for (var i = 0; i < line.length - 1; i++) {
96184               var dist = CGAlgorithms.distancePointLine(p, line[i], line[i + 1]);
96185               if (dist < minDistance) {
96186                 minDistance = dist;
96187               }
96188             }
96189             return minDistance
96190           } else if (arguments.length === 3) {
96191             var p$1 = arguments[0];
96192             var A = arguments[1];
96193             var B = arguments[2];
96194             if (A.x === B.x && A.y === B.y) { return p$1.distance(A) }
96195             var len2 = (B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y);
96196             var r = ((p$1.x - A.x) * (B.x - A.x) + (p$1.y - A.y) * (B.y - A.y)) / len2;
96197             if (r <= 0.0) { return p$1.distance(A) }
96198             if (r >= 1.0) { return p$1.distance(B) }
96199             var s = ((A.y - p$1.y) * (B.x - A.x) - (A.x - p$1.x) * (B.y - A.y)) / len2;
96200             return Math.abs(s) * Math.sqrt(len2)
96201           }
96202         };
96203         CGAlgorithms.isOnLine = function isOnLine (p, pt) {
96204           var lineIntersector = new RobustLineIntersector();
96205           for (var i = 1; i < pt.length; i++) {
96206             var p0 = pt[i - 1];
96207             var p1 = pt[i];
96208             lineIntersector.computeIntersection(p, p0, p1);
96209             if (lineIntersector.hasIntersection()) {
96210               return true
96211             }
96212           }
96213           return false
96214         };
96215         staticAccessors$3.CLOCKWISE.get = function () { return -1 };
96216         staticAccessors$3.RIGHT.get = function () { return CGAlgorithms.CLOCKWISE };
96217         staticAccessors$3.COUNTERCLOCKWISE.get = function () { return 1 };
96218         staticAccessors$3.LEFT.get = function () { return CGAlgorithms.COUNTERCLOCKWISE };
96219         staticAccessors$3.COLLINEAR.get = function () { return 0 };
96220         staticAccessors$3.STRAIGHT.get = function () { return CGAlgorithms.COLLINEAR };
96221
96222         Object.defineProperties( CGAlgorithms, staticAccessors$3 );
96223
96224         var GeometryComponentFilter = function GeometryComponentFilter () {};
96225
96226         GeometryComponentFilter.prototype.filter = function filter (geom) {};
96227         GeometryComponentFilter.prototype.interfaces_ = function interfaces_ () {
96228           return []
96229         };
96230         GeometryComponentFilter.prototype.getClass = function getClass () {
96231           return GeometryComponentFilter
96232         };
96233
96234         var Geometry = function Geometry () {
96235           var factory = arguments[0];
96236
96237           this._envelope = null;
96238           this._factory = null;
96239           this._SRID = null;
96240           this._userData = null;
96241           this._factory = factory;
96242           this._SRID = factory.getSRID();
96243         };
96244
96245         var staticAccessors$11 = { serialVersionUID: { configurable: true },SORTINDEX_POINT: { configurable: true },SORTINDEX_MULTIPOINT: { configurable: true },SORTINDEX_LINESTRING: { configurable: true },SORTINDEX_LINEARRING: { configurable: true },SORTINDEX_MULTILINESTRING: { configurable: true },SORTINDEX_POLYGON: { configurable: true },SORTINDEX_MULTIPOLYGON: { configurable: true },SORTINDEX_GEOMETRYCOLLECTION: { configurable: true },geometryChangedFilter: { configurable: true } };
96246         Geometry.prototype.isGeometryCollection = function isGeometryCollection () {
96247           return this.getSortIndex() === Geometry.SORTINDEX_GEOMETRYCOLLECTION
96248         };
96249         Geometry.prototype.getFactory = function getFactory () {
96250           return this._factory
96251         };
96252         Geometry.prototype.getGeometryN = function getGeometryN (n) {
96253           return this
96254         };
96255         Geometry.prototype.getArea = function getArea () {
96256           return 0.0
96257         };
96258         Geometry.prototype.isRectangle = function isRectangle () {
96259           return false
96260         };
96261         Geometry.prototype.equals = function equals () {
96262           if (arguments[0] instanceof Geometry) {
96263             var g$1 = arguments[0];
96264             if (g$1 === null) { return false }
96265             return this.equalsTopo(g$1)
96266           } else if (arguments[0] instanceof Object) {
96267             var o = arguments[0];
96268             if (!(o instanceof Geometry)) { return false }
96269             var g = o;
96270             return this.equalsExact(g)
96271           }
96272         };
96273         Geometry.prototype.equalsExact = function equalsExact (other) {
96274           return this === other || this.equalsExact(other, 0)
96275         };
96276         Geometry.prototype.geometryChanged = function geometryChanged () {
96277           this.apply(Geometry.geometryChangedFilter);
96278         };
96279         Geometry.prototype.geometryChangedAction = function geometryChangedAction () {
96280           this._envelope = null;
96281         };
96282         Geometry.prototype.equalsNorm = function equalsNorm (g) {
96283           if (g === null) { return false }
96284           return this.norm().equalsExact(g.norm())
96285         };
96286         Geometry.prototype.getLength = function getLength () {
96287           return 0.0
96288         };
96289         Geometry.prototype.getNumGeometries = function getNumGeometries () {
96290           return 1
96291         };
96292         Geometry.prototype.compareTo = function compareTo () {
96293           if (arguments.length === 1) {
96294             var o = arguments[0];
96295             var other = o;
96296             if (this.getSortIndex() !== other.getSortIndex()) {
96297               return this.getSortIndex() - other.getSortIndex()
96298             }
96299             if (this.isEmpty() && other.isEmpty()) {
96300               return 0
96301             }
96302             if (this.isEmpty()) {
96303               return -1
96304             }
96305             if (other.isEmpty()) {
96306               return 1
96307             }
96308             return this.compareToSameClass(o)
96309           } else if (arguments.length === 2) {
96310             var other$1 = arguments[0];
96311             var comp = arguments[1];
96312             if (this.getSortIndex() !== other$1.getSortIndex()) {
96313               return this.getSortIndex() - other$1.getSortIndex()
96314             }
96315             if (this.isEmpty() && other$1.isEmpty()) {
96316               return 0
96317             }
96318             if (this.isEmpty()) {
96319               return -1
96320             }
96321             if (other$1.isEmpty()) {
96322               return 1
96323             }
96324             return this.compareToSameClass(other$1, comp)
96325           }
96326         };
96327         Geometry.prototype.getUserData = function getUserData () {
96328           return this._userData
96329         };
96330         Geometry.prototype.getSRID = function getSRID () {
96331           return this._SRID
96332         };
96333         Geometry.prototype.getEnvelope = function getEnvelope () {
96334           return this.getFactory().toGeometry(this.getEnvelopeInternal())
96335         };
96336         Geometry.prototype.checkNotGeometryCollection = function checkNotGeometryCollection (g) {
96337           if (g.getSortIndex() === Geometry.SORTINDEX_GEOMETRYCOLLECTION) {
96338             throw new IllegalArgumentException('This method does not support GeometryCollection arguments')
96339           }
96340         };
96341         Geometry.prototype.equal = function equal (a, b, tolerance) {
96342           if (tolerance === 0) {
96343             return a.equals(b)
96344           }
96345           return a.distance(b) <= tolerance
96346         };
96347         Geometry.prototype.norm = function norm () {
96348           var copy = this.copy();
96349           copy.normalize();
96350           return copy
96351         };
96352         Geometry.prototype.getPrecisionModel = function getPrecisionModel () {
96353           return this._factory.getPrecisionModel()
96354         };
96355         Geometry.prototype.getEnvelopeInternal = function getEnvelopeInternal () {
96356           if (this._envelope === null) {
96357             this._envelope = this.computeEnvelopeInternal();
96358           }
96359           return new Envelope(this._envelope)
96360         };
96361         Geometry.prototype.setSRID = function setSRID (SRID) {
96362           this._SRID = SRID;
96363         };
96364         Geometry.prototype.setUserData = function setUserData (userData) {
96365           this._userData = userData;
96366         };
96367         Geometry.prototype.compare = function compare (a, b) {
96368           var i = a.iterator();
96369           var j = b.iterator();
96370           while (i.hasNext() && j.hasNext()) {
96371             var aElement = i.next();
96372             var bElement = j.next();
96373             var comparison = aElement.compareTo(bElement);
96374             if (comparison !== 0) {
96375               return comparison
96376             }
96377           }
96378           if (i.hasNext()) {
96379             return 1
96380           }
96381           if (j.hasNext()) {
96382             return -1
96383           }
96384           return 0
96385         };
96386         Geometry.prototype.hashCode = function hashCode () {
96387           return this.getEnvelopeInternal().hashCode()
96388         };
96389         Geometry.prototype.isGeometryCollectionOrDerived = function isGeometryCollectionOrDerived () {
96390           if (this.getSortIndex() === Geometry.SORTINDEX_GEOMETRYCOLLECTION || this.getSortIndex() === Geometry.SORTINDEX_MULTIPOINT || this.getSortIndex() === Geometry.SORTINDEX_MULTILINESTRING || this.getSortIndex() === Geometry.SORTINDEX_MULTIPOLYGON) {
96391             return true
96392           }
96393           return false
96394         };
96395         Geometry.prototype.interfaces_ = function interfaces_ () {
96396           return [Clonable, Comparable, Serializable]
96397         };
96398         Geometry.prototype.getClass = function getClass () {
96399           return Geometry
96400         };
96401         Geometry.hasNonEmptyElements = function hasNonEmptyElements (geometries) {
96402           for (var i = 0; i < geometries.length; i++) {
96403             if (!geometries[i].isEmpty()) {
96404               return true
96405             }
96406           }
96407           return false
96408         };
96409         Geometry.hasNullElements = function hasNullElements (array) {
96410           for (var i = 0; i < array.length; i++) {
96411             if (array[i] === null) {
96412               return true
96413             }
96414           }
96415           return false
96416         };
96417         staticAccessors$11.serialVersionUID.get = function () { return 8763622679187376702 };
96418         staticAccessors$11.SORTINDEX_POINT.get = function () { return 0 };
96419         staticAccessors$11.SORTINDEX_MULTIPOINT.get = function () { return 1 };
96420         staticAccessors$11.SORTINDEX_LINESTRING.get = function () { return 2 };
96421         staticAccessors$11.SORTINDEX_LINEARRING.get = function () { return 3 };
96422         staticAccessors$11.SORTINDEX_MULTILINESTRING.get = function () { return 4 };
96423         staticAccessors$11.SORTINDEX_POLYGON.get = function () { return 5 };
96424         staticAccessors$11.SORTINDEX_MULTIPOLYGON.get = function () { return 6 };
96425         staticAccessors$11.SORTINDEX_GEOMETRYCOLLECTION.get = function () { return 7 };
96426         staticAccessors$11.geometryChangedFilter.get = function () { return geometryChangedFilter };
96427
96428         Object.defineProperties( Geometry, staticAccessors$11 );
96429
96430         var geometryChangedFilter = function geometryChangedFilter () {};
96431
96432         geometryChangedFilter.interfaces_ = function interfaces_ () {
96433           return [GeometryComponentFilter]
96434         };
96435         geometryChangedFilter.filter = function filter (geom) {
96436           geom.geometryChangedAction();
96437         };
96438
96439         var CoordinateFilter = function CoordinateFilter () {};
96440
96441         CoordinateFilter.prototype.filter = function filter (coord) {};
96442         CoordinateFilter.prototype.interfaces_ = function interfaces_ () {
96443           return []
96444         };
96445         CoordinateFilter.prototype.getClass = function getClass () {
96446           return CoordinateFilter
96447         };
96448
96449         var BoundaryNodeRule = function BoundaryNodeRule () {};
96450
96451         var staticAccessors$12 = { Mod2BoundaryNodeRule: { configurable: true },EndPointBoundaryNodeRule: { configurable: true },MultiValentEndPointBoundaryNodeRule: { configurable: true },MonoValentEndPointBoundaryNodeRule: { configurable: true },MOD2_BOUNDARY_RULE: { configurable: true },ENDPOINT_BOUNDARY_RULE: { configurable: true },MULTIVALENT_ENDPOINT_BOUNDARY_RULE: { configurable: true },MONOVALENT_ENDPOINT_BOUNDARY_RULE: { configurable: true },OGC_SFS_BOUNDARY_RULE: { configurable: true } };
96452
96453         BoundaryNodeRule.prototype.isInBoundary = function isInBoundary (boundaryCount) {};
96454         BoundaryNodeRule.prototype.interfaces_ = function interfaces_ () {
96455           return []
96456         };
96457         BoundaryNodeRule.prototype.getClass = function getClass () {
96458           return BoundaryNodeRule
96459         };
96460         staticAccessors$12.Mod2BoundaryNodeRule.get = function () { return Mod2BoundaryNodeRule };
96461         staticAccessors$12.EndPointBoundaryNodeRule.get = function () { return EndPointBoundaryNodeRule };
96462         staticAccessors$12.MultiValentEndPointBoundaryNodeRule.get = function () { return MultiValentEndPointBoundaryNodeRule };
96463         staticAccessors$12.MonoValentEndPointBoundaryNodeRule.get = function () { return MonoValentEndPointBoundaryNodeRule };
96464         staticAccessors$12.MOD2_BOUNDARY_RULE.get = function () { return new Mod2BoundaryNodeRule() };
96465         staticAccessors$12.ENDPOINT_BOUNDARY_RULE.get = function () { return new EndPointBoundaryNodeRule() };
96466         staticAccessors$12.MULTIVALENT_ENDPOINT_BOUNDARY_RULE.get = function () { return new MultiValentEndPointBoundaryNodeRule() };
96467         staticAccessors$12.MONOVALENT_ENDPOINT_BOUNDARY_RULE.get = function () { return new MonoValentEndPointBoundaryNodeRule() };
96468         staticAccessors$12.OGC_SFS_BOUNDARY_RULE.get = function () { return BoundaryNodeRule.MOD2_BOUNDARY_RULE };
96469
96470         Object.defineProperties( BoundaryNodeRule, staticAccessors$12 );
96471
96472         var Mod2BoundaryNodeRule = function Mod2BoundaryNodeRule () {};
96473
96474         Mod2BoundaryNodeRule.prototype.isInBoundary = function isInBoundary (boundaryCount) {
96475           return boundaryCount % 2 === 1
96476         };
96477         Mod2BoundaryNodeRule.prototype.interfaces_ = function interfaces_ () {
96478           return [BoundaryNodeRule]
96479         };
96480         Mod2BoundaryNodeRule.prototype.getClass = function getClass () {
96481           return Mod2BoundaryNodeRule
96482         };
96483
96484         var EndPointBoundaryNodeRule = function EndPointBoundaryNodeRule () {};
96485
96486         EndPointBoundaryNodeRule.prototype.isInBoundary = function isInBoundary (boundaryCount) {
96487           return boundaryCount > 0
96488         };
96489         EndPointBoundaryNodeRule.prototype.interfaces_ = function interfaces_ () {
96490           return [BoundaryNodeRule]
96491         };
96492         EndPointBoundaryNodeRule.prototype.getClass = function getClass () {
96493           return EndPointBoundaryNodeRule
96494         };
96495
96496         var MultiValentEndPointBoundaryNodeRule = function MultiValentEndPointBoundaryNodeRule () {};
96497
96498         MultiValentEndPointBoundaryNodeRule.prototype.isInBoundary = function isInBoundary (boundaryCount) {
96499           return boundaryCount > 1
96500         };
96501         MultiValentEndPointBoundaryNodeRule.prototype.interfaces_ = function interfaces_ () {
96502           return [BoundaryNodeRule]
96503         };
96504         MultiValentEndPointBoundaryNodeRule.prototype.getClass = function getClass () {
96505           return MultiValentEndPointBoundaryNodeRule
96506         };
96507
96508         var MonoValentEndPointBoundaryNodeRule = function MonoValentEndPointBoundaryNodeRule () {};
96509
96510         MonoValentEndPointBoundaryNodeRule.prototype.isInBoundary = function isInBoundary (boundaryCount) {
96511           return boundaryCount === 1
96512         };
96513         MonoValentEndPointBoundaryNodeRule.prototype.interfaces_ = function interfaces_ () {
96514           return [BoundaryNodeRule]
96515         };
96516         MonoValentEndPointBoundaryNodeRule.prototype.getClass = function getClass () {
96517           return MonoValentEndPointBoundaryNodeRule
96518         };
96519
96520         // import Iterator from './Iterator'
96521
96522         /**
96523          * @see http://download.oracle.com/javase/6/docs/api/java/util/Collection.html
96524          *
96525          * @constructor
96526          * @private
96527          */
96528         var Collection = function Collection () {};
96529
96530         Collection.prototype.add = function add () {};
96531
96532         /**
96533          * Appends all of the elements in the specified collection to the end of this
96534          * list, in the order that they are returned by the specified collection's
96535          * iterator (optional operation).
96536          * @param {javascript.util.Collection} c
96537          * @return {boolean}
96538          */
96539         Collection.prototype.addAll = function addAll () {};
96540
96541         /**
96542          * Returns true if this collection contains no elements.
96543          * @return {boolean}
96544          */
96545         Collection.prototype.isEmpty = function isEmpty () {};
96546
96547         /**
96548          * Returns an iterator over the elements in this collection.
96549          * @return {javascript.util.Iterator}
96550          */
96551         Collection.prototype.iterator = function iterator () {};
96552
96553         /**
96554          * Returns an iterator over the elements in this collection.
96555          * @return {number}
96556          */
96557         Collection.prototype.size = function size () {};
96558
96559         /**
96560          * Returns an array containing all of the elements in this collection.
96561          * @return {Array}
96562          */
96563         Collection.prototype.toArray = function toArray () {};
96564
96565         /**
96566          * Removes a single instance of the specified element from this collection if it
96567          * is present. (optional)
96568          * @param {Object} e
96569          * @return {boolean}
96570          */
96571         Collection.prototype.remove = function remove () {};
96572
96573         /**
96574          * @param {string=} message Optional message
96575          * @extends {Error}
96576          * @constructor
96577          * @private
96578          */
96579         function IndexOutOfBoundsException (message) {
96580           this.message = message || '';
96581         }
96582         IndexOutOfBoundsException.prototype = new Error();
96583
96584         /**
96585          * @type {string}
96586          */
96587         IndexOutOfBoundsException.prototype.name = 'IndexOutOfBoundsException';
96588
96589         /**
96590          * @see http://download.oracle.com/javase/6/docs/api/java/util/Iterator.html
96591          * @constructor
96592          * @private
96593          */
96594         var Iterator$1 = function Iterator () {};
96595
96596         Iterator$1.prototype.hasNext = function hasNext () {};
96597
96598         /**
96599          * Returns the next element in the iteration.
96600          * @return {Object}
96601          */
96602         Iterator$1.prototype.next = function next () {};
96603
96604         /**
96605          * Removes from the underlying collection the last element returned by the
96606          * iterator (optional operation).
96607          */
96608         Iterator$1.prototype.remove = function remove () {};
96609
96610         /**
96611          * @see http://download.oracle.com/javase/6/docs/api/java/util/List.html
96612          *
96613          * @extends {javascript.util.Collection}
96614          * @constructor
96615          * @private
96616          */
96617         var List = (function (Collection$$1) {
96618           function List () {
96619             Collection$$1.apply(this, arguments);
96620           }
96621
96622           if ( Collection$$1 ) List.__proto__ = Collection$$1;
96623           List.prototype = Object.create( Collection$$1 && Collection$$1.prototype );
96624           List.prototype.constructor = List;
96625
96626           List.prototype.get = function get () { };
96627
96628           /**
96629            * Replaces the element at the specified position in this list with the
96630            * specified element (optional operation).
96631            * @param {number} index
96632            * @param {Object} e
96633            * @return {Object}
96634            */
96635           List.prototype.set = function set () { };
96636
96637           /**
96638            * Returns true if this collection contains no elements.
96639            * @return {boolean}
96640            */
96641           List.prototype.isEmpty = function isEmpty () { };
96642
96643           return List;
96644         }(Collection));
96645
96646         /**
96647          * @param {string=} message Optional message
96648          * @extends {Error}
96649          * @constructor
96650          * @private
96651          */
96652         function NoSuchElementException (message) {
96653           this.message = message || '';
96654         }
96655         NoSuchElementException.prototype = new Error();
96656
96657         /**
96658          * @type {string}
96659          */
96660         NoSuchElementException.prototype.name = 'NoSuchElementException';
96661
96662         // import OperationNotSupported from './OperationNotSupported'
96663
96664         /**
96665          * @see http://download.oracle.com/javase/6/docs/api/java/util/ArrayList.html
96666          *
96667          * @extends List
96668          * @private
96669          */
96670         var ArrayList = (function (List$$1) {
96671           function ArrayList () {
96672             List$$1.call(this);
96673             this.array_ = [];
96674
96675             if (arguments[0] instanceof Collection) {
96676               this.addAll(arguments[0]);
96677             }
96678           }
96679
96680           if ( List$$1 ) ArrayList.__proto__ = List$$1;
96681           ArrayList.prototype = Object.create( List$$1 && List$$1.prototype );
96682           ArrayList.prototype.constructor = ArrayList;
96683
96684           ArrayList.prototype.ensureCapacity = function ensureCapacity () {};
96685           ArrayList.prototype.interfaces_ = function interfaces_ () { return [List$$1, Collection] };
96686
96687           /**
96688            * @override
96689            */
96690           ArrayList.prototype.add = function add (e) {
96691             if (arguments.length === 1) {
96692               this.array_.push(e);
96693             } else {
96694               this.array_.splice(arguments[0], arguments[1]);
96695             }
96696             return true
96697           };
96698
96699           ArrayList.prototype.clear = function clear () {
96700             this.array_ = [];
96701           };
96702
96703           /**
96704            * @override
96705            */
96706           ArrayList.prototype.addAll = function addAll (c) {
96707             var this$1 = this;
96708
96709             for (var i = c.iterator(); i.hasNext();) {
96710               this$1.add(i.next());
96711             }
96712             return true
96713           };
96714
96715           /**
96716            * @override
96717            */
96718           ArrayList.prototype.set = function set (index, element) {
96719             var oldElement = this.array_[index];
96720             this.array_[index] = element;
96721             return oldElement
96722           };
96723
96724           /**
96725            * @override
96726            */
96727           ArrayList.prototype.iterator = function iterator () {
96728             return new Iterator_(this)
96729           };
96730
96731           /**
96732            * @override
96733            */
96734           ArrayList.prototype.get = function get (index) {
96735             if (index < 0 || index >= this.size()) {
96736               throw new IndexOutOfBoundsException()
96737             }
96738
96739             return this.array_[index]
96740           };
96741
96742           /**
96743            * @override
96744            */
96745           ArrayList.prototype.isEmpty = function isEmpty () {
96746             return this.array_.length === 0
96747           };
96748
96749           /**
96750            * @override
96751            */
96752           ArrayList.prototype.size = function size () {
96753             return this.array_.length
96754           };
96755
96756           /**
96757            * @override
96758            */
96759           ArrayList.prototype.toArray = function toArray () {
96760             var this$1 = this;
96761
96762             var array = [];
96763
96764             for (var i = 0, len = this.array_.length; i < len; i++) {
96765               array.push(this$1.array_[i]);
96766             }
96767
96768             return array
96769           };
96770
96771           /**
96772            * @override
96773            */
96774           ArrayList.prototype.remove = function remove (o) {
96775             var this$1 = this;
96776
96777             var found = false;
96778
96779             for (var i = 0, len = this.array_.length; i < len; i++) {
96780               if (this$1.array_[i] === o) {
96781                 this$1.array_.splice(i, 1);
96782                 found = true;
96783                 break
96784               }
96785             }
96786
96787             return found
96788           };
96789
96790           return ArrayList;
96791         }(List));
96792
96793         /**
96794          * @extends {Iterator}
96795          * @param {ArrayList} arrayList
96796          * @constructor
96797          * @private
96798          */
96799         var Iterator_ = (function (Iterator$$1) {
96800           function Iterator_ (arrayList) {
96801             Iterator$$1.call(this);
96802             /**
96803              * @type {ArrayList}
96804              * @private
96805             */
96806             this.arrayList_ = arrayList;
96807             /**
96808              * @type {number}
96809              * @private
96810             */
96811             this.position_ = 0;
96812           }
96813
96814           if ( Iterator$$1 ) Iterator_.__proto__ = Iterator$$1;
96815           Iterator_.prototype = Object.create( Iterator$$1 && Iterator$$1.prototype );
96816           Iterator_.prototype.constructor = Iterator_;
96817
96818           /**
96819            * @override
96820            */
96821           Iterator_.prototype.next = function next () {
96822             if (this.position_ === this.arrayList_.size()) {
96823               throw new NoSuchElementException()
96824             }
96825             return this.arrayList_.get(this.position_++)
96826           };
96827
96828           /**
96829            * @override
96830            */
96831           Iterator_.prototype.hasNext = function hasNext () {
96832             if (this.position_ < this.arrayList_.size()) {
96833               return true
96834             } else {
96835               return false
96836             }
96837           };
96838
96839           /**
96840            * TODO: should be in ListIterator
96841            * @override
96842            */
96843           Iterator_.prototype.set = function set (element) {
96844             return this.arrayList_.set(this.position_ - 1, element)
96845           };
96846
96847           /**
96848            * @override
96849            */
96850           Iterator_.prototype.remove = function remove () {
96851             this.arrayList_.remove(this.arrayList_.get(this.position_));
96852           };
96853
96854           return Iterator_;
96855         }(Iterator$1));
96856
96857         var CoordinateList = (function (ArrayList$$1) {
96858           function CoordinateList () {
96859             ArrayList$$1.call(this);
96860             if (arguments.length === 0) ; else if (arguments.length === 1) {
96861               var coord = arguments[0];
96862               this.ensureCapacity(coord.length);
96863               this.add(coord, true);
96864             } else if (arguments.length === 2) {
96865               var coord$1 = arguments[0];
96866               var allowRepeated = arguments[1];
96867               this.ensureCapacity(coord$1.length);
96868               this.add(coord$1, allowRepeated);
96869             }
96870           }
96871
96872           if ( ArrayList$$1 ) CoordinateList.__proto__ = ArrayList$$1;
96873           CoordinateList.prototype = Object.create( ArrayList$$1 && ArrayList$$1.prototype );
96874           CoordinateList.prototype.constructor = CoordinateList;
96875
96876           var staticAccessors = { coordArrayType: { configurable: true } };
96877           staticAccessors.coordArrayType.get = function () { return new Array(0).fill(null) };
96878           CoordinateList.prototype.getCoordinate = function getCoordinate (i) {
96879             return this.get(i)
96880           };
96881           CoordinateList.prototype.addAll = function addAll () {
96882             var this$1 = this;
96883
96884             if (arguments.length === 2) {
96885               var coll = arguments[0];
96886               var allowRepeated = arguments[1];
96887               var isChanged = false;
96888               for (var i = coll.iterator(); i.hasNext();) {
96889                 this$1.add(i.next(), allowRepeated);
96890                 isChanged = true;
96891               }
96892               return isChanged
96893             } else { return ArrayList$$1.prototype.addAll.apply(this, arguments) }
96894           };
96895           CoordinateList.prototype.clone = function clone () {
96896             var this$1 = this;
96897
96898             var clone = ArrayList$$1.prototype.clone.call(this);
96899             for (var i = 0; i < this.size(); i++) {
96900               clone.add(i, this$1.get(i).copy());
96901             }
96902             return clone
96903           };
96904           CoordinateList.prototype.toCoordinateArray = function toCoordinateArray () {
96905             return this.toArray(CoordinateList.coordArrayType)
96906           };
96907           CoordinateList.prototype.add = function add () {
96908             var this$1 = this;
96909
96910             if (arguments.length === 1) {
96911               var coord = arguments[0];
96912               ArrayList$$1.prototype.add.call(this, coord);
96913             } else if (arguments.length === 2) {
96914               if (arguments[0] instanceof Array && typeof arguments[1] === 'boolean') {
96915                 var coord$1 = arguments[0];
96916                 var allowRepeated = arguments[1];
96917                 this.add(coord$1, allowRepeated, true);
96918                 return true
96919               } else if (arguments[0] instanceof Coordinate && typeof arguments[1] === 'boolean') {
96920                 var coord$2 = arguments[0];
96921                 var allowRepeated$1 = arguments[1];
96922                 if (!allowRepeated$1) {
96923                   if (this.size() >= 1) {
96924                     var last = this.get(this.size() - 1);
96925                     if (last.equals2D(coord$2)) { return null }
96926                   }
96927                 }
96928                 ArrayList$$1.prototype.add.call(this, coord$2);
96929               } else if (arguments[0] instanceof Object && typeof arguments[1] === 'boolean') {
96930                 var obj = arguments[0];
96931                 var allowRepeated$2 = arguments[1];
96932                 this.add(obj, allowRepeated$2);
96933                 return true
96934               }
96935             } else if (arguments.length === 3) {
96936               if (typeof arguments[2] === 'boolean' && (arguments[0] instanceof Array && typeof arguments[1] === 'boolean')) {
96937                 var coord$3 = arguments[0];
96938                 var allowRepeated$3 = arguments[1];
96939                 var direction = arguments[2];
96940                 if (direction) {
96941                   for (var i$1 = 0; i$1 < coord$3.length; i$1++) {
96942                     this$1.add(coord$3[i$1], allowRepeated$3);
96943                   }
96944                 } else {
96945                   for (var i$2 = coord$3.length - 1; i$2 >= 0; i$2--) {
96946                     this$1.add(coord$3[i$2], allowRepeated$3);
96947                   }
96948                 }
96949                 return true
96950               } else if (typeof arguments[2] === 'boolean' && (Number.isInteger(arguments[0]) && arguments[1] instanceof Coordinate)) {
96951                 var i$3 = arguments[0];
96952                 var coord$4 = arguments[1];
96953                 var allowRepeated$4 = arguments[2];
96954                 if (!allowRepeated$4) {
96955                   var size = this.size();
96956                   if (size > 0) {
96957                     if (i$3 > 0) {
96958                       var prev = this.get(i$3 - 1);
96959                       if (prev.equals2D(coord$4)) { return null }
96960                     }
96961                     if (i$3 < size) {
96962                       var next = this.get(i$3);
96963                       if (next.equals2D(coord$4)) { return null }
96964                     }
96965                   }
96966                 }
96967                 ArrayList$$1.prototype.add.call(this, i$3, coord$4);
96968               }
96969             } else if (arguments.length === 4) {
96970               var coord$5 = arguments[0];
96971               var allowRepeated$5 = arguments[1];
96972               var start = arguments[2];
96973               var end = arguments[3];
96974               var inc = 1;
96975               if (start > end) { inc = -1; }
96976               for (var i = start; i !== end; i += inc) {
96977                 this$1.add(coord$5[i], allowRepeated$5);
96978               }
96979               return true
96980             }
96981           };
96982           CoordinateList.prototype.closeRing = function closeRing () {
96983             if (this.size() > 0) { this.add(new Coordinate(this.get(0)), false); }
96984           };
96985           CoordinateList.prototype.interfaces_ = function interfaces_ () {
96986             return []
96987           };
96988           CoordinateList.prototype.getClass = function getClass () {
96989             return CoordinateList
96990           };
96991
96992           Object.defineProperties( CoordinateList, staticAccessors );
96993
96994           return CoordinateList;
96995         }(ArrayList));
96996
96997         var CoordinateArrays = function CoordinateArrays () {};
96998
96999         var staticAccessors$13 = { ForwardComparator: { configurable: true },BidirectionalComparator: { configurable: true },coordArrayType: { configurable: true } };
97000
97001         staticAccessors$13.ForwardComparator.get = function () { return ForwardComparator };
97002         staticAccessors$13.BidirectionalComparator.get = function () { return BidirectionalComparator };
97003         staticAccessors$13.coordArrayType.get = function () { return new Array(0).fill(null) };
97004
97005         CoordinateArrays.prototype.interfaces_ = function interfaces_ () {
97006           return []
97007         };
97008         CoordinateArrays.prototype.getClass = function getClass () {
97009           return CoordinateArrays
97010         };
97011         CoordinateArrays.isRing = function isRing (pts) {
97012           if (pts.length < 4) { return false }
97013           if (!pts[0].equals2D(pts[pts.length - 1])) { return false }
97014           return true
97015         };
97016         CoordinateArrays.ptNotInList = function ptNotInList (testPts, pts) {
97017           for (var i = 0; i < testPts.length; i++) {
97018             var testPt = testPts[i];
97019             if (CoordinateArrays.indexOf(testPt, pts) < 0) { return testPt }
97020           }
97021           return null
97022         };
97023         CoordinateArrays.scroll = function scroll (coordinates, firstCoordinate) {
97024           var i = CoordinateArrays.indexOf(firstCoordinate, coordinates);
97025           if (i < 0) { return null }
97026           var newCoordinates = new Array(coordinates.length).fill(null);
97027           System.arraycopy(coordinates, i, newCoordinates, 0, coordinates.length - i);
97028           System.arraycopy(coordinates, 0, newCoordinates, coordinates.length - i, i);
97029           System.arraycopy(newCoordinates, 0, coordinates, 0, coordinates.length);
97030         };
97031         CoordinateArrays.equals = function equals () {
97032           if (arguments.length === 2) {
97033             var coord1 = arguments[0];
97034             var coord2 = arguments[1];
97035             if (coord1 === coord2) { return true }
97036             if (coord1 === null || coord2 === null) { return false }
97037             if (coord1.length !== coord2.length) { return false }
97038             for (var i = 0; i < coord1.length; i++) {
97039               if (!coord1[i].equals(coord2[i])) { return false }
97040             }
97041             return true
97042           } else if (arguments.length === 3) {
97043             var coord1$1 = arguments[0];
97044             var coord2$1 = arguments[1];
97045             var coordinateComparator = arguments[2];
97046             if (coord1$1 === coord2$1) { return true }
97047             if (coord1$1 === null || coord2$1 === null) { return false }
97048             if (coord1$1.length !== coord2$1.length) { return false }
97049             for (var i$1 = 0; i$1 < coord1$1.length; i$1++) {
97050               if (coordinateComparator.compare(coord1$1[i$1], coord2$1[i$1]) !== 0) { return false }
97051             }
97052             return true
97053           }
97054         };
97055         CoordinateArrays.intersection = function intersection (coordinates, env) {
97056           var coordList = new CoordinateList();
97057           for (var i = 0; i < coordinates.length; i++) {
97058             if (env.intersects(coordinates[i])) { coordList.add(coordinates[i], true); }
97059           }
97060           return coordList.toCoordinateArray()
97061         };
97062         CoordinateArrays.hasRepeatedPoints = function hasRepeatedPoints (coord) {
97063           for (var i = 1; i < coord.length; i++) {
97064             if (coord[i - 1].equals(coord[i])) {
97065               return true
97066             }
97067           }
97068           return false
97069         };
97070         CoordinateArrays.removeRepeatedPoints = function removeRepeatedPoints (coord) {
97071           if (!CoordinateArrays.hasRepeatedPoints(coord)) { return coord }
97072           var coordList = new CoordinateList(coord, false);
97073           return coordList.toCoordinateArray()
97074         };
97075         CoordinateArrays.reverse = function reverse (coord) {
97076           var last = coord.length - 1;
97077           var mid = Math.trunc(last / 2);
97078           for (var i = 0; i <= mid; i++) {
97079             var tmp = coord[i];
97080             coord[i] = coord[last - i];
97081             coord[last - i] = tmp;
97082           }
97083         };
97084         CoordinateArrays.removeNull = function removeNull (coord) {
97085           var nonNull = 0;
97086           for (var i = 0; i < coord.length; i++) {
97087             if (coord[i] !== null) { nonNull++; }
97088           }
97089           var newCoord = new Array(nonNull).fill(null);
97090           if (nonNull === 0) { return newCoord }
97091           var j = 0;
97092           for (var i$1 = 0; i$1 < coord.length; i$1++) {
97093             if (coord[i$1] !== null) { newCoord[j++] = coord[i$1]; }
97094           }
97095           return newCoord
97096         };
97097         CoordinateArrays.copyDeep = function copyDeep () {
97098           if (arguments.length === 1) {
97099             var coordinates = arguments[0];
97100             var copy = new Array(coordinates.length).fill(null);
97101             for (var i = 0; i < coordinates.length; i++) {
97102               copy[i] = new Coordinate(coordinates[i]);
97103             }
97104             return copy
97105           } else if (arguments.length === 5) {
97106             var src = arguments[0];
97107             var srcStart = arguments[1];
97108             var dest = arguments[2];
97109             var destStart = arguments[3];
97110             var length = arguments[4];
97111             for (var i$1 = 0; i$1 < length; i$1++) {
97112               dest[destStart + i$1] = new Coordinate(src[srcStart + i$1]);
97113             }
97114           }
97115         };
97116         CoordinateArrays.isEqualReversed = function isEqualReversed (pts1, pts2) {
97117           for (var i = 0; i < pts1.length; i++) {
97118             var p1 = pts1[i];
97119             var p2 = pts2[pts1.length - i - 1];
97120             if (p1.compareTo(p2) !== 0) { return false }
97121           }
97122           return true
97123         };
97124         CoordinateArrays.envelope = function envelope (coordinates) {
97125           var env = new Envelope();
97126           for (var i = 0; i < coordinates.length; i++) {
97127             env.expandToInclude(coordinates[i]);
97128           }
97129           return env
97130         };
97131         CoordinateArrays.toCoordinateArray = function toCoordinateArray (coordList) {
97132           return coordList.toArray(CoordinateArrays.coordArrayType)
97133         };
97134         CoordinateArrays.atLeastNCoordinatesOrNothing = function atLeastNCoordinatesOrNothing (n, c) {
97135           return c.length >= n ? c : []
97136         };
97137         CoordinateArrays.indexOf = function indexOf (coordinate, coordinates) {
97138           for (var i = 0; i < coordinates.length; i++) {
97139             if (coordinate.equals(coordinates[i])) {
97140               return i
97141             }
97142           }
97143           return -1
97144         };
97145         CoordinateArrays.increasingDirection = function increasingDirection (pts) {
97146           for (var i = 0; i < Math.trunc(pts.length / 2); i++) {
97147             var j = pts.length - 1 - i;
97148             var comp = pts[i].compareTo(pts[j]);
97149             if (comp !== 0) { return comp }
97150           }
97151           return 1
97152         };
97153         CoordinateArrays.compare = function compare (pts1, pts2) {
97154           var i = 0;
97155           while (i < pts1.length && i < pts2.length) {
97156             var compare = pts1[i].compareTo(pts2[i]);
97157             if (compare !== 0) { return compare }
97158             i++;
97159           }
97160           if (i < pts2.length) { return -1 }
97161           if (i < pts1.length) { return 1 }
97162           return 0
97163         };
97164         CoordinateArrays.minCoordinate = function minCoordinate (coordinates) {
97165           var minCoord = null;
97166           for (var i = 0; i < coordinates.length; i++) {
97167             if (minCoord === null || minCoord.compareTo(coordinates[i]) > 0) {
97168               minCoord = coordinates[i];
97169             }
97170           }
97171           return minCoord
97172         };
97173         CoordinateArrays.extract = function extract (pts, start, end) {
97174           start = MathUtil.clamp(start, 0, pts.length);
97175           end = MathUtil.clamp(end, -1, pts.length);
97176           var npts = end - start + 1;
97177           if (end < 0) { npts = 0; }
97178           if (start >= pts.length) { npts = 0; }
97179           if (end < start) { npts = 0; }
97180           var extractPts = new Array(npts).fill(null);
97181           if (npts === 0) { return extractPts }
97182           var iPts = 0;
97183           for (var i = start; i <= end; i++) {
97184             extractPts[iPts++] = pts[i];
97185           }
97186           return extractPts
97187         };
97188
97189         Object.defineProperties( CoordinateArrays, staticAccessors$13 );
97190
97191         var ForwardComparator = function ForwardComparator () {};
97192
97193         ForwardComparator.prototype.compare = function compare (o1, o2) {
97194           var pts1 = o1;
97195           var pts2 = o2;
97196           return CoordinateArrays.compare(pts1, pts2)
97197         };
97198         ForwardComparator.prototype.interfaces_ = function interfaces_ () {
97199           return [Comparator]
97200         };
97201         ForwardComparator.prototype.getClass = function getClass () {
97202           return ForwardComparator
97203         };
97204
97205         var BidirectionalComparator = function BidirectionalComparator () {};
97206
97207         BidirectionalComparator.prototype.compare = function compare (o1, o2) {
97208           var pts1 = o1;
97209           var pts2 = o2;
97210           if (pts1.length < pts2.length) { return -1 }
97211           if (pts1.length > pts2.length) { return 1 }
97212           if (pts1.length === 0) { return 0 }
97213           var forwardComp = CoordinateArrays.compare(pts1, pts2);
97214           var isEqualRev = CoordinateArrays.isEqualReversed(pts1, pts2);
97215           if (isEqualRev) { return 0 }
97216           return forwardComp
97217         };
97218         BidirectionalComparator.prototype.OLDcompare = function OLDcompare (o1, o2) {
97219           var pts1 = o1;
97220           var pts2 = o2;
97221           if (pts1.length < pts2.length) { return -1 }
97222           if (pts1.length > pts2.length) { return 1 }
97223           if (pts1.length === 0) { return 0 }
97224           var dir1 = CoordinateArrays.increasingDirection(pts1);
97225           var dir2 = CoordinateArrays.increasingDirection(pts2);
97226           var i1 = dir1 > 0 ? 0 : pts1.length - 1;
97227           var i2 = dir2 > 0 ? 0 : pts1.length - 1;
97228           for (var i = 0; i < pts1.length; i++) {
97229             var comparePt = pts1[i1].compareTo(pts2[i2]);
97230             if (comparePt !== 0) { return comparePt }
97231             i1 += dir1;
97232             i2 += dir2;
97233           }
97234           return 0
97235         };
97236         BidirectionalComparator.prototype.interfaces_ = function interfaces_ () {
97237           return [Comparator]
97238         };
97239         BidirectionalComparator.prototype.getClass = function getClass () {
97240           return BidirectionalComparator
97241         };
97242
97243         /**
97244          * @see http://download.oracle.com/javase/6/docs/api/java/util/Map.html
97245          *
97246          * @constructor
97247          * @private
97248          */
97249         var Map$1$1 = function Map () {};
97250
97251         Map$1$1.prototype.get = function get () {};
97252         /**
97253          * Associates the specified value with the specified key in this map (optional
97254          * operation).
97255          * @param {Object} key
97256          * @param {Object} value
97257          * @return {Object}
97258          */
97259         Map$1$1.prototype.put = function put () {};
97260
97261         /**
97262          * Returns the number of key-value mappings in this map.
97263          * @return {number}
97264          */
97265         Map$1$1.prototype.size = function size () {};
97266
97267         /**
97268          * Returns a Collection view of the values contained in this map.
97269          * @return {javascript.util.Collection}
97270          */
97271         Map$1$1.prototype.values = function values () {};
97272
97273         /**
97274          * Returns a {@link Set} view of the mappings contained in this map.
97275          * The set is backed by the map, so changes to the map are
97276          * reflected in the set, and vice-versa.If the map is modified
97277          * while an iteration over the set is in progress (except through
97278          * the iterator's own <tt>remove</tt> operation, or through the
97279          * <tt>setValue</tt> operation on a map entry returned by the
97280          * iterator) the results of the iteration are undefined.The set
97281          * supports element removal, which removes the corresponding
97282          * mapping from the map, via the <tt>Iterator.remove</tt>,
97283          * <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt> and
97284          * <tt>clear</tt> operations.It does not support the
97285          * <tt>add</tt> or <tt>addAll</tt> operations.
97286          *
97287          * @return {Set} a set view of the mappings contained in this map
97288          */
97289         Map$1$1.prototype.entrySet = function entrySet () {};
97290
97291         /**
97292          * @see http://download.oracle.com/javase/6/docs/api/java/util/SortedMap.html
97293          *
97294          * @extends {Map}
97295          * @constructor
97296          * @private
97297          */
97298         var SortedMap = (function (Map) {
97299                 function SortedMap () {
97300                         Map.apply(this, arguments);
97301                 }if ( Map ) SortedMap.__proto__ = Map;
97302                 SortedMap.prototype = Object.create( Map && Map.prototype );
97303                 SortedMap.prototype.constructor = SortedMap;
97304
97305                 
97306
97307                 return SortedMap;
97308         }(Map$1$1));
97309
97310         /**
97311          * @param {string=} message Optional message
97312          * @extends {Error}
97313          * @constructor
97314          * @private
97315          */
97316         function OperationNotSupported (message) {
97317           this.message = message || '';
97318         }
97319         OperationNotSupported.prototype = new Error();
97320
97321         /**
97322          * @type {string}
97323          */
97324         OperationNotSupported.prototype.name = 'OperationNotSupported';
97325
97326         /**
97327          * @see http://download.oracle.com/javase/6/docs/api/java/util/Set.html
97328          *
97329          * @extends {Collection}
97330          * @constructor
97331          * @private
97332          */
97333         function Set$2() {}
97334         Set$2.prototype = new Collection();
97335
97336
97337         /**
97338          * Returns true if this set contains the specified element. More formally,
97339          * returns true if and only if this set contains an element e such that (o==null ?
97340          * e==null : o.equals(e)).
97341          * @param {Object} e
97342          * @return {boolean}
97343          */
97344         Set$2.prototype.contains = function() {};
97345
97346         /**
97347          * @see http://docs.oracle.com/javase/6/docs/api/java/util/HashSet.html
97348          *
97349          * @extends {javascript.util.Set}
97350          * @constructor
97351          * @private
97352          */
97353         var HashSet = (function (Set$$1) {
97354           function HashSet () {
97355             Set$$1.call(this);
97356             this.array_ = [];
97357
97358             if (arguments[0] instanceof Collection) {
97359               this.addAll(arguments[0]);
97360             }
97361           }
97362
97363           if ( Set$$1 ) HashSet.__proto__ = Set$$1;
97364           HashSet.prototype = Object.create( Set$$1 && Set$$1.prototype );
97365           HashSet.prototype.constructor = HashSet;
97366
97367           /**
97368            * @override
97369            */
97370           HashSet.prototype.contains = function contains (o) {
97371             var this$1 = this;
97372
97373             for (var i = 0, len = this.array_.length; i < len; i++) {
97374               var e = this$1.array_[i];
97375               if (e === o) {
97376                 return true
97377               }
97378             }
97379             return false
97380           };
97381
97382           /**
97383            * @override
97384            */
97385           HashSet.prototype.add = function add (o) {
97386             if (this.contains(o)) {
97387               return false
97388             }
97389
97390             this.array_.push(o);
97391
97392             return true
97393           };
97394
97395           /**
97396            * @override
97397            */
97398           HashSet.prototype.addAll = function addAll (c) {
97399             var this$1 = this;
97400
97401             for (var i = c.iterator(); i.hasNext();) {
97402               this$1.add(i.next());
97403             }
97404             return true
97405           };
97406
97407           /**
97408            * @override
97409            */
97410           HashSet.prototype.remove = function remove (o) {
97411             // throw new javascript.util.OperationNotSupported()
97412             throw new Error()
97413           };
97414
97415           /**
97416            * @override
97417            */
97418           HashSet.prototype.size = function size () {
97419             return this.array_.length
97420           };
97421
97422           /**
97423            * @override
97424            */
97425           HashSet.prototype.isEmpty = function isEmpty () {
97426             return this.array_.length === 0
97427           };
97428
97429           /**
97430            * @override
97431            */
97432           HashSet.prototype.toArray = function toArray () {
97433             var this$1 = this;
97434
97435             var array = [];
97436
97437             for (var i = 0, len = this.array_.length; i < len; i++) {
97438               array.push(this$1.array_[i]);
97439             }
97440
97441             return array
97442           };
97443
97444           /**
97445            * @override
97446            */
97447           HashSet.prototype.iterator = function iterator () {
97448             return new Iterator_$1(this)
97449           };
97450
97451           return HashSet;
97452         }(Set$2));
97453
97454         /**
97455            * @extends {Iterator}
97456            * @param {HashSet} hashSet
97457            * @constructor
97458            * @private
97459            */
97460         var Iterator_$1 = (function (Iterator$$1) {
97461           function Iterator_ (hashSet) {
97462             Iterator$$1.call(this);
97463             /**
97464              * @type {HashSet}
97465              * @private
97466              */
97467             this.hashSet_ = hashSet;
97468             /**
97469              * @type {number}
97470              * @private
97471              */
97472             this.position_ = 0;
97473           }
97474
97475           if ( Iterator$$1 ) Iterator_.__proto__ = Iterator$$1;
97476           Iterator_.prototype = Object.create( Iterator$$1 && Iterator$$1.prototype );
97477           Iterator_.prototype.constructor = Iterator_;
97478
97479           /**
97480            * @override
97481            */
97482           Iterator_.prototype.next = function next () {
97483             if (this.position_ === this.hashSet_.size()) {
97484               throw new NoSuchElementException()
97485             }
97486             return this.hashSet_.array_[this.position_++]
97487           };
97488
97489           /**
97490            * @override
97491            */
97492           Iterator_.prototype.hasNext = function hasNext () {
97493             if (this.position_ < this.hashSet_.size()) {
97494               return true
97495             } else {
97496               return false
97497             }
97498           };
97499
97500           /**
97501            * @override
97502            */
97503           Iterator_.prototype.remove = function remove () {
97504             throw new OperationNotSupported()
97505           };
97506
97507           return Iterator_;
97508         }(Iterator$1));
97509
97510         var BLACK = 0;
97511         var RED = 1;
97512         function colorOf (p) { return (p === null ? BLACK : p.color) }
97513         function parentOf (p) { return (p === null ? null : p.parent) }
97514         function setColor (p, c) { if (p !== null) { p.color = c; } }
97515         function leftOf (p) { return (p === null ? null : p.left) }
97516         function rightOf (p) { return (p === null ? null : p.right) }
97517
97518         /**
97519          * @see http://download.oracle.com/javase/6/docs/api/java/util/TreeMap.html
97520          *
97521          * @extends {SortedMap}
97522          * @constructor
97523          * @private
97524          */
97525         function TreeMap () {
97526           /**
97527            * @type {Object}
97528            * @private
97529            */
97530           this.root_ = null;
97531           /**
97532            * @type {number}
97533            * @private
97534           */
97535           this.size_ = 0;
97536         }
97537         TreeMap.prototype = new SortedMap();
97538
97539         /**
97540          * @override
97541          */
97542         TreeMap.prototype.get = function (key) {
97543           var p = this.root_;
97544           while (p !== null) {
97545             var cmp = key['compareTo'](p.key);
97546             if (cmp < 0) { p = p.left; }
97547             else if (cmp > 0) { p = p.right; }
97548             else { return p.value }
97549           }
97550           return null
97551         };
97552
97553         /**
97554          * @override
97555          */
97556         TreeMap.prototype.put = function (key, value) {
97557           if (this.root_ === null) {
97558             this.root_ = {
97559               key: key,
97560               value: value,
97561               left: null,
97562               right: null,
97563               parent: null,
97564               color: BLACK,
97565               getValue: function getValue () { return this.value },
97566               getKey: function getKey () { return this.key }
97567             };
97568             this.size_ = 1;
97569             return null
97570           }
97571           var t = this.root_;
97572           var parent;
97573           var cmp;
97574           do {
97575             parent = t;
97576             cmp = key['compareTo'](t.key);
97577             if (cmp < 0) {
97578               t = t.left;
97579             } else if (cmp > 0) {
97580               t = t.right;
97581             } else {
97582               var oldValue = t.value;
97583               t.value = value;
97584               return oldValue
97585             }
97586           } while (t !== null)
97587           var e = {
97588             key: key,
97589             left: null,
97590             right: null,
97591             value: value,
97592             parent: parent,
97593             color: BLACK,
97594             getValue: function getValue () { return this.value },
97595             getKey: function getKey () { return this.key }
97596           };
97597           if (cmp < 0) {
97598             parent.left = e;
97599           } else {
97600             parent.right = e;
97601           }
97602           this.fixAfterInsertion(e);
97603           this.size_++;
97604           return null
97605         };
97606
97607         /**
97608          * @param {Object} x
97609          */
97610         TreeMap.prototype.fixAfterInsertion = function (x) {
97611           var this$1 = this;
97612
97613           x.color = RED;
97614           while (x != null && x !== this.root_ && x.parent.color === RED) {
97615             if (parentOf(x) === leftOf(parentOf(parentOf(x)))) {
97616               var y = rightOf(parentOf(parentOf(x)));
97617               if (colorOf(y) === RED) {
97618                 setColor(parentOf(x), BLACK);
97619                 setColor(y, BLACK);
97620                 setColor(parentOf(parentOf(x)), RED);
97621                 x = parentOf(parentOf(x));
97622               } else {
97623                 if (x === rightOf(parentOf(x))) {
97624                   x = parentOf(x);
97625                   this$1.rotateLeft(x);
97626                 }
97627                 setColor(parentOf(x), BLACK);
97628                 setColor(parentOf(parentOf(x)), RED);
97629                 this$1.rotateRight(parentOf(parentOf(x)));
97630               }
97631             } else {
97632               var y$1 = leftOf(parentOf(parentOf(x)));
97633               if (colorOf(y$1) === RED) {
97634                 setColor(parentOf(x), BLACK);
97635                 setColor(y$1, BLACK);
97636                 setColor(parentOf(parentOf(x)), RED);
97637                 x = parentOf(parentOf(x));
97638               } else {
97639                 if (x === leftOf(parentOf(x))) {
97640                   x = parentOf(x);
97641                   this$1.rotateRight(x);
97642                 }
97643                 setColor(parentOf(x), BLACK);
97644                 setColor(parentOf(parentOf(x)), RED);
97645                 this$1.rotateLeft(parentOf(parentOf(x)));
97646               }
97647             }
97648           }
97649           this.root_.color = BLACK;
97650         };
97651
97652         /**
97653          * @override
97654          */
97655         TreeMap.prototype.values = function () {
97656           var arrayList = new ArrayList();
97657           var p = this.getFirstEntry();
97658           if (p !== null) {
97659             arrayList.add(p.value);
97660             while ((p = TreeMap.successor(p)) !== null) {
97661               arrayList.add(p.value);
97662             }
97663           }
97664           return arrayList
97665         };
97666
97667         /**
97668          * @override
97669          */
97670         TreeMap.prototype.entrySet = function () {
97671           var hashSet = new HashSet();
97672           var p = this.getFirstEntry();
97673           if (p !== null) {
97674             hashSet.add(p);
97675             while ((p = TreeMap.successor(p)) !== null) {
97676               hashSet.add(p);
97677             }
97678           }
97679           return hashSet
97680         };
97681
97682         /**
97683          * @param {Object} p
97684          */
97685         TreeMap.prototype.rotateLeft = function (p) {
97686           if (p != null) {
97687             var r = p.right;
97688             p.right = r.left;
97689             if (r.left != null) { r.left.parent = p; }
97690             r.parent = p.parent;
97691             if (p.parent === null) { this.root_ = r; } else if (p.parent.left === p) { p.parent.left = r; } else { p.parent.right = r; }
97692             r.left = p;
97693             p.parent = r;
97694           }
97695         };
97696
97697         /**
97698          * @param {Object} p
97699          */
97700         TreeMap.prototype.rotateRight = function (p) {
97701           if (p != null) {
97702             var l = p.left;
97703             p.left = l.right;
97704             if (l.right != null) { l.right.parent = p; }
97705             l.parent = p.parent;
97706             if (p.parent === null) { this.root_ = l; } else if (p.parent.right === p) { p.parent.right = l; } else { p.parent.left = l; }
97707             l.right = p;
97708             p.parent = l;
97709           }
97710         };
97711
97712         /**
97713          * @return {Object}
97714          */
97715         TreeMap.prototype.getFirstEntry = function () {
97716           var p = this.root_;
97717           if (p != null) {
97718             while (p.left != null) {
97719               p = p.left;
97720             }
97721           }
97722           return p
97723         };
97724
97725         /**
97726          * @param {Object} t
97727          * @return {Object}
97728          * @private
97729          */
97730         TreeMap.successor = function (t) {
97731           if (t === null) { return null } else if (t.right !== null) {
97732             var p = t.right;
97733             while (p.left !== null) {
97734               p = p.left;
97735             }
97736             return p
97737           } else {
97738             var p$1 = t.parent;
97739             var ch = t;
97740             while (p$1 !== null && ch === p$1.right) {
97741               ch = p$1;
97742               p$1 = p$1.parent;
97743             }
97744             return p$1
97745           }
97746         };
97747
97748         /**
97749          * @override
97750          */
97751         TreeMap.prototype.size = function () {
97752           return this.size_
97753         };
97754
97755         var Lineal = function Lineal () {};
97756
97757         Lineal.prototype.interfaces_ = function interfaces_ () {
97758           return []
97759         };
97760         Lineal.prototype.getClass = function getClass () {
97761           return Lineal
97762         };
97763
97764         /**
97765          * @see http://download.oracle.com/javase/6/docs/api/java/util/SortedSet.html
97766          *
97767          * @extends {Set}
97768          * @constructor
97769          * @private
97770          */
97771         function SortedSet () {}
97772         SortedSet.prototype = new Set$2();
97773
97774         // import Iterator from './Iterator'
97775         /**
97776          * @see http://download.oracle.com/javase/6/docs/api/java/util/TreeSet.html
97777          *
97778          * @extends {SortedSet}
97779          * @constructor
97780          * @private
97781          */
97782         function TreeSet () {
97783           /**
97784            * @type {Array}
97785            * @private
97786           */
97787           this.array_ = [];
97788
97789           if (arguments[0] instanceof Collection) {
97790             this.addAll(arguments[0]);
97791           }
97792         }
97793         TreeSet.prototype = new SortedSet();
97794
97795         /**
97796          * @override
97797          */
97798         TreeSet.prototype.contains = function (o) {
97799           var this$1 = this;
97800
97801           for (var i = 0, len = this.array_.length; i < len; i++) {
97802             var e = this$1.array_[i];
97803             if (e['compareTo'](o) === 0) {
97804               return true
97805             }
97806           }
97807           return false
97808         };
97809
97810         /**
97811          * @override
97812          */
97813         TreeSet.prototype.add = function (o) {
97814           var this$1 = this;
97815
97816           if (this.contains(o)) {
97817             return false
97818           }
97819
97820           for (var i = 0, len = this.array_.length; i < len; i++) {
97821             var e = this$1.array_[i];
97822             if (e['compareTo'](o) === 1) {
97823               this$1.array_.splice(i, 0, o);
97824               return true
97825             }
97826           }
97827
97828           this.array_.push(o);
97829
97830           return true
97831         };
97832
97833         /**
97834          * @override
97835          */
97836         TreeSet.prototype.addAll = function (c) {
97837           var this$1 = this;
97838
97839           for (var i = c.iterator(); i.hasNext();) {
97840             this$1.add(i.next());
97841           }
97842           return true
97843         };
97844
97845         /**
97846          * @override
97847          */
97848         TreeSet.prototype.remove = function (e) {
97849           throw new OperationNotSupported()
97850         };
97851
97852         /**
97853          * @override
97854          */
97855         TreeSet.prototype.size = function () {
97856           return this.array_.length
97857         };
97858
97859         /**
97860          * @override
97861          */
97862         TreeSet.prototype.isEmpty = function () {
97863           return this.array_.length === 0
97864         };
97865
97866         /**
97867          * @override
97868          */
97869         TreeSet.prototype.toArray = function () {
97870           var this$1 = this;
97871
97872           var array = [];
97873
97874           for (var i = 0, len = this.array_.length; i < len; i++) {
97875             array.push(this$1.array_[i]);
97876           }
97877
97878           return array
97879         };
97880
97881         /**
97882          * @override
97883          */
97884         TreeSet.prototype.iterator = function () {
97885           return new Iterator_$2(this)
97886         };
97887
97888         /**
97889          * @extends {javascript.util.Iterator}
97890          * @param {javascript.util.TreeSet} treeSet
97891          * @constructor
97892          * @private
97893          */
97894         var Iterator_$2 = function (treeSet) {
97895           /**
97896            * @type {javascript.util.TreeSet}
97897            * @private
97898            */
97899           this.treeSet_ = treeSet;
97900           /**
97901            * @type {number}
97902            * @private
97903            */
97904           this.position_ = 0;
97905         };
97906
97907         /**
97908          * @override
97909          */
97910         Iterator_$2.prototype.next = function () {
97911           if (this.position_ === this.treeSet_.size()) {
97912             throw new NoSuchElementException()
97913           }
97914           return this.treeSet_.array_[this.position_++]
97915         };
97916
97917         /**
97918          * @override
97919          */
97920         Iterator_$2.prototype.hasNext = function () {
97921           if (this.position_ < this.treeSet_.size()) {
97922             return true
97923           } else {
97924             return false
97925           }
97926         };
97927
97928         /**
97929          * @override
97930          */
97931         Iterator_$2.prototype.remove = function () {
97932           throw new OperationNotSupported()
97933         };
97934
97935         /**
97936          * @see http://download.oracle.com/javase/6/docs/api/java/util/Arrays.html
97937          *
97938          * @constructor
97939          * @private
97940          */
97941         var Arrays = function Arrays () {};
97942
97943         Arrays.sort = function sort () {
97944           var a = arguments[0];
97945           var i;
97946           var t;
97947           var comparator;
97948           var compare;
97949           if (arguments.length === 1) {
97950             compare = function (a, b) {
97951               return a.compareTo(b)
97952             };
97953             a.sort(compare);
97954           } else if (arguments.length === 2) {
97955             comparator = arguments[1];
97956             compare = function (a, b) {
97957               return comparator['compare'](a, b)
97958             };
97959             a.sort(compare);
97960           } else if (arguments.length === 3) {
97961             t = a.slice(arguments[1], arguments[2]);
97962             t.sort();
97963             var r = a.slice(0, arguments[1]).concat(t, a.slice(arguments[2], a.length));
97964             a.splice(0, a.length);
97965             for (i = 0; i < r.length; i++) {
97966               a.push(r[i]);
97967             }
97968           } else if (arguments.length === 4) {
97969             t = a.slice(arguments[1], arguments[2]);
97970             comparator = arguments[3];
97971             compare = function (a, b) {
97972               return comparator['compare'](a, b)
97973             };
97974             t.sort(compare);
97975             r = a.slice(0, arguments[1]).concat(t, a.slice(arguments[2], a.length));
97976             a.splice(0, a.length);
97977             for (i = 0; i < r.length; i++) {
97978               a.push(r[i]);
97979             }
97980           }
97981         };
97982         /**
97983          * @param {Array} array
97984          * @return {ArrayList}
97985          */
97986         Arrays.asList = function asList (array) {
97987           var arrayList = new ArrayList();
97988           for (var i = 0, len = array.length; i < len; i++) {
97989             arrayList.add(array[i]);
97990           }
97991           return arrayList
97992         };
97993
97994         var Dimension = function Dimension () {};
97995
97996         var staticAccessors$14 = { P: { configurable: true },L: { configurable: true },A: { configurable: true },FALSE: { configurable: true },TRUE: { configurable: true },DONTCARE: { configurable: true },SYM_FALSE: { configurable: true },SYM_TRUE: { configurable: true },SYM_DONTCARE: { configurable: true },SYM_P: { configurable: true },SYM_L: { configurable: true },SYM_A: { configurable: true } };
97997
97998         staticAccessors$14.P.get = function () { return 0 };
97999         staticAccessors$14.L.get = function () { return 1 };
98000         staticAccessors$14.A.get = function () { return 2 };
98001         staticAccessors$14.FALSE.get = function () { return -1 };
98002         staticAccessors$14.TRUE.get = function () { return -2 };
98003         staticAccessors$14.DONTCARE.get = function () { return -3 };
98004         staticAccessors$14.SYM_FALSE.get = function () { return 'F' };
98005         staticAccessors$14.SYM_TRUE.get = function () { return 'T' };
98006         staticAccessors$14.SYM_DONTCARE.get = function () { return '*' };
98007         staticAccessors$14.SYM_P.get = function () { return '0' };
98008         staticAccessors$14.SYM_L.get = function () { return '1' };
98009         staticAccessors$14.SYM_A.get = function () { return '2' };
98010
98011         Dimension.prototype.interfaces_ = function interfaces_ () {
98012           return []
98013         };
98014         Dimension.prototype.getClass = function getClass () {
98015           return Dimension
98016         };
98017         Dimension.toDimensionSymbol = function toDimensionSymbol (dimensionValue) {
98018           switch (dimensionValue) {
98019             case Dimension.FALSE:
98020               return Dimension.SYM_FALSE
98021             case Dimension.TRUE:
98022               return Dimension.SYM_TRUE
98023             case Dimension.DONTCARE:
98024               return Dimension.SYM_DONTCARE
98025             case Dimension.P:
98026               return Dimension.SYM_P
98027             case Dimension.L:
98028               return Dimension.SYM_L
98029             case Dimension.A:
98030               return Dimension.SYM_A
98031           }
98032           throw new IllegalArgumentException('Unknown dimension value: ' + dimensionValue)
98033         };
98034         Dimension.toDimensionValue = function toDimensionValue (dimensionSymbol) {
98035           switch (Character.toUpperCase(dimensionSymbol)) {
98036             case Dimension.SYM_FALSE:
98037               return Dimension.FALSE
98038             case Dimension.SYM_TRUE:
98039               return Dimension.TRUE
98040             case Dimension.SYM_DONTCARE:
98041               return Dimension.DONTCARE
98042             case Dimension.SYM_P:
98043               return Dimension.P
98044             case Dimension.SYM_L:
98045               return Dimension.L
98046             case Dimension.SYM_A:
98047               return Dimension.A
98048           }
98049           throw new IllegalArgumentException('Unknown dimension symbol: ' + dimensionSymbol)
98050         };
98051
98052         Object.defineProperties( Dimension, staticAccessors$14 );
98053
98054         var GeometryFilter = function GeometryFilter () {};
98055
98056         GeometryFilter.prototype.filter = function filter (geom) {};
98057         GeometryFilter.prototype.interfaces_ = function interfaces_ () {
98058           return []
98059         };
98060         GeometryFilter.prototype.getClass = function getClass () {
98061           return GeometryFilter
98062         };
98063
98064         var CoordinateSequenceFilter = function CoordinateSequenceFilter () {};
98065
98066         CoordinateSequenceFilter.prototype.filter = function filter (seq, i) {};
98067         CoordinateSequenceFilter.prototype.isDone = function isDone () {};
98068         CoordinateSequenceFilter.prototype.isGeometryChanged = function isGeometryChanged () {};
98069         CoordinateSequenceFilter.prototype.interfaces_ = function interfaces_ () {
98070           return []
98071         };
98072         CoordinateSequenceFilter.prototype.getClass = function getClass () {
98073           return CoordinateSequenceFilter
98074         };
98075
98076         var GeometryCollection = (function (Geometry$$1) {
98077           function GeometryCollection (geometries, factory) {
98078             Geometry$$1.call(this, factory);
98079             this._geometries = geometries || [];
98080
98081             if (Geometry$$1.hasNullElements(this._geometries)) {
98082               throw new IllegalArgumentException('geometries must not contain null elements')
98083             }
98084           }
98085
98086           if ( Geometry$$1 ) GeometryCollection.__proto__ = Geometry$$1;
98087           GeometryCollection.prototype = Object.create( Geometry$$1 && Geometry$$1.prototype );
98088           GeometryCollection.prototype.constructor = GeometryCollection;
98089
98090           var staticAccessors = { serialVersionUID: { configurable: true } };
98091           GeometryCollection.prototype.computeEnvelopeInternal = function computeEnvelopeInternal () {
98092             var this$1 = this;
98093
98094             var envelope = new Envelope();
98095             for (var i = 0; i < this._geometries.length; i++) {
98096               envelope.expandToInclude(this$1._geometries[i].getEnvelopeInternal());
98097             }
98098             return envelope
98099           };
98100           GeometryCollection.prototype.getGeometryN = function getGeometryN (n) {
98101             return this._geometries[n]
98102           };
98103           GeometryCollection.prototype.getSortIndex = function getSortIndex () {
98104             return Geometry$$1.SORTINDEX_GEOMETRYCOLLECTION
98105           };
98106           GeometryCollection.prototype.getCoordinates = function getCoordinates () {
98107             var this$1 = this;
98108
98109             var coordinates = new Array(this.getNumPoints()).fill(null);
98110             var k = -1;
98111             for (var i = 0; i < this._geometries.length; i++) {
98112               var childCoordinates = this$1._geometries[i].getCoordinates();
98113               for (var j = 0; j < childCoordinates.length; j++) {
98114                 k++;
98115                 coordinates[k] = childCoordinates[j];
98116               }
98117             }
98118             return coordinates
98119           };
98120           GeometryCollection.prototype.getArea = function getArea () {
98121             var this$1 = this;
98122
98123             var area = 0.0;
98124             for (var i = 0; i < this._geometries.length; i++) {
98125               area += this$1._geometries[i].getArea();
98126             }
98127             return area
98128           };
98129           GeometryCollection.prototype.equalsExact = function equalsExact () {
98130             var this$1 = this;
98131
98132             if (arguments.length === 2) {
98133               var other = arguments[0];
98134               var tolerance = arguments[1];
98135               if (!this.isEquivalentClass(other)) {
98136                 return false
98137               }
98138               var otherCollection = other;
98139               if (this._geometries.length !== otherCollection._geometries.length) {
98140                 return false
98141               }
98142               for (var i = 0; i < this._geometries.length; i++) {
98143                 if (!this$1._geometries[i].equalsExact(otherCollection._geometries[i], tolerance)) {
98144                   return false
98145                 }
98146               }
98147               return true
98148             } else { return Geometry$$1.prototype.equalsExact.apply(this, arguments) }
98149           };
98150           GeometryCollection.prototype.normalize = function normalize () {
98151             var this$1 = this;
98152
98153             for (var i = 0; i < this._geometries.length; i++) {
98154               this$1._geometries[i].normalize();
98155             }
98156             Arrays.sort(this._geometries);
98157           };
98158           GeometryCollection.prototype.getCoordinate = function getCoordinate () {
98159             if (this.isEmpty()) { return null }
98160             return this._geometries[0].getCoordinate()
98161           };
98162           GeometryCollection.prototype.getBoundaryDimension = function getBoundaryDimension () {
98163             var this$1 = this;
98164
98165             var dimension = Dimension.FALSE;
98166             for (var i = 0; i < this._geometries.length; i++) {
98167               dimension = Math.max(dimension, this$1._geometries[i].getBoundaryDimension());
98168             }
98169             return dimension
98170           };
98171           GeometryCollection.prototype.getDimension = function getDimension () {
98172             var this$1 = this;
98173
98174             var dimension = Dimension.FALSE;
98175             for (var i = 0; i < this._geometries.length; i++) {
98176               dimension = Math.max(dimension, this$1._geometries[i].getDimension());
98177             }
98178             return dimension
98179           };
98180           GeometryCollection.prototype.getLength = function getLength () {
98181             var this$1 = this;
98182
98183             var sum = 0.0;
98184             for (var i = 0; i < this._geometries.length; i++) {
98185               sum += this$1._geometries[i].getLength();
98186             }
98187             return sum
98188           };
98189           GeometryCollection.prototype.getNumPoints = function getNumPoints () {
98190             var this$1 = this;
98191
98192             var numPoints = 0;
98193             for (var i = 0; i < this._geometries.length; i++) {
98194               numPoints += this$1._geometries[i].getNumPoints();
98195             }
98196             return numPoints
98197           };
98198           GeometryCollection.prototype.getNumGeometries = function getNumGeometries () {
98199             return this._geometries.length
98200           };
98201           GeometryCollection.prototype.reverse = function reverse () {
98202             var this$1 = this;
98203
98204             var n = this._geometries.length;
98205             var revGeoms = new Array(n).fill(null);
98206             for (var i = 0; i < this._geometries.length; i++) {
98207               revGeoms[i] = this$1._geometries[i].reverse();
98208             }
98209             return this.getFactory().createGeometryCollection(revGeoms)
98210           };
98211           GeometryCollection.prototype.compareToSameClass = function compareToSameClass () {
98212             var this$1 = this;
98213
98214             if (arguments.length === 1) {
98215               var o = arguments[0];
98216               var theseElements = new TreeSet(Arrays.asList(this._geometries));
98217               var otherElements = new TreeSet(Arrays.asList(o._geometries));
98218               return this.compare(theseElements, otherElements)
98219             } else if (arguments.length === 2) {
98220               var o$1 = arguments[0];
98221               var comp = arguments[1];
98222               var gc = o$1;
98223               var n1 = this.getNumGeometries();
98224               var n2 = gc.getNumGeometries();
98225               var i = 0;
98226               while (i < n1 && i < n2) {
98227                 var thisGeom = this$1.getGeometryN(i);
98228                 var otherGeom = gc.getGeometryN(i);
98229                 var holeComp = thisGeom.compareToSameClass(otherGeom, comp);
98230                 if (holeComp !== 0) { return holeComp }
98231                 i++;
98232               }
98233               if (i < n1) { return 1 }
98234               if (i < n2) { return -1 }
98235               return 0
98236             }
98237           };
98238           GeometryCollection.prototype.apply = function apply () {
98239             var this$1 = this;
98240
98241             if (hasInterface(arguments[0], CoordinateFilter)) {
98242               var filter = arguments[0];
98243               for (var i = 0; i < this._geometries.length; i++) {
98244                 this$1._geometries[i].apply(filter);
98245               }
98246             } else if (hasInterface(arguments[0], CoordinateSequenceFilter)) {
98247               var filter$1 = arguments[0];
98248               if (this._geometries.length === 0) { return null }
98249               for (var i$1 = 0; i$1 < this._geometries.length; i$1++) {
98250                 this$1._geometries[i$1].apply(filter$1);
98251                 if (filter$1.isDone()) {
98252                   break
98253                 }
98254               }
98255               if (filter$1.isGeometryChanged()) { this.geometryChanged(); }
98256             } else if (hasInterface(arguments[0], GeometryFilter)) {
98257               var filter$2 = arguments[0];
98258               filter$2.filter(this);
98259               for (var i$2 = 0; i$2 < this._geometries.length; i$2++) {
98260                 this$1._geometries[i$2].apply(filter$2);
98261               }
98262             } else if (hasInterface(arguments[0], GeometryComponentFilter)) {
98263               var filter$3 = arguments[0];
98264               filter$3.filter(this);
98265               for (var i$3 = 0; i$3 < this._geometries.length; i$3++) {
98266                 this$1._geometries[i$3].apply(filter$3);
98267               }
98268             }
98269           };
98270           GeometryCollection.prototype.getBoundary = function getBoundary () {
98271             this.checkNotGeometryCollection(this);
98272             Assert.shouldNeverReachHere();
98273             return null
98274           };
98275           GeometryCollection.prototype.clone = function clone () {
98276             var this$1 = this;
98277
98278             var gc = Geometry$$1.prototype.clone.call(this);
98279             gc._geometries = new Array(this._geometries.length).fill(null);
98280             for (var i = 0; i < this._geometries.length; i++) {
98281               gc._geometries[i] = this$1._geometries[i].clone();
98282             }
98283             return gc
98284           };
98285           GeometryCollection.prototype.getGeometryType = function getGeometryType () {
98286             return 'GeometryCollection'
98287           };
98288           GeometryCollection.prototype.copy = function copy () {
98289             var this$1 = this;
98290
98291             var geometries = new Array(this._geometries.length).fill(null);
98292             for (var i = 0; i < geometries.length; i++) {
98293               geometries[i] = this$1._geometries[i].copy();
98294             }
98295             return new GeometryCollection(geometries, this._factory)
98296           };
98297           GeometryCollection.prototype.isEmpty = function isEmpty () {
98298             var this$1 = this;
98299
98300             for (var i = 0; i < this._geometries.length; i++) {
98301               if (!this$1._geometries[i].isEmpty()) {
98302                 return false
98303               }
98304             }
98305             return true
98306           };
98307           GeometryCollection.prototype.interfaces_ = function interfaces_ () {
98308             return []
98309           };
98310           GeometryCollection.prototype.getClass = function getClass () {
98311             return GeometryCollection
98312           };
98313           staticAccessors.serialVersionUID.get = function () { return -5694727726395021467 };
98314
98315           Object.defineProperties( GeometryCollection, staticAccessors );
98316
98317           return GeometryCollection;
98318         }(Geometry));
98319
98320         var MultiLineString = (function (GeometryCollection$$1) {
98321           function MultiLineString () {
98322             GeometryCollection$$1.apply(this, arguments);
98323           }
98324
98325           if ( GeometryCollection$$1 ) MultiLineString.__proto__ = GeometryCollection$$1;
98326           MultiLineString.prototype = Object.create( GeometryCollection$$1 && GeometryCollection$$1.prototype );
98327           MultiLineString.prototype.constructor = MultiLineString;
98328
98329           var staticAccessors = { serialVersionUID: { configurable: true } };
98330
98331           MultiLineString.prototype.getSortIndex = function getSortIndex () {
98332             return Geometry.SORTINDEX_MULTILINESTRING
98333           };
98334           MultiLineString.prototype.equalsExact = function equalsExact () {
98335             if (arguments.length === 2) {
98336               var other = arguments[0];
98337               var tolerance = arguments[1];
98338               if (!this.isEquivalentClass(other)) {
98339                 return false
98340               }
98341               return GeometryCollection$$1.prototype.equalsExact.call(this, other, tolerance)
98342             } else { return GeometryCollection$$1.prototype.equalsExact.apply(this, arguments) }
98343           };
98344           MultiLineString.prototype.getBoundaryDimension = function getBoundaryDimension () {
98345             if (this.isClosed()) {
98346               return Dimension.FALSE
98347             }
98348             return 0
98349           };
98350           MultiLineString.prototype.isClosed = function isClosed () {
98351             var this$1 = this;
98352
98353             if (this.isEmpty()) {
98354               return false
98355             }
98356             for (var i = 0; i < this._geometries.length; i++) {
98357               if (!this$1._geometries[i].isClosed()) {
98358                 return false
98359               }
98360             }
98361             return true
98362           };
98363           MultiLineString.prototype.getDimension = function getDimension () {
98364             return 1
98365           };
98366           MultiLineString.prototype.reverse = function reverse () {
98367             var this$1 = this;
98368
98369             var nLines = this._geometries.length;
98370             var revLines = new Array(nLines).fill(null);
98371             for (var i = 0; i < this._geometries.length; i++) {
98372               revLines[nLines - 1 - i] = this$1._geometries[i].reverse();
98373             }
98374             return this.getFactory().createMultiLineString(revLines)
98375           };
98376           MultiLineString.prototype.getBoundary = function getBoundary () {
98377             return new BoundaryOp(this).getBoundary()
98378           };
98379           MultiLineString.prototype.getGeometryType = function getGeometryType () {
98380             return 'MultiLineString'
98381           };
98382           MultiLineString.prototype.copy = function copy () {
98383             var this$1 = this;
98384
98385             var lineStrings = new Array(this._geometries.length).fill(null);
98386             for (var i = 0; i < lineStrings.length; i++) {
98387               lineStrings[i] = this$1._geometries[i].copy();
98388             }
98389             return new MultiLineString(lineStrings, this._factory)
98390           };
98391           MultiLineString.prototype.interfaces_ = function interfaces_ () {
98392             return [Lineal]
98393           };
98394           MultiLineString.prototype.getClass = function getClass () {
98395             return MultiLineString
98396           };
98397           staticAccessors.serialVersionUID.get = function () { return 8166665132445433741 };
98398
98399           Object.defineProperties( MultiLineString, staticAccessors );
98400
98401           return MultiLineString;
98402         }(GeometryCollection));
98403
98404         var BoundaryOp = function BoundaryOp () {
98405           this._geom = null;
98406           this._geomFact = null;
98407           this._bnRule = null;
98408           this._endpointMap = null;
98409           if (arguments.length === 1) {
98410             var geom = arguments[0];
98411             var bnRule = BoundaryNodeRule.MOD2_BOUNDARY_RULE;
98412             this._geom = geom;
98413             this._geomFact = geom.getFactory();
98414             this._bnRule = bnRule;
98415           } else if (arguments.length === 2) {
98416             var geom$1 = arguments[0];
98417             var bnRule$1 = arguments[1];
98418             this._geom = geom$1;
98419             this._geomFact = geom$1.getFactory();
98420             this._bnRule = bnRule$1;
98421           }
98422         };
98423         BoundaryOp.prototype.boundaryMultiLineString = function boundaryMultiLineString (mLine) {
98424           if (this._geom.isEmpty()) {
98425             return this.getEmptyMultiPoint()
98426           }
98427           var bdyPts = this.computeBoundaryCoordinates(mLine);
98428           if (bdyPts.length === 1) {
98429             return this._geomFact.createPoint(bdyPts[0])
98430           }
98431           return this._geomFact.createMultiPointFromCoords(bdyPts)
98432         };
98433         BoundaryOp.prototype.getBoundary = function getBoundary () {
98434           if (this._geom instanceof LineString) { return this.boundaryLineString(this._geom) }
98435           if (this._geom instanceof MultiLineString) { return this.boundaryMultiLineString(this._geom) }
98436           return this._geom.getBoundary()
98437         };
98438         BoundaryOp.prototype.boundaryLineString = function boundaryLineString (line) {
98439           if (this._geom.isEmpty()) {
98440             return this.getEmptyMultiPoint()
98441           }
98442           if (line.isClosed()) {
98443             var closedEndpointOnBoundary = this._bnRule.isInBoundary(2);
98444             if (closedEndpointOnBoundary) {
98445               return line.getStartPoint()
98446             } else {
98447               return this._geomFact.createMultiPoint()
98448             }
98449           }
98450           return this._geomFact.createMultiPoint([line.getStartPoint(), line.getEndPoint()])
98451         };
98452         BoundaryOp.prototype.getEmptyMultiPoint = function getEmptyMultiPoint () {
98453           return this._geomFact.createMultiPoint()
98454         };
98455         BoundaryOp.prototype.computeBoundaryCoordinates = function computeBoundaryCoordinates (mLine) {
98456             var this$1 = this;
98457
98458           var bdyPts = new ArrayList();
98459           this._endpointMap = new TreeMap();
98460           for (var i = 0; i < mLine.getNumGeometries(); i++) {
98461             var line = mLine.getGeometryN(i);
98462             if (line.getNumPoints() === 0) { continue }
98463             this$1.addEndpoint(line.getCoordinateN(0));
98464             this$1.addEndpoint(line.getCoordinateN(line.getNumPoints() - 1));
98465           }
98466           for (var it = this._endpointMap.entrySet().iterator(); it.hasNext();) {
98467             var entry = it.next();
98468             var counter = entry.getValue();
98469             var valence = counter.count;
98470             if (this$1._bnRule.isInBoundary(valence)) {
98471               bdyPts.add(entry.getKey());
98472             }
98473           }
98474           return CoordinateArrays.toCoordinateArray(bdyPts)
98475         };
98476         BoundaryOp.prototype.addEndpoint = function addEndpoint (pt) {
98477           var counter = this._endpointMap.get(pt);
98478           if (counter === null) {
98479             counter = new Counter();
98480             this._endpointMap.put(pt, counter);
98481           }
98482           counter.count++;
98483         };
98484         BoundaryOp.prototype.interfaces_ = function interfaces_ () {
98485           return []
98486         };
98487         BoundaryOp.prototype.getClass = function getClass () {
98488           return BoundaryOp
98489         };
98490         BoundaryOp.getBoundary = function getBoundary () {
98491           if (arguments.length === 1) {
98492             var g = arguments[0];
98493             var bop = new BoundaryOp(g);
98494             return bop.getBoundary()
98495           } else if (arguments.length === 2) {
98496             var g$1 = arguments[0];
98497             var bnRule = arguments[1];
98498             var bop$1 = new BoundaryOp(g$1, bnRule);
98499             return bop$1.getBoundary()
98500           }
98501         };
98502
98503         var Counter = function Counter () {
98504           this.count = null;
98505         };
98506         Counter.prototype.interfaces_ = function interfaces_ () {
98507           return []
98508         };
98509         Counter.prototype.getClass = function getClass () {
98510           return Counter
98511         };
98512
98513         // boundary
98514
98515         function PrintStream () {}
98516
98517         function StringReader () {}
98518
98519         var DecimalFormat = function DecimalFormat () {};
98520
98521         function ByteArrayOutputStream () {}
98522
98523         function IOException () {}
98524
98525         function LineNumberReader () {}
98526
98527         var StringUtil = function StringUtil () {};
98528
98529         var staticAccessors$15 = { NEWLINE: { configurable: true },SIMPLE_ORDINATE_FORMAT: { configurable: true } };
98530
98531         StringUtil.prototype.interfaces_ = function interfaces_ () {
98532           return []
98533         };
98534         StringUtil.prototype.getClass = function getClass () {
98535           return StringUtil
98536         };
98537         StringUtil.chars = function chars (c, n) {
98538           var ch = new Array(n).fill(null);
98539           for (var i = 0; i < n; i++) {
98540             ch[i] = c;
98541           }
98542           return String(ch)
98543         };
98544         StringUtil.getStackTrace = function getStackTrace () {
98545           if (arguments.length === 1) {
98546             var t = arguments[0];
98547             var os = new ByteArrayOutputStream();
98548             var ps = new PrintStream(os);
98549             t.printStackTrace(ps);
98550             return os.toString()
98551           } else if (arguments.length === 2) {
98552             var t$1 = arguments[0];
98553             var depth = arguments[1];
98554             var stackTrace = '';
98555             var stringReader = new StringReader(StringUtil.getStackTrace(t$1));
98556             var lineNumberReader = new LineNumberReader(stringReader);
98557             for (var i = 0; i < depth; i++) {
98558               try {
98559                 stackTrace += lineNumberReader.readLine() + StringUtil.NEWLINE;
98560               } catch (e) {
98561                 if (e instanceof IOException) {
98562                   Assert.shouldNeverReachHere();
98563                 } else { throw e }
98564               } finally {}
98565             }
98566             return stackTrace
98567           }
98568         };
98569         StringUtil.split = function split (s, separator) {
98570           var separatorlen = separator.length;
98571           var tokenList = new ArrayList();
98572           var tmpString = '' + s;
98573           var pos = tmpString.indexOf(separator);
98574           while (pos >= 0) {
98575             var token = tmpString.substring(0, pos);
98576             tokenList.add(token);
98577             tmpString = tmpString.substring(pos + separatorlen);
98578             pos = tmpString.indexOf(separator);
98579           }
98580           if (tmpString.length > 0) { tokenList.add(tmpString); }
98581           var res = new Array(tokenList.size()).fill(null);
98582           for (var i = 0; i < res.length; i++) {
98583             res[i] = tokenList.get(i);
98584           }
98585           return res
98586         };
98587         StringUtil.toString = function toString () {
98588           if (arguments.length === 1) {
98589             var d = arguments[0];
98590             return StringUtil.SIMPLE_ORDINATE_FORMAT.format(d)
98591           }
98592         };
98593         StringUtil.spaces = function spaces (n) {
98594           return StringUtil.chars(' ', n)
98595         };
98596         staticAccessors$15.NEWLINE.get = function () { return System.getProperty('line.separator') };
98597         staticAccessors$15.SIMPLE_ORDINATE_FORMAT.get = function () { return new DecimalFormat('0.#') };
98598
98599         Object.defineProperties( StringUtil, staticAccessors$15 );
98600
98601         var CoordinateSequences = function CoordinateSequences () {};
98602
98603         CoordinateSequences.prototype.interfaces_ = function interfaces_ () {
98604           return []
98605         };
98606         CoordinateSequences.prototype.getClass = function getClass () {
98607           return CoordinateSequences
98608         };
98609         CoordinateSequences.copyCoord = function copyCoord (src, srcPos, dest, destPos) {
98610           var minDim = Math.min(src.getDimension(), dest.getDimension());
98611           for (var dim = 0; dim < minDim; dim++) {
98612             dest.setOrdinate(destPos, dim, src.getOrdinate(srcPos, dim));
98613           }
98614         };
98615         CoordinateSequences.isRing = function isRing (seq) {
98616           var n = seq.size();
98617           if (n === 0) { return true }
98618           if (n <= 3) { return false }
98619           return seq.getOrdinate(0, CoordinateSequence.X) === seq.getOrdinate(n - 1, CoordinateSequence.X) && seq.getOrdinate(0, CoordinateSequence.Y) === seq.getOrdinate(n - 1, CoordinateSequence.Y)
98620         };
98621         CoordinateSequences.isEqual = function isEqual (cs1, cs2) {
98622           var cs1Size = cs1.size();
98623           var cs2Size = cs2.size();
98624           if (cs1Size !== cs2Size) { return false }
98625           var dim = Math.min(cs1.getDimension(), cs2.getDimension());
98626           for (var i = 0; i < cs1Size; i++) {
98627             for (var d = 0; d < dim; d++) {
98628               var v1 = cs1.getOrdinate(i, d);
98629               var v2 = cs2.getOrdinate(i, d);
98630               if (cs1.getOrdinate(i, d) === cs2.getOrdinate(i, d)) { continue }
98631               if (Double.isNaN(v1) && Double.isNaN(v2)) { continue }
98632               return false
98633             }
98634           }
98635           return true
98636         };
98637         CoordinateSequences.extend = function extend (fact, seq, size) {
98638           var newseq = fact.create(size, seq.getDimension());
98639           var n = seq.size();
98640           CoordinateSequences.copy(seq, 0, newseq, 0, n);
98641           if (n > 0) {
98642             for (var i = n; i < size; i++) { CoordinateSequences.copy(seq, n - 1, newseq, i, 1); }
98643           }
98644           return newseq
98645         };
98646         CoordinateSequences.reverse = function reverse (seq) {
98647           var last = seq.size() - 1;
98648           var mid = Math.trunc(last / 2);
98649           for (var i = 0; i <= mid; i++) {
98650             CoordinateSequences.swap(seq, i, last - i);
98651           }
98652         };
98653         CoordinateSequences.swap = function swap (seq, i, j) {
98654           if (i === j) { return null }
98655           for (var dim = 0; dim < seq.getDimension(); dim++) {
98656             var tmp = seq.getOrdinate(i, dim);
98657             seq.setOrdinate(i, dim, seq.getOrdinate(j, dim));
98658             seq.setOrdinate(j, dim, tmp);
98659           }
98660         };
98661         CoordinateSequences.copy = function copy (src, srcPos, dest, destPos, length) {
98662           for (var i = 0; i < length; i++) {
98663             CoordinateSequences.copyCoord(src, srcPos + i, dest, destPos + i);
98664           }
98665         };
98666         CoordinateSequences.toString = function toString () {
98667           if (arguments.length === 1) {
98668             var cs = arguments[0];
98669             var size = cs.size();
98670             if (size === 0) { return '()' }
98671             var dim = cs.getDimension();
98672             var buf = new StringBuffer();
98673             buf.append('(');
98674             for (var i = 0; i < size; i++) {
98675               if (i > 0) { buf.append(' '); }
98676               for (var d = 0; d < dim; d++) {
98677                 if (d > 0) { buf.append(','); }
98678                 buf.append(StringUtil.toString(cs.getOrdinate(i, d)));
98679               }
98680             }
98681             buf.append(')');
98682             return buf.toString()
98683           }
98684         };
98685         CoordinateSequences.ensureValidRing = function ensureValidRing (fact, seq) {
98686           var n = seq.size();
98687           if (n === 0) { return seq }
98688           if (n <= 3) { return CoordinateSequences.createClosedRing(fact, seq, 4) }
98689           var isClosed = seq.getOrdinate(0, CoordinateSequence.X) === seq.getOrdinate(n - 1, CoordinateSequence.X) && seq.getOrdinate(0, CoordinateSequence.Y) === seq.getOrdinate(n - 1, CoordinateSequence.Y);
98690           if (isClosed) { return seq }
98691           return CoordinateSequences.createClosedRing(fact, seq, n + 1)
98692         };
98693         CoordinateSequences.createClosedRing = function createClosedRing (fact, seq, size) {
98694           var newseq = fact.create(size, seq.getDimension());
98695           var n = seq.size();
98696           CoordinateSequences.copy(seq, 0, newseq, 0, n);
98697           for (var i = n; i < size; i++) { CoordinateSequences.copy(seq, 0, newseq, i, 1); }
98698           return newseq
98699         };
98700
98701         var LineString = (function (Geometry$$1) {
98702           function LineString (points, factory) {
98703             Geometry$$1.call(this, factory);
98704             this._points = null;
98705             this.init(points);
98706           }
98707
98708           if ( Geometry$$1 ) LineString.__proto__ = Geometry$$1;
98709           LineString.prototype = Object.create( Geometry$$1 && Geometry$$1.prototype );
98710           LineString.prototype.constructor = LineString;
98711
98712           var staticAccessors = { serialVersionUID: { configurable: true } };
98713           LineString.prototype.computeEnvelopeInternal = function computeEnvelopeInternal () {
98714             if (this.isEmpty()) {
98715               return new Envelope()
98716             }
98717             return this._points.expandEnvelope(new Envelope())
98718           };
98719           LineString.prototype.isRing = function isRing () {
98720             return this.isClosed() && this.isSimple()
98721           };
98722           LineString.prototype.getSortIndex = function getSortIndex () {
98723             return Geometry$$1.SORTINDEX_LINESTRING
98724           };
98725           LineString.prototype.getCoordinates = function getCoordinates () {
98726             return this._points.toCoordinateArray()
98727           };
98728           LineString.prototype.equalsExact = function equalsExact () {
98729             var this$1 = this;
98730
98731             if (arguments.length === 2) {
98732               var other = arguments[0];
98733               var tolerance = arguments[1];
98734               if (!this.isEquivalentClass(other)) {
98735                 return false
98736               }
98737               var otherLineString = other;
98738               if (this._points.size() !== otherLineString._points.size()) {
98739                 return false
98740               }
98741               for (var i = 0; i < this._points.size(); i++) {
98742                 if (!this$1.equal(this$1._points.getCoordinate(i), otherLineString._points.getCoordinate(i), tolerance)) {
98743                   return false
98744                 }
98745               }
98746               return true
98747             } else { return Geometry$$1.prototype.equalsExact.apply(this, arguments) }
98748           };
98749           LineString.prototype.normalize = function normalize () {
98750             var this$1 = this;
98751
98752             for (var i = 0; i < Math.trunc(this._points.size() / 2); i++) {
98753               var j = this$1._points.size() - 1 - i;
98754               if (!this$1._points.getCoordinate(i).equals(this$1._points.getCoordinate(j))) {
98755                 if (this$1._points.getCoordinate(i).compareTo(this$1._points.getCoordinate(j)) > 0) {
98756                   CoordinateSequences.reverse(this$1._points);
98757                 }
98758                 return null
98759               }
98760             }
98761           };
98762           LineString.prototype.getCoordinate = function getCoordinate () {
98763             if (this.isEmpty()) { return null }
98764             return this._points.getCoordinate(0)
98765           };
98766           LineString.prototype.getBoundaryDimension = function getBoundaryDimension () {
98767             if (this.isClosed()) {
98768               return Dimension.FALSE
98769             }
98770             return 0
98771           };
98772           LineString.prototype.isClosed = function isClosed () {
98773             if (this.isEmpty()) {
98774               return false
98775             }
98776             return this.getCoordinateN(0).equals2D(this.getCoordinateN(this.getNumPoints() - 1))
98777           };
98778           LineString.prototype.getEndPoint = function getEndPoint () {
98779             if (this.isEmpty()) {
98780               return null
98781             }
98782             return this.getPointN(this.getNumPoints() - 1)
98783           };
98784           LineString.prototype.getDimension = function getDimension () {
98785             return 1
98786           };
98787           LineString.prototype.getLength = function getLength () {
98788             return CGAlgorithms.computeLength(this._points)
98789           };
98790           LineString.prototype.getNumPoints = function getNumPoints () {
98791             return this._points.size()
98792           };
98793           LineString.prototype.reverse = function reverse () {
98794             var seq = this._points.copy();
98795             CoordinateSequences.reverse(seq);
98796             var revLine = this.getFactory().createLineString(seq);
98797             return revLine
98798           };
98799           LineString.prototype.compareToSameClass = function compareToSameClass () {
98800             var this$1 = this;
98801
98802             if (arguments.length === 1) {
98803               var o = arguments[0];
98804               var line = o;
98805               var i = 0;
98806               var j = 0;
98807               while (i < this._points.size() && j < line._points.size()) {
98808                 var comparison = this$1._points.getCoordinate(i).compareTo(line._points.getCoordinate(j));
98809                 if (comparison !== 0) {
98810                   return comparison
98811                 }
98812                 i++;
98813                 j++;
98814               }
98815               if (i < this._points.size()) {
98816                 return 1
98817               }
98818               if (j < line._points.size()) {
98819                 return -1
98820               }
98821               return 0
98822             } else if (arguments.length === 2) {
98823               var o$1 = arguments[0];
98824               var comp = arguments[1];
98825               var line$1 = o$1;
98826               return comp.compare(this._points, line$1._points)
98827             }
98828           };
98829           LineString.prototype.apply = function apply () {
98830             var this$1 = this;
98831
98832             if (hasInterface(arguments[0], CoordinateFilter)) {
98833               var filter = arguments[0];
98834               for (var i = 0; i < this._points.size(); i++) {
98835                 filter.filter(this$1._points.getCoordinate(i));
98836               }
98837             } else if (hasInterface(arguments[0], CoordinateSequenceFilter)) {
98838               var filter$1 = arguments[0];
98839               if (this._points.size() === 0) { return null }
98840               for (var i$1 = 0; i$1 < this._points.size(); i$1++) {
98841                 filter$1.filter(this$1._points, i$1);
98842                 if (filter$1.isDone()) { break }
98843               }
98844               if (filter$1.isGeometryChanged()) { this.geometryChanged(); }
98845             } else if (hasInterface(arguments[0], GeometryFilter)) {
98846               var filter$2 = arguments[0];
98847               filter$2.filter(this);
98848             } else if (hasInterface(arguments[0], GeometryComponentFilter)) {
98849               var filter$3 = arguments[0];
98850               filter$3.filter(this);
98851             }
98852           };
98853           LineString.prototype.getBoundary = function getBoundary () {
98854             return new BoundaryOp(this).getBoundary()
98855           };
98856           LineString.prototype.isEquivalentClass = function isEquivalentClass (other) {
98857             return other instanceof LineString
98858           };
98859           LineString.prototype.clone = function clone () {
98860             var ls = Geometry$$1.prototype.clone.call(this);
98861             ls._points = this._points.clone();
98862             return ls
98863           };
98864           LineString.prototype.getCoordinateN = function getCoordinateN (n) {
98865             return this._points.getCoordinate(n)
98866           };
98867           LineString.prototype.getGeometryType = function getGeometryType () {
98868             return 'LineString'
98869           };
98870           LineString.prototype.copy = function copy () {
98871             return new LineString(this._points.copy(), this._factory)
98872           };
98873           LineString.prototype.getCoordinateSequence = function getCoordinateSequence () {
98874             return this._points
98875           };
98876           LineString.prototype.isEmpty = function isEmpty () {
98877             return this._points.size() === 0
98878           };
98879           LineString.prototype.init = function init (points) {
98880             if (points === null) {
98881               points = this.getFactory().getCoordinateSequenceFactory().create([]);
98882             }
98883             if (points.size() === 1) {
98884               throw new IllegalArgumentException('Invalid number of points in LineString (found ' + points.size() + ' - must be 0 or >= 2)')
98885             }
98886             this._points = points;
98887           };
98888           LineString.prototype.isCoordinate = function isCoordinate (pt) {
98889             var this$1 = this;
98890
98891             for (var i = 0; i < this._points.size(); i++) {
98892               if (this$1._points.getCoordinate(i).equals(pt)) {
98893                 return true
98894               }
98895             }
98896             return false
98897           };
98898           LineString.prototype.getStartPoint = function getStartPoint () {
98899             if (this.isEmpty()) {
98900               return null
98901             }
98902             return this.getPointN(0)
98903           };
98904           LineString.prototype.getPointN = function getPointN (n) {
98905             return this.getFactory().createPoint(this._points.getCoordinate(n))
98906           };
98907           LineString.prototype.interfaces_ = function interfaces_ () {
98908             return [Lineal]
98909           };
98910           LineString.prototype.getClass = function getClass () {
98911             return LineString
98912           };
98913           staticAccessors.serialVersionUID.get = function () { return 3110669828065365560 };
98914
98915           Object.defineProperties( LineString, staticAccessors );
98916
98917           return LineString;
98918         }(Geometry));
98919
98920         var Puntal = function Puntal () {};
98921
98922         Puntal.prototype.interfaces_ = function interfaces_ () {
98923           return []
98924         };
98925         Puntal.prototype.getClass = function getClass () {
98926           return Puntal
98927         };
98928
98929         var Point$1 = (function (Geometry$$1) {
98930           function Point (coordinates, factory) {
98931             Geometry$$1.call(this, factory);
98932             this._coordinates = coordinates || null;
98933             this.init(this._coordinates);
98934           }
98935
98936           if ( Geometry$$1 ) Point.__proto__ = Geometry$$1;
98937           Point.prototype = Object.create( Geometry$$1 && Geometry$$1.prototype );
98938           Point.prototype.constructor = Point;
98939
98940           var staticAccessors = { serialVersionUID: { configurable: true } };
98941           Point.prototype.computeEnvelopeInternal = function computeEnvelopeInternal () {
98942             if (this.isEmpty()) {
98943               return new Envelope()
98944             }
98945             var env = new Envelope();
98946             env.expandToInclude(this._coordinates.getX(0), this._coordinates.getY(0));
98947             return env
98948           };
98949           Point.prototype.getSortIndex = function getSortIndex () {
98950             return Geometry$$1.SORTINDEX_POINT
98951           };
98952           Point.prototype.getCoordinates = function getCoordinates () {
98953             return this.isEmpty() ? [] : [this.getCoordinate()]
98954           };
98955           Point.prototype.equalsExact = function equalsExact () {
98956             if (arguments.length === 2) {
98957               var other = arguments[0];
98958               var tolerance = arguments[1];
98959               if (!this.isEquivalentClass(other)) {
98960                 return false
98961               }
98962               if (this.isEmpty() && other.isEmpty()) {
98963                 return true
98964               }
98965               if (this.isEmpty() !== other.isEmpty()) {
98966                 return false
98967               }
98968               return this.equal(other.getCoordinate(), this.getCoordinate(), tolerance)
98969             } else { return Geometry$$1.prototype.equalsExact.apply(this, arguments) }
98970           };
98971           Point.prototype.normalize = function normalize () {};
98972           Point.prototype.getCoordinate = function getCoordinate () {
98973             return this._coordinates.size() !== 0 ? this._coordinates.getCoordinate(0) : null
98974           };
98975           Point.prototype.getBoundaryDimension = function getBoundaryDimension () {
98976             return Dimension.FALSE
98977           };
98978           Point.prototype.getDimension = function getDimension () {
98979             return 0
98980           };
98981           Point.prototype.getNumPoints = function getNumPoints () {
98982             return this.isEmpty() ? 0 : 1
98983           };
98984           Point.prototype.reverse = function reverse () {
98985             return this.copy()
98986           };
98987           Point.prototype.getX = function getX () {
98988             if (this.getCoordinate() === null) {
98989               throw new Error('getX called on empty Point')
98990             }
98991             return this.getCoordinate().x
98992           };
98993           Point.prototype.compareToSameClass = function compareToSameClass () {
98994             if (arguments.length === 1) {
98995               var other = arguments[0];
98996               var point$1 = other;
98997               return this.getCoordinate().compareTo(point$1.getCoordinate())
98998             } else if (arguments.length === 2) {
98999               var other$1 = arguments[0];
99000               var comp = arguments[1];
99001               var point = other$1;
99002               return comp.compare(this._coordinates, point._coordinates)
99003             }
99004           };
99005           Point.prototype.apply = function apply () {
99006             if (hasInterface(arguments[0], CoordinateFilter)) {
99007               var filter = arguments[0];
99008               if (this.isEmpty()) {
99009                 return null
99010               }
99011               filter.filter(this.getCoordinate());
99012             } else if (hasInterface(arguments[0], CoordinateSequenceFilter)) {
99013               var filter$1 = arguments[0];
99014               if (this.isEmpty()) { return null }
99015               filter$1.filter(this._coordinates, 0);
99016               if (filter$1.isGeometryChanged()) { this.geometryChanged(); }
99017             } else if (hasInterface(arguments[0], GeometryFilter)) {
99018               var filter$2 = arguments[0];
99019               filter$2.filter(this);
99020             } else if (hasInterface(arguments[0], GeometryComponentFilter)) {
99021               var filter$3 = arguments[0];
99022               filter$3.filter(this);
99023             }
99024           };
99025           Point.prototype.getBoundary = function getBoundary () {
99026             return this.getFactory().createGeometryCollection(null)
99027           };
99028           Point.prototype.clone = function clone () {
99029             var p = Geometry$$1.prototype.clone.call(this);
99030             p._coordinates = this._coordinates.clone();
99031             return p
99032           };
99033           Point.prototype.getGeometryType = function getGeometryType () {
99034             return 'Point'
99035           };
99036           Point.prototype.copy = function copy () {
99037             return new Point(this._coordinates.copy(), this._factory)
99038           };
99039           Point.prototype.getCoordinateSequence = function getCoordinateSequence () {
99040             return this._coordinates
99041           };
99042           Point.prototype.getY = function getY () {
99043             if (this.getCoordinate() === null) {
99044               throw new Error('getY called on empty Point')
99045             }
99046             return this.getCoordinate().y
99047           };
99048           Point.prototype.isEmpty = function isEmpty () {
99049             return this._coordinates.size() === 0
99050           };
99051           Point.prototype.init = function init (coordinates) {
99052             if (coordinates === null) {
99053               coordinates = this.getFactory().getCoordinateSequenceFactory().create([]);
99054             }
99055             Assert.isTrue(coordinates.size() <= 1);
99056             this._coordinates = coordinates;
99057           };
99058           Point.prototype.isSimple = function isSimple () {
99059             return true
99060           };
99061           Point.prototype.interfaces_ = function interfaces_ () {
99062             return [Puntal]
99063           };
99064           Point.prototype.getClass = function getClass () {
99065             return Point
99066           };
99067           staticAccessors.serialVersionUID.get = function () { return 4902022702746614570 };
99068
99069           Object.defineProperties( Point, staticAccessors );
99070
99071           return Point;
99072         }(Geometry));
99073
99074         var Polygonal = function Polygonal () {};
99075
99076         Polygonal.prototype.interfaces_ = function interfaces_ () {
99077           return []
99078         };
99079         Polygonal.prototype.getClass = function getClass () {
99080           return Polygonal
99081         };
99082
99083         var Polygon = (function (Geometry$$1) {
99084           function Polygon (shell, holes, factory) {
99085             Geometry$$1.call(this, factory);
99086             this._shell = null;
99087             this._holes = null;
99088             if (shell === null) {
99089               shell = this.getFactory().createLinearRing();
99090             }
99091             if (holes === null) {
99092               holes = [];
99093             }
99094             if (Geometry$$1.hasNullElements(holes)) {
99095               throw new IllegalArgumentException('holes must not contain null elements')
99096             }
99097             if (shell.isEmpty() && Geometry$$1.hasNonEmptyElements(holes)) {
99098               throw new IllegalArgumentException('shell is empty but holes are not')
99099             }
99100             this._shell = shell;
99101             this._holes = holes;
99102           }
99103
99104           if ( Geometry$$1 ) Polygon.__proto__ = Geometry$$1;
99105           Polygon.prototype = Object.create( Geometry$$1 && Geometry$$1.prototype );
99106           Polygon.prototype.constructor = Polygon;
99107
99108           var staticAccessors = { serialVersionUID: { configurable: true } };
99109           Polygon.prototype.computeEnvelopeInternal = function computeEnvelopeInternal () {
99110             return this._shell.getEnvelopeInternal()
99111           };
99112           Polygon.prototype.getSortIndex = function getSortIndex () {
99113             return Geometry$$1.SORTINDEX_POLYGON
99114           };
99115           Polygon.prototype.getCoordinates = function getCoordinates () {
99116             var this$1 = this;
99117
99118             if (this.isEmpty()) {
99119               return []
99120             }
99121             var coordinates = new Array(this.getNumPoints()).fill(null);
99122             var k = -1;
99123             var shellCoordinates = this._shell.getCoordinates();
99124             for (var x = 0; x < shellCoordinates.length; x++) {
99125               k++;
99126               coordinates[k] = shellCoordinates[x];
99127             }
99128             for (var i = 0; i < this._holes.length; i++) {
99129               var childCoordinates = this$1._holes[i].getCoordinates();
99130               for (var j = 0; j < childCoordinates.length; j++) {
99131                 k++;
99132                 coordinates[k] = childCoordinates[j];
99133               }
99134             }
99135             return coordinates
99136           };
99137           Polygon.prototype.getArea = function getArea () {
99138             var this$1 = this;
99139
99140             var area = 0.0;
99141             area += Math.abs(CGAlgorithms.signedArea(this._shell.getCoordinateSequence()));
99142             for (var i = 0; i < this._holes.length; i++) {
99143               area -= Math.abs(CGAlgorithms.signedArea(this$1._holes[i].getCoordinateSequence()));
99144             }
99145             return area
99146           };
99147           Polygon.prototype.isRectangle = function isRectangle () {
99148             if (this.getNumInteriorRing() !== 0) { return false }
99149             if (this._shell === null) { return false }
99150             if (this._shell.getNumPoints() !== 5) { return false }
99151             var seq = this._shell.getCoordinateSequence();
99152             var env = this.getEnvelopeInternal();
99153             for (var i = 0; i < 5; i++) {
99154               var x = seq.getX(i);
99155               if (!(x === env.getMinX() || x === env.getMaxX())) { return false }
99156               var y = seq.getY(i);
99157               if (!(y === env.getMinY() || y === env.getMaxY())) { return false }
99158             }
99159             var prevX = seq.getX(0);
99160             var prevY = seq.getY(0);
99161             for (var i$1 = 1; i$1 <= 4; i$1++) {
99162               var x$1 = seq.getX(i$1);
99163               var y$1 = seq.getY(i$1);
99164               var xChanged = x$1 !== prevX;
99165               var yChanged = y$1 !== prevY;
99166               if (xChanged === yChanged) { return false }
99167               prevX = x$1;
99168               prevY = y$1;
99169             }
99170             return true
99171           };
99172           Polygon.prototype.equalsExact = function equalsExact () {
99173             var this$1 = this;
99174
99175             if (arguments.length === 2) {
99176               var other = arguments[0];
99177               var tolerance = arguments[1];
99178               if (!this.isEquivalentClass(other)) {
99179                 return false
99180               }
99181               var otherPolygon = other;
99182               var thisShell = this._shell;
99183               var otherPolygonShell = otherPolygon._shell;
99184               if (!thisShell.equalsExact(otherPolygonShell, tolerance)) {
99185                 return false
99186               }
99187               if (this._holes.length !== otherPolygon._holes.length) {
99188                 return false
99189               }
99190               for (var i = 0; i < this._holes.length; i++) {
99191                 if (!this$1._holes[i].equalsExact(otherPolygon._holes[i], tolerance)) {
99192                   return false
99193                 }
99194               }
99195               return true
99196             } else { return Geometry$$1.prototype.equalsExact.apply(this, arguments) }
99197           };
99198           Polygon.prototype.normalize = function normalize () {
99199             var this$1 = this;
99200
99201             if (arguments.length === 0) {
99202               this.normalize(this._shell, true);
99203               for (var i = 0; i < this._holes.length; i++) {
99204                 this$1.normalize(this$1._holes[i], false);
99205               }
99206               Arrays.sort(this._holes);
99207             } else if (arguments.length === 2) {
99208               var ring = arguments[0];
99209               var clockwise = arguments[1];
99210               if (ring.isEmpty()) {
99211                 return null
99212               }
99213               var uniqueCoordinates = new Array(ring.getCoordinates().length - 1).fill(null);
99214               System.arraycopy(ring.getCoordinates(), 0, uniqueCoordinates, 0, uniqueCoordinates.length);
99215               var minCoordinate = CoordinateArrays.minCoordinate(ring.getCoordinates());
99216               CoordinateArrays.scroll(uniqueCoordinates, minCoordinate);
99217               System.arraycopy(uniqueCoordinates, 0, ring.getCoordinates(), 0, uniqueCoordinates.length);
99218               ring.getCoordinates()[uniqueCoordinates.length] = uniqueCoordinates[0];
99219               if (CGAlgorithms.isCCW(ring.getCoordinates()) === clockwise) {
99220                 CoordinateArrays.reverse(ring.getCoordinates());
99221               }
99222             }
99223           };
99224           Polygon.prototype.getCoordinate = function getCoordinate () {
99225             return this._shell.getCoordinate()
99226           };
99227           Polygon.prototype.getNumInteriorRing = function getNumInteriorRing () {
99228             return this._holes.length
99229           };
99230           Polygon.prototype.getBoundaryDimension = function getBoundaryDimension () {
99231             return 1
99232           };
99233           Polygon.prototype.getDimension = function getDimension () {
99234             return 2
99235           };
99236           Polygon.prototype.getLength = function getLength () {
99237             var this$1 = this;
99238
99239             var len = 0.0;
99240             len += this._shell.getLength();
99241             for (var i = 0; i < this._holes.length; i++) {
99242               len += this$1._holes[i].getLength();
99243             }
99244             return len
99245           };
99246           Polygon.prototype.getNumPoints = function getNumPoints () {
99247             var this$1 = this;
99248
99249             var numPoints = this._shell.getNumPoints();
99250             for (var i = 0; i < this._holes.length; i++) {
99251               numPoints += this$1._holes[i].getNumPoints();
99252             }
99253             return numPoints
99254           };
99255           Polygon.prototype.reverse = function reverse () {
99256             var this$1 = this;
99257
99258             var poly = this.copy();
99259             poly._shell = this._shell.copy().reverse();
99260             poly._holes = new Array(this._holes.length).fill(null);
99261             for (var i = 0; i < this._holes.length; i++) {
99262               poly._holes[i] = this$1._holes[i].copy().reverse();
99263             }
99264             return poly
99265           };
99266           Polygon.prototype.convexHull = function convexHull () {
99267             return this.getExteriorRing().convexHull()
99268           };
99269           Polygon.prototype.compareToSameClass = function compareToSameClass () {
99270             var this$1 = this;
99271
99272             if (arguments.length === 1) {
99273               var o = arguments[0];
99274               var thisShell = this._shell;
99275               var otherShell = o._shell;
99276               return thisShell.compareToSameClass(otherShell)
99277             } else if (arguments.length === 2) {
99278               var o$1 = arguments[0];
99279               var comp = arguments[1];
99280               var poly = o$1;
99281               var thisShell$1 = this._shell;
99282               var otherShell$1 = poly._shell;
99283               var shellComp = thisShell$1.compareToSameClass(otherShell$1, comp);
99284               if (shellComp !== 0) { return shellComp }
99285               var nHole1 = this.getNumInteriorRing();
99286               var nHole2 = poly.getNumInteriorRing();
99287               var i = 0;
99288               while (i < nHole1 && i < nHole2) {
99289                 var thisHole = this$1.getInteriorRingN(i);
99290                 var otherHole = poly.getInteriorRingN(i);
99291                 var holeComp = thisHole.compareToSameClass(otherHole, comp);
99292                 if (holeComp !== 0) { return holeComp }
99293                 i++;
99294               }
99295               if (i < nHole1) { return 1 }
99296               if (i < nHole2) { return -1 }
99297               return 0
99298             }
99299           };
99300           Polygon.prototype.apply = function apply (filter) {
99301             var this$1 = this;
99302
99303             if (hasInterface(filter, CoordinateFilter)) {
99304               this._shell.apply(filter);
99305               for (var i$1 = 0; i$1 < this._holes.length; i$1++) {
99306                 this$1._holes[i$1].apply(filter);
99307               }
99308             } else if (hasInterface(filter, CoordinateSequenceFilter)) {
99309               this._shell.apply(filter);
99310               if (!filter.isDone()) {
99311                 for (var i$2 = 0; i$2 < this._holes.length; i$2++) {
99312                   this$1._holes[i$2].apply(filter);
99313                   if (filter.isDone()) { break }
99314                 }
99315               }
99316               if (filter.isGeometryChanged()) { this.geometryChanged(); }
99317             } else if (hasInterface(filter, GeometryFilter)) {
99318               filter.filter(this);
99319             } else if (hasInterface(filter, GeometryComponentFilter)) {
99320               filter.filter(this);
99321               this._shell.apply(filter);
99322               for (var i = 0; i < this._holes.length; i++) {
99323                 this$1._holes[i].apply(filter);
99324               }
99325             }
99326           };
99327           Polygon.prototype.getBoundary = function getBoundary () {
99328             var this$1 = this;
99329
99330             if (this.isEmpty()) {
99331               return this.getFactory().createMultiLineString()
99332             }
99333             var rings = new Array(this._holes.length + 1).fill(null);
99334             rings[0] = this._shell;
99335             for (var i = 0; i < this._holes.length; i++) {
99336               rings[i + 1] = this$1._holes[i];
99337             }
99338             if (rings.length <= 1) { return this.getFactory().createLinearRing(rings[0].getCoordinateSequence()) }
99339             return this.getFactory().createMultiLineString(rings)
99340           };
99341           Polygon.prototype.clone = function clone () {
99342             var this$1 = this;
99343
99344             var poly = Geometry$$1.prototype.clone.call(this);
99345             poly._shell = this._shell.clone();
99346             poly._holes = new Array(this._holes.length).fill(null);
99347             for (var i = 0; i < this._holes.length; i++) {
99348               poly._holes[i] = this$1._holes[i].clone();
99349             }
99350             return poly
99351           };
99352           Polygon.prototype.getGeometryType = function getGeometryType () {
99353             return 'Polygon'
99354           };
99355           Polygon.prototype.copy = function copy () {
99356             var this$1 = this;
99357
99358             var shell = this._shell.copy();
99359             var holes = new Array(this._holes.length).fill(null);
99360             for (var i = 0; i < holes.length; i++) {
99361               holes[i] = this$1._holes[i].copy();
99362             }
99363             return new Polygon(shell, holes, this._factory)
99364           };
99365           Polygon.prototype.getExteriorRing = function getExteriorRing () {
99366             return this._shell
99367           };
99368           Polygon.prototype.isEmpty = function isEmpty () {
99369             return this._shell.isEmpty()
99370           };
99371           Polygon.prototype.getInteriorRingN = function getInteriorRingN (n) {
99372             return this._holes[n]
99373           };
99374           Polygon.prototype.interfaces_ = function interfaces_ () {
99375             return [Polygonal]
99376           };
99377           Polygon.prototype.getClass = function getClass () {
99378             return Polygon
99379           };
99380           staticAccessors.serialVersionUID.get = function () { return -3494792200821764533 };
99381
99382           Object.defineProperties( Polygon, staticAccessors );
99383
99384           return Polygon;
99385         }(Geometry));
99386
99387         var MultiPoint = (function (GeometryCollection$$1) {
99388           function MultiPoint () {
99389             GeometryCollection$$1.apply(this, arguments);
99390           }
99391
99392           if ( GeometryCollection$$1 ) MultiPoint.__proto__ = GeometryCollection$$1;
99393           MultiPoint.prototype = Object.create( GeometryCollection$$1 && GeometryCollection$$1.prototype );
99394           MultiPoint.prototype.constructor = MultiPoint;
99395
99396           var staticAccessors = { serialVersionUID: { configurable: true } };
99397
99398           MultiPoint.prototype.getSortIndex = function getSortIndex () {
99399             return Geometry.SORTINDEX_MULTIPOINT
99400           };
99401           MultiPoint.prototype.isValid = function isValid () {
99402             return true
99403           };
99404           MultiPoint.prototype.equalsExact = function equalsExact () {
99405             if (arguments.length === 2) {
99406               var other = arguments[0];
99407               var tolerance = arguments[1];
99408               if (!this.isEquivalentClass(other)) {
99409                 return false
99410               }
99411               return GeometryCollection$$1.prototype.equalsExact.call(this, other, tolerance)
99412             } else { return GeometryCollection$$1.prototype.equalsExact.apply(this, arguments) }
99413           };
99414           MultiPoint.prototype.getCoordinate = function getCoordinate () {
99415             if (arguments.length === 1) {
99416               var n = arguments[0];
99417               return this._geometries[n].getCoordinate()
99418             } else { return GeometryCollection$$1.prototype.getCoordinate.apply(this, arguments) }
99419           };
99420           MultiPoint.prototype.getBoundaryDimension = function getBoundaryDimension () {
99421             return Dimension.FALSE
99422           };
99423           MultiPoint.prototype.getDimension = function getDimension () {
99424             return 0
99425           };
99426           MultiPoint.prototype.getBoundary = function getBoundary () {
99427             return this.getFactory().createGeometryCollection(null)
99428           };
99429           MultiPoint.prototype.getGeometryType = function getGeometryType () {
99430             return 'MultiPoint'
99431           };
99432           MultiPoint.prototype.copy = function copy () {
99433             var this$1 = this;
99434
99435             var points = new Array(this._geometries.length).fill(null);
99436             for (var i = 0; i < points.length; i++) {
99437               points[i] = this$1._geometries[i].copy();
99438             }
99439             return new MultiPoint(points, this._factory)
99440           };
99441           MultiPoint.prototype.interfaces_ = function interfaces_ () {
99442             return [Puntal]
99443           };
99444           MultiPoint.prototype.getClass = function getClass () {
99445             return MultiPoint
99446           };
99447           staticAccessors.serialVersionUID.get = function () { return -8048474874175355449 };
99448
99449           Object.defineProperties( MultiPoint, staticAccessors );
99450
99451           return MultiPoint;
99452         }(GeometryCollection));
99453
99454         var LinearRing = (function (LineString$$1) {
99455           function LinearRing (points, factory) {
99456             if (points instanceof Coordinate && factory instanceof GeometryFactory) {
99457               points = factory.getCoordinateSequenceFactory().create(points);
99458             }
99459             LineString$$1.call(this, points, factory);
99460             this.validateConstruction();
99461           }
99462
99463           if ( LineString$$1 ) LinearRing.__proto__ = LineString$$1;
99464           LinearRing.prototype = Object.create( LineString$$1 && LineString$$1.prototype );
99465           LinearRing.prototype.constructor = LinearRing;
99466
99467           var staticAccessors = { MINIMUM_VALID_SIZE: { configurable: true },serialVersionUID: { configurable: true } };
99468           LinearRing.prototype.getSortIndex = function getSortIndex () {
99469             return Geometry.SORTINDEX_LINEARRING
99470           };
99471           LinearRing.prototype.getBoundaryDimension = function getBoundaryDimension () {
99472             return Dimension.FALSE
99473           };
99474           LinearRing.prototype.isClosed = function isClosed () {
99475             if (this.isEmpty()) {
99476               return true
99477             }
99478             return LineString$$1.prototype.isClosed.call(this)
99479           };
99480           LinearRing.prototype.reverse = function reverse () {
99481             var seq = this._points.copy();
99482             CoordinateSequences.reverse(seq);
99483             var rev = this.getFactory().createLinearRing(seq);
99484             return rev
99485           };
99486           LinearRing.prototype.validateConstruction = function validateConstruction () {
99487             if (!this.isEmpty() && !LineString$$1.prototype.isClosed.call(this)) {
99488               throw new IllegalArgumentException('Points of LinearRing do not form a closed linestring')
99489             }
99490             if (this.getCoordinateSequence().size() >= 1 && this.getCoordinateSequence().size() < LinearRing.MINIMUM_VALID_SIZE) {
99491               throw new IllegalArgumentException('Invalid number of points in LinearRing (found ' + this.getCoordinateSequence().size() + ' - must be 0 or >= 4)')
99492             }
99493           };
99494           LinearRing.prototype.getGeometryType = function getGeometryType () {
99495             return 'LinearRing'
99496           };
99497           LinearRing.prototype.copy = function copy () {
99498             return new LinearRing(this._points.copy(), this._factory)
99499           };
99500           LinearRing.prototype.interfaces_ = function interfaces_ () {
99501             return []
99502           };
99503           LinearRing.prototype.getClass = function getClass () {
99504             return LinearRing
99505           };
99506           staticAccessors.MINIMUM_VALID_SIZE.get = function () { return 4 };
99507           staticAccessors.serialVersionUID.get = function () { return -4261142084085851829 };
99508
99509           Object.defineProperties( LinearRing, staticAccessors );
99510
99511           return LinearRing;
99512         }(LineString));
99513
99514         var MultiPolygon = (function (GeometryCollection$$1) {
99515           function MultiPolygon () {
99516             GeometryCollection$$1.apply(this, arguments);
99517           }
99518
99519           if ( GeometryCollection$$1 ) MultiPolygon.__proto__ = GeometryCollection$$1;
99520           MultiPolygon.prototype = Object.create( GeometryCollection$$1 && GeometryCollection$$1.prototype );
99521           MultiPolygon.prototype.constructor = MultiPolygon;
99522
99523           var staticAccessors = { serialVersionUID: { configurable: true } };
99524
99525           MultiPolygon.prototype.getSortIndex = function getSortIndex () {
99526             return Geometry.SORTINDEX_MULTIPOLYGON
99527           };
99528           MultiPolygon.prototype.equalsExact = function equalsExact () {
99529             if (arguments.length === 2) {
99530               var other = arguments[0];
99531               var tolerance = arguments[1];
99532               if (!this.isEquivalentClass(other)) {
99533                 return false
99534               }
99535               return GeometryCollection$$1.prototype.equalsExact.call(this, other, tolerance)
99536             } else { return GeometryCollection$$1.prototype.equalsExact.apply(this, arguments) }
99537           };
99538           MultiPolygon.prototype.getBoundaryDimension = function getBoundaryDimension () {
99539             return 1
99540           };
99541           MultiPolygon.prototype.getDimension = function getDimension () {
99542             return 2
99543           };
99544           MultiPolygon.prototype.reverse = function reverse () {
99545             var this$1 = this;
99546
99547             var n = this._geometries.length;
99548             var revGeoms = new Array(n).fill(null);
99549             for (var i = 0; i < this._geometries.length; i++) {
99550               revGeoms[i] = this$1._geometries[i].reverse();
99551             }
99552             return this.getFactory().createMultiPolygon(revGeoms)
99553           };
99554           MultiPolygon.prototype.getBoundary = function getBoundary () {
99555             var this$1 = this;
99556
99557             if (this.isEmpty()) {
99558               return this.getFactory().createMultiLineString()
99559             }
99560             var allRings = new ArrayList();
99561             for (var i = 0; i < this._geometries.length; i++) {
99562               var polygon = this$1._geometries[i];
99563               var rings = polygon.getBoundary();
99564               for (var j = 0; j < rings.getNumGeometries(); j++) {
99565                 allRings.add(rings.getGeometryN(j));
99566               }
99567             }
99568             var allRingsArray = new Array(allRings.size()).fill(null);
99569             return this.getFactory().createMultiLineString(allRings.toArray(allRingsArray))
99570           };
99571           MultiPolygon.prototype.getGeometryType = function getGeometryType () {
99572             return 'MultiPolygon'
99573           };
99574           MultiPolygon.prototype.copy = function copy () {
99575             var this$1 = this;
99576
99577             var polygons = new Array(this._geometries.length).fill(null);
99578             for (var i = 0; i < polygons.length; i++) {
99579               polygons[i] = this$1._geometries[i].copy();
99580             }
99581             return new MultiPolygon(polygons, this._factory)
99582           };
99583           MultiPolygon.prototype.interfaces_ = function interfaces_ () {
99584             return [Polygonal]
99585           };
99586           MultiPolygon.prototype.getClass = function getClass () {
99587             return MultiPolygon
99588           };
99589           staticAccessors.serialVersionUID.get = function () { return -551033529766975875 };
99590
99591           Object.defineProperties( MultiPolygon, staticAccessors );
99592
99593           return MultiPolygon;
99594         }(GeometryCollection));
99595
99596         var GeometryEditor = function GeometryEditor (factory) {
99597           this._factory = factory || null;
99598           this._isUserDataCopied = false;
99599         };
99600
99601         var staticAccessors$16 = { NoOpGeometryOperation: { configurable: true },CoordinateOperation: { configurable: true },CoordinateSequenceOperation: { configurable: true } };
99602         GeometryEditor.prototype.setCopyUserData = function setCopyUserData (isUserDataCopied) {
99603           this._isUserDataCopied = isUserDataCopied;
99604         };
99605         GeometryEditor.prototype.edit = function edit (geometry, operation) {
99606           if (geometry === null) { return null }
99607           var result = this.editInternal(geometry, operation);
99608           if (this._isUserDataCopied) {
99609             result.setUserData(geometry.getUserData());
99610           }
99611           return result
99612         };
99613         GeometryEditor.prototype.editInternal = function editInternal (geometry, operation) {
99614           if (this._factory === null) { this._factory = geometry.getFactory(); }
99615           if (geometry instanceof GeometryCollection) {
99616             return this.editGeometryCollection(geometry, operation)
99617           }
99618           if (geometry instanceof Polygon) {
99619             return this.editPolygon(geometry, operation)
99620           }
99621           if (geometry instanceof Point$1) {
99622             return operation.edit(geometry, this._factory)
99623           }
99624           if (geometry instanceof LineString) {
99625             return operation.edit(geometry, this._factory)
99626           }
99627           Assert.shouldNeverReachHere('Unsupported Geometry class: ' + geometry.getClass().getName());
99628           return null
99629         };
99630         GeometryEditor.prototype.editGeometryCollection = function editGeometryCollection (collection, operation) {
99631             var this$1 = this;
99632
99633           var collectionForType = operation.edit(collection, this._factory);
99634           var geometries = new ArrayList();
99635           for (var i = 0; i < collectionForType.getNumGeometries(); i++) {
99636             var geometry = this$1.edit(collectionForType.getGeometryN(i), operation);
99637             if (geometry === null || geometry.isEmpty()) {
99638               continue
99639             }
99640             geometries.add(geometry);
99641           }
99642           if (collectionForType.getClass() === MultiPoint) {
99643             return this._factory.createMultiPoint(geometries.toArray([]))
99644           }
99645           if (collectionForType.getClass() === MultiLineString) {
99646             return this._factory.createMultiLineString(geometries.toArray([]))
99647           }
99648           if (collectionForType.getClass() === MultiPolygon) {
99649             return this._factory.createMultiPolygon(geometries.toArray([]))
99650           }
99651           return this._factory.createGeometryCollection(geometries.toArray([]))
99652         };
99653         GeometryEditor.prototype.editPolygon = function editPolygon (polygon, operation) {
99654             var this$1 = this;
99655
99656           var newPolygon = operation.edit(polygon, this._factory);
99657           if (newPolygon === null) { newPolygon = this._factory.createPolygon(null); }
99658           if (newPolygon.isEmpty()) {
99659             return newPolygon
99660           }
99661           var shell = this.edit(newPolygon.getExteriorRing(), operation);
99662           if (shell === null || shell.isEmpty()) {
99663             return this._factory.createPolygon()
99664           }
99665           var holes = new ArrayList();
99666           for (var i = 0; i < newPolygon.getNumInteriorRing(); i++) {
99667             var hole = this$1.edit(newPolygon.getInteriorRingN(i), operation);
99668             if (hole === null || hole.isEmpty()) {
99669               continue
99670             }
99671             holes.add(hole);
99672           }
99673           return this._factory.createPolygon(shell, holes.toArray([]))
99674         };
99675         GeometryEditor.prototype.interfaces_ = function interfaces_ () {
99676           return []
99677         };
99678         GeometryEditor.prototype.getClass = function getClass () {
99679           return GeometryEditor
99680         };
99681         GeometryEditor.GeometryEditorOperation = function GeometryEditorOperation () {};
99682         staticAccessors$16.NoOpGeometryOperation.get = function () { return NoOpGeometryOperation };
99683         staticAccessors$16.CoordinateOperation.get = function () { return CoordinateOperation };
99684         staticAccessors$16.CoordinateSequenceOperation.get = function () { return CoordinateSequenceOperation };
99685
99686         Object.defineProperties( GeometryEditor, staticAccessors$16 );
99687
99688         var NoOpGeometryOperation = function NoOpGeometryOperation () {};
99689
99690         NoOpGeometryOperation.prototype.edit = function edit (geometry, factory) {
99691           return geometry
99692         };
99693         NoOpGeometryOperation.prototype.interfaces_ = function interfaces_ () {
99694           return [GeometryEditor.GeometryEditorOperation]
99695         };
99696         NoOpGeometryOperation.prototype.getClass = function getClass () {
99697           return NoOpGeometryOperation
99698         };
99699
99700         var CoordinateOperation = function CoordinateOperation () {};
99701
99702         CoordinateOperation.prototype.edit = function edit (geometry, factory) {
99703           var coords = this.editCoordinates(geometry.getCoordinates(), geometry);
99704           if (coords === null) { return geometry }
99705           if (geometry instanceof LinearRing) {
99706             return factory.createLinearRing(coords)
99707           }
99708           if (geometry instanceof LineString) {
99709             return factory.createLineString(coords)
99710           }
99711           if (geometry instanceof Point$1) {
99712             if (coords.length > 0) {
99713               return factory.createPoint(coords[0])
99714             } else {
99715               return factory.createPoint()
99716             }
99717           }
99718           return geometry
99719         };
99720         CoordinateOperation.prototype.interfaces_ = function interfaces_ () {
99721           return [GeometryEditor.GeometryEditorOperation]
99722         };
99723         CoordinateOperation.prototype.getClass = function getClass () {
99724           return CoordinateOperation
99725         };
99726
99727         var CoordinateSequenceOperation = function CoordinateSequenceOperation () {};
99728
99729         CoordinateSequenceOperation.prototype.edit = function edit (geometry, factory) {
99730           if (geometry instanceof LinearRing) {
99731             return factory.createLinearRing(this.edit(geometry.getCoordinateSequence(), geometry))
99732           }
99733           if (geometry instanceof LineString) {
99734             return factory.createLineString(this.edit(geometry.getCoordinateSequence(), geometry))
99735           }
99736           if (geometry instanceof Point$1) {
99737             return factory.createPoint(this.edit(geometry.getCoordinateSequence(), geometry))
99738           }
99739           return geometry
99740         };
99741         CoordinateSequenceOperation.prototype.interfaces_ = function interfaces_ () {
99742           return [GeometryEditor.GeometryEditorOperation]
99743         };
99744         CoordinateSequenceOperation.prototype.getClass = function getClass () {
99745           return CoordinateSequenceOperation
99746         };
99747
99748         var CoordinateArraySequence = function CoordinateArraySequence () {
99749           var this$1 = this;
99750
99751           this._dimension = 3;
99752           this._coordinates = null;
99753           if (arguments.length === 1) {
99754             if (arguments[0] instanceof Array) {
99755               this._coordinates = arguments[0];
99756               this._dimension = 3;
99757             } else if (Number.isInteger(arguments[0])) {
99758               var size = arguments[0];
99759               this._coordinates = new Array(size).fill(null);
99760               for (var i = 0; i < size; i++) {
99761                 this$1._coordinates[i] = new Coordinate();
99762               }
99763             } else if (hasInterface(arguments[0], CoordinateSequence)) {
99764               var coordSeq = arguments[0];
99765               if (coordSeq === null) {
99766                 this._coordinates = new Array(0).fill(null);
99767                 return null
99768               }
99769               this._dimension = coordSeq.getDimension();
99770               this._coordinates = new Array(coordSeq.size()).fill(null);
99771               for (var i$1 = 0; i$1 < this._coordinates.length; i$1++) {
99772                 this$1._coordinates[i$1] = coordSeq.getCoordinateCopy(i$1);
99773               }
99774             }
99775           } else if (arguments.length === 2) {
99776             if (arguments[0] instanceof Array && Number.isInteger(arguments[1])) {
99777               var coordinates = arguments[0];
99778               var dimension = arguments[1];
99779               this._coordinates = coordinates;
99780               this._dimension = dimension;
99781               if (coordinates === null) { this._coordinates = new Array(0).fill(null); }
99782             } else if (Number.isInteger(arguments[0]) && Number.isInteger(arguments[1])) {
99783               var size$1 = arguments[0];
99784               var dimension$1 = arguments[1];
99785               this._coordinates = new Array(size$1).fill(null);
99786               this._dimension = dimension$1;
99787               for (var i$2 = 0; i$2 < size$1; i$2++) {
99788                 this$1._coordinates[i$2] = new Coordinate();
99789               }
99790             }
99791           }
99792         };
99793
99794         var staticAccessors$18 = { serialVersionUID: { configurable: true } };
99795         CoordinateArraySequence.prototype.setOrdinate = function setOrdinate (index, ordinateIndex, value) {
99796           switch (ordinateIndex) {
99797             case CoordinateSequence.X:
99798               this._coordinates[index].x = value;
99799               break
99800             case CoordinateSequence.Y:
99801               this._coordinates[index].y = value;
99802               break
99803             case CoordinateSequence.Z:
99804               this._coordinates[index].z = value;
99805               break
99806             default:
99807               throw new IllegalArgumentException('invalid ordinateIndex')
99808           }
99809         };
99810         CoordinateArraySequence.prototype.size = function size () {
99811           return this._coordinates.length
99812         };
99813         CoordinateArraySequence.prototype.getOrdinate = function getOrdinate (index, ordinateIndex) {
99814           switch (ordinateIndex) {
99815             case CoordinateSequence.X:
99816               return this._coordinates[index].x
99817             case CoordinateSequence.Y:
99818               return this._coordinates[index].y
99819             case CoordinateSequence.Z:
99820               return this._coordinates[index].z
99821           }
99822           return Double.NaN
99823         };
99824         CoordinateArraySequence.prototype.getCoordinate = function getCoordinate () {
99825           if (arguments.length === 1) {
99826             var i = arguments[0];
99827             return this._coordinates[i]
99828           } else if (arguments.length === 2) {
99829             var index = arguments[0];
99830             var coord = arguments[1];
99831             coord.x = this._coordinates[index].x;
99832             coord.y = this._coordinates[index].y;
99833             coord.z = this._coordinates[index].z;
99834           }
99835         };
99836         CoordinateArraySequence.prototype.getCoordinateCopy = function getCoordinateCopy (i) {
99837           return new Coordinate(this._coordinates[i])
99838         };
99839         CoordinateArraySequence.prototype.getDimension = function getDimension () {
99840           return this._dimension
99841         };
99842         CoordinateArraySequence.prototype.getX = function getX (index) {
99843           return this._coordinates[index].x
99844         };
99845         CoordinateArraySequence.prototype.clone = function clone () {
99846             var this$1 = this;
99847
99848           var cloneCoordinates = new Array(this.size()).fill(null);
99849           for (var i = 0; i < this._coordinates.length; i++) {
99850             cloneCoordinates[i] = this$1._coordinates[i].clone();
99851           }
99852           return new CoordinateArraySequence(cloneCoordinates, this._dimension)
99853         };
99854         CoordinateArraySequence.prototype.expandEnvelope = function expandEnvelope (env) {
99855             var this$1 = this;
99856
99857           for (var i = 0; i < this._coordinates.length; i++) {
99858             env.expandToInclude(this$1._coordinates[i]);
99859           }
99860           return env
99861         };
99862         CoordinateArraySequence.prototype.copy = function copy () {
99863             var this$1 = this;
99864
99865           var cloneCoordinates = new Array(this.size()).fill(null);
99866           for (var i = 0; i < this._coordinates.length; i++) {
99867             cloneCoordinates[i] = this$1._coordinates[i].copy();
99868           }
99869           return new CoordinateArraySequence(cloneCoordinates, this._dimension)
99870         };
99871         CoordinateArraySequence.prototype.toString = function toString () {
99872             var this$1 = this;
99873
99874           if (this._coordinates.length > 0) {
99875             var strBuf = new StringBuffer(17 * this._coordinates.length);
99876             strBuf.append('(');
99877             strBuf.append(this._coordinates[0]);
99878             for (var i = 1; i < this._coordinates.length; i++) {
99879               strBuf.append(', ');
99880               strBuf.append(this$1._coordinates[i]);
99881             }
99882             strBuf.append(')');
99883             return strBuf.toString()
99884           } else {
99885             return '()'
99886           }
99887         };
99888         CoordinateArraySequence.prototype.getY = function getY (index) {
99889           return this._coordinates[index].y
99890         };
99891         CoordinateArraySequence.prototype.toCoordinateArray = function toCoordinateArray () {
99892           return this._coordinates
99893         };
99894         CoordinateArraySequence.prototype.interfaces_ = function interfaces_ () {
99895           return [CoordinateSequence, Serializable]
99896         };
99897         CoordinateArraySequence.prototype.getClass = function getClass () {
99898           return CoordinateArraySequence
99899         };
99900         staticAccessors$18.serialVersionUID.get = function () { return -915438501601840650 };
99901
99902         Object.defineProperties( CoordinateArraySequence, staticAccessors$18 );
99903
99904         var CoordinateArraySequenceFactory = function CoordinateArraySequenceFactory () {};
99905
99906         var staticAccessors$17 = { serialVersionUID: { configurable: true },instanceObject: { configurable: true } };
99907
99908         CoordinateArraySequenceFactory.prototype.readResolve = function readResolve () {
99909           return CoordinateArraySequenceFactory.instance()
99910         };
99911         CoordinateArraySequenceFactory.prototype.create = function create () {
99912           if (arguments.length === 1) {
99913             if (arguments[0] instanceof Array) {
99914               var coordinates = arguments[0];
99915               return new CoordinateArraySequence(coordinates)
99916             } else if (hasInterface(arguments[0], CoordinateSequence)) {
99917               var coordSeq = arguments[0];
99918               return new CoordinateArraySequence(coordSeq)
99919             }
99920           } else if (arguments.length === 2) {
99921             var size = arguments[0];
99922             var dimension = arguments[1];
99923             if (dimension > 3) { dimension = 3; }
99924             if (dimension < 2) { return new CoordinateArraySequence(size) }
99925             return new CoordinateArraySequence(size, dimension)
99926           }
99927         };
99928         CoordinateArraySequenceFactory.prototype.interfaces_ = function interfaces_ () {
99929           return [CoordinateSequenceFactory, Serializable]
99930         };
99931         CoordinateArraySequenceFactory.prototype.getClass = function getClass () {
99932           return CoordinateArraySequenceFactory
99933         };
99934         CoordinateArraySequenceFactory.instance = function instance () {
99935           return CoordinateArraySequenceFactory.instanceObject
99936         };
99937
99938         staticAccessors$17.serialVersionUID.get = function () { return -4099577099607551657 };
99939         staticAccessors$17.instanceObject.get = function () { return new CoordinateArraySequenceFactory() };
99940
99941         Object.defineProperties( CoordinateArraySequenceFactory, staticAccessors$17 );
99942
99943         /**
99944          * @see http://download.oracle.com/javase/6/docs/api/java/util/HashMap.html
99945          *
99946          * @extends {javascript.util.Map}
99947          * @constructor
99948          * @private
99949          */
99950         var HashMap = (function (MapInterface) {
99951           function HashMap () {
99952             MapInterface.call(this);
99953             this.map_ = new Map();
99954           }
99955
99956           if ( MapInterface ) HashMap.__proto__ = MapInterface;
99957           HashMap.prototype = Object.create( MapInterface && MapInterface.prototype );
99958           HashMap.prototype.constructor = HashMap;
99959           /**
99960            * @override
99961            */
99962           HashMap.prototype.get = function get (key) {
99963             return this.map_.get(key) || null
99964           };
99965
99966           /**
99967            * @override
99968            */
99969           HashMap.prototype.put = function put (key, value) {
99970             this.map_.set(key, value);
99971             return value
99972           };
99973
99974           /**
99975            * @override
99976            */
99977           HashMap.prototype.values = function values () {
99978             var arrayList = new ArrayList();
99979             var it = this.map_.values();
99980             var o = it.next();
99981             while (!o.done) {
99982               arrayList.add(o.value);
99983               o = it.next();
99984             }
99985             return arrayList
99986           };
99987
99988           /**
99989            * @override
99990            */
99991           HashMap.prototype.entrySet = function entrySet () {
99992             var hashSet = new HashSet();
99993             this.map_.entries().forEach(function (entry) { return hashSet.add(entry); });
99994             return hashSet
99995           };
99996
99997           /**
99998            * @override
99999            */
100000           HashMap.prototype.size = function size () {
100001             return this.map_.size()
100002           };
100003
100004           return HashMap;
100005         }(Map$1$1));
100006
100007         var PrecisionModel = function PrecisionModel () {
100008           this._modelType = null;
100009           this._scale = null;
100010           if (arguments.length === 0) {
100011             this._modelType = PrecisionModel.FLOATING;
100012           } else if (arguments.length === 1) {
100013             if (arguments[0] instanceof Type$2) {
100014               var modelType = arguments[0];
100015               this._modelType = modelType;
100016               if (modelType === PrecisionModel.FIXED) {
100017                 this.setScale(1.0);
100018               }
100019             } else if (typeof arguments[0] === 'number') {
100020               var scale = arguments[0];
100021               this._modelType = PrecisionModel.FIXED;
100022               this.setScale(scale);
100023             } else if (arguments[0] instanceof PrecisionModel) {
100024               var pm = arguments[0];
100025               this._modelType = pm._modelType;
100026               this._scale = pm._scale;
100027             }
100028           }
100029         };
100030
100031         var staticAccessors$19 = { serialVersionUID: { configurable: true },maximumPreciseValue: { configurable: true } };
100032         PrecisionModel.prototype.equals = function equals (other) {
100033           if (!(other instanceof PrecisionModel)) {
100034             return false
100035           }
100036           var otherPrecisionModel = other;
100037           return this._modelType === otherPrecisionModel._modelType && this._scale === otherPrecisionModel._scale
100038         };
100039         PrecisionModel.prototype.compareTo = function compareTo (o) {
100040           var other = o;
100041           var sigDigits = this.getMaximumSignificantDigits();
100042           var otherSigDigits = other.getMaximumSignificantDigits();
100043           return new Integer(sigDigits).compareTo(new Integer(otherSigDigits))
100044         };
100045         PrecisionModel.prototype.getScale = function getScale () {
100046           return this._scale
100047         };
100048         PrecisionModel.prototype.isFloating = function isFloating () {
100049           return this._modelType === PrecisionModel.FLOATING || this._modelType === PrecisionModel.FLOATING_SINGLE
100050         };
100051         PrecisionModel.prototype.getType = function getType () {
100052           return this._modelType
100053         };
100054         PrecisionModel.prototype.toString = function toString () {
100055           var description = 'UNKNOWN';
100056           if (this._modelType === PrecisionModel.FLOATING) {
100057             description = 'Floating';
100058           } else if (this._modelType === PrecisionModel.FLOATING_SINGLE) {
100059             description = 'Floating-Single';
100060           } else if (this._modelType === PrecisionModel.FIXED) {
100061             description = 'Fixed (Scale=' + this.getScale() + ')';
100062           }
100063           return description
100064         };
100065         PrecisionModel.prototype.makePrecise = function makePrecise () {
100066           if (typeof arguments[0] === 'number') {
100067             var val = arguments[0];
100068             if (Double.isNaN(val)) { return val }
100069             if (this._modelType === PrecisionModel.FLOATING_SINGLE) {
100070               var floatSingleVal = val;
100071               return floatSingleVal
100072             }
100073             if (this._modelType === PrecisionModel.FIXED) {
100074               return Math.round(val * this._scale) / this._scale
100075             }
100076             return val
100077           } else if (arguments[0] instanceof Coordinate) {
100078             var coord = arguments[0];
100079             if (this._modelType === PrecisionModel.FLOATING) { return null }
100080             coord.x = this.makePrecise(coord.x);
100081             coord.y = this.makePrecise(coord.y);
100082           }
100083         };
100084         PrecisionModel.prototype.getMaximumSignificantDigits = function getMaximumSignificantDigits () {
100085           var maxSigDigits = 16;
100086           if (this._modelType === PrecisionModel.FLOATING) {
100087             maxSigDigits = 16;
100088           } else if (this._modelType === PrecisionModel.FLOATING_SINGLE) {
100089             maxSigDigits = 6;
100090           } else if (this._modelType === PrecisionModel.FIXED) {
100091             maxSigDigits = 1 + Math.trunc(Math.ceil(Math.log(this.getScale()) / Math.log(10)));
100092           }
100093           return maxSigDigits
100094         };
100095         PrecisionModel.prototype.setScale = function setScale (scale) {
100096           this._scale = Math.abs(scale);
100097         };
100098         PrecisionModel.prototype.interfaces_ = function interfaces_ () {
100099           return [Serializable, Comparable]
100100         };
100101         PrecisionModel.prototype.getClass = function getClass () {
100102           return PrecisionModel
100103         };
100104         PrecisionModel.mostPrecise = function mostPrecise (pm1, pm2) {
100105           if (pm1.compareTo(pm2) >= 0) { return pm1 }
100106           return pm2
100107         };
100108         staticAccessors$19.serialVersionUID.get = function () { return 7777263578777803835 };
100109         staticAccessors$19.maximumPreciseValue.get = function () { return 9007199254740992.0 };
100110
100111         Object.defineProperties( PrecisionModel, staticAccessors$19 );
100112
100113         var Type$2 = function Type (name) {
100114           this._name = name || null;
100115           Type.nameToTypeMap.put(name, this);
100116         };
100117
100118         var staticAccessors$1$1 = { serialVersionUID: { configurable: true },nameToTypeMap: { configurable: true } };
100119         Type$2.prototype.readResolve = function readResolve () {
100120           return Type$2.nameToTypeMap.get(this._name)
100121         };
100122         Type$2.prototype.toString = function toString () {
100123           return this._name
100124         };
100125         Type$2.prototype.interfaces_ = function interfaces_ () {
100126           return [Serializable]
100127         };
100128         Type$2.prototype.getClass = function getClass () {
100129           return Type$2
100130         };
100131         staticAccessors$1$1.serialVersionUID.get = function () { return -5528602631731589822 };
100132         staticAccessors$1$1.nameToTypeMap.get = function () { return new HashMap() };
100133
100134         Object.defineProperties( Type$2, staticAccessors$1$1 );
100135
100136         PrecisionModel.Type = Type$2;
100137         PrecisionModel.FIXED = new Type$2('FIXED');
100138         PrecisionModel.FLOATING = new Type$2('FLOATING');
100139         PrecisionModel.FLOATING_SINGLE = new Type$2('FLOATING SINGLE');
100140
100141         var GeometryFactory = function GeometryFactory () {
100142           this._precisionModel = new PrecisionModel();
100143           this._SRID = 0;
100144           this._coordinateSequenceFactory = GeometryFactory.getDefaultCoordinateSequenceFactory();
100145
100146           if (arguments.length === 0) ; else if (arguments.length === 1) {
100147             if (hasInterface(arguments[0], CoordinateSequenceFactory)) {
100148               this._coordinateSequenceFactory = arguments[0];
100149             } else if (arguments[0] instanceof PrecisionModel) {
100150               this._precisionModel = arguments[0];
100151             }
100152           } else if (arguments.length === 2) {
100153             this._precisionModel = arguments[0];
100154             this._SRID = arguments[1];
100155           } else if (arguments.length === 3) {
100156             this._precisionModel = arguments[0];
100157             this._SRID = arguments[1];
100158             this._coordinateSequenceFactory = arguments[2];
100159           }
100160         };
100161
100162         var staticAccessors$2 = { serialVersionUID: { configurable: true } };
100163         GeometryFactory.prototype.toGeometry = function toGeometry (envelope) {
100164           if (envelope.isNull()) {
100165             return this.createPoint(null)
100166           }
100167           if (envelope.getMinX() === envelope.getMaxX() && envelope.getMinY() === envelope.getMaxY()) {
100168             return this.createPoint(new Coordinate(envelope.getMinX(), envelope.getMinY()))
100169           }
100170           if (envelope.getMinX() === envelope.getMaxX() || envelope.getMinY() === envelope.getMaxY()) {
100171             return this.createLineString([new Coordinate(envelope.getMinX(), envelope.getMinY()), new Coordinate(envelope.getMaxX(), envelope.getMaxY())])
100172           }
100173           return this.createPolygon(this.createLinearRing([new Coordinate(envelope.getMinX(), envelope.getMinY()), new Coordinate(envelope.getMinX(), envelope.getMaxY()), new Coordinate(envelope.getMaxX(), envelope.getMaxY()), new Coordinate(envelope.getMaxX(), envelope.getMinY()), new Coordinate(envelope.getMinX(), envelope.getMinY())]), null)
100174         };
100175         GeometryFactory.prototype.createLineString = function createLineString (coordinates) {
100176           if (!coordinates) { return new LineString(this.getCoordinateSequenceFactory().create([]), this) }
100177           else if (coordinates instanceof Array) { return new LineString(this.getCoordinateSequenceFactory().create(coordinates), this) }
100178           else if (hasInterface(coordinates, CoordinateSequence)) { return new LineString(coordinates, this) }
100179         };
100180         GeometryFactory.prototype.createMultiLineString = function createMultiLineString () {
100181           if (arguments.length === 0) {
100182             return new MultiLineString(null, this)
100183           } else if (arguments.length === 1) {
100184             var lineStrings = arguments[0];
100185             return new MultiLineString(lineStrings, this)
100186           }
100187         };
100188         GeometryFactory.prototype.buildGeometry = function buildGeometry (geomList) {
100189           var geomClass = null;
100190           var isHeterogeneous = false;
100191           var hasGeometryCollection = false;
100192           for (var i = geomList.iterator(); i.hasNext();) {
100193             var geom = i.next();
100194             var partClass = geom.getClass();
100195             if (geomClass === null) {
100196               geomClass = partClass;
100197             }
100198             if (partClass !== geomClass) {
100199               isHeterogeneous = true;
100200             }
100201             if (geom.isGeometryCollectionOrDerived()) { hasGeometryCollection = true; }
100202           }
100203           if (geomClass === null) {
100204             return this.createGeometryCollection()
100205           }
100206           if (isHeterogeneous || hasGeometryCollection) {
100207             return this.createGeometryCollection(GeometryFactory.toGeometryArray(geomList))
100208           }
100209           var geom0 = geomList.iterator().next();
100210           var isCollection = geomList.size() > 1;
100211           if (isCollection) {
100212             if (geom0 instanceof Polygon) {
100213               return this.createMultiPolygon(GeometryFactory.toPolygonArray(geomList))
100214             } else if (geom0 instanceof LineString) {
100215               return this.createMultiLineString(GeometryFactory.toLineStringArray(geomList))
100216             } else if (geom0 instanceof Point$1) {
100217               return this.createMultiPoint(GeometryFactory.toPointArray(geomList))
100218             }
100219             Assert.shouldNeverReachHere('Unhandled class: ' + geom0.getClass().getName());
100220           }
100221           return geom0
100222         };
100223         GeometryFactory.prototype.createMultiPointFromCoords = function createMultiPointFromCoords (coordinates) {
100224           return this.createMultiPoint(coordinates !== null ? this.getCoordinateSequenceFactory().create(coordinates) : null)
100225         };
100226         GeometryFactory.prototype.createPoint = function createPoint () {
100227           if (arguments.length === 0) {
100228             return this.createPoint(this.getCoordinateSequenceFactory().create([]))
100229           } else if (arguments.length === 1) {
100230             if (arguments[0] instanceof Coordinate) {
100231               var coordinate = arguments[0];
100232               return this.createPoint(coordinate !== null ? this.getCoordinateSequenceFactory().create([coordinate]) : null)
100233             } else if (hasInterface(arguments[0], CoordinateSequence)) {
100234               var coordinates = arguments[0];
100235               return new Point$1(coordinates, this)
100236             }
100237           }
100238         };
100239         GeometryFactory.prototype.getCoordinateSequenceFactory = function getCoordinateSequenceFactory () {
100240           return this._coordinateSequenceFactory
100241         };
100242         GeometryFactory.prototype.createPolygon = function createPolygon () {
100243           if (arguments.length === 0) {
100244             return new Polygon(null, null, this)
100245           } else if (arguments.length === 1) {
100246             if (hasInterface(arguments[0], CoordinateSequence)) {
100247               var coordinates = arguments[0];
100248               return this.createPolygon(this.createLinearRing(coordinates))
100249             } else if (arguments[0] instanceof Array) {
100250               var coordinates$1 = arguments[0];
100251               return this.createPolygon(this.createLinearRing(coordinates$1))
100252             } else if (arguments[0] instanceof LinearRing) {
100253               var shell = arguments[0];
100254               return this.createPolygon(shell, null)
100255             }
100256           } else if (arguments.length === 2) {
100257             var shell$1 = arguments[0];
100258             var holes = arguments[1];
100259             return new Polygon(shell$1, holes, this)
100260           }
100261         };
100262         GeometryFactory.prototype.getSRID = function getSRID () {
100263           return this._SRID
100264         };
100265         GeometryFactory.prototype.createGeometryCollection = function createGeometryCollection () {
100266           if (arguments.length === 0) {
100267             return new GeometryCollection(null, this)
100268           } else if (arguments.length === 1) {
100269             var geometries = arguments[0];
100270             return new GeometryCollection(geometries, this)
100271           }
100272         };
100273         GeometryFactory.prototype.createGeometry = function createGeometry (g) {
100274           var editor = new GeometryEditor(this);
100275           return editor.edit(g, {
100276             edit: function () {
100277               if (arguments.length === 2) {
100278                 var coordSeq = arguments[0];
100279                 // const geometry = arguments[1]
100280                 return this._coordinateSequenceFactory.create(coordSeq)
100281               }
100282             }
100283           })
100284         };
100285         GeometryFactory.prototype.getPrecisionModel = function getPrecisionModel () {
100286           return this._precisionModel
100287         };
100288         GeometryFactory.prototype.createLinearRing = function createLinearRing () {
100289           if (arguments.length === 0) {
100290             return this.createLinearRing(this.getCoordinateSequenceFactory().create([]))
100291           } else if (arguments.length === 1) {
100292             if (arguments[0] instanceof Array) {
100293               var coordinates = arguments[0];
100294               return this.createLinearRing(coordinates !== null ? this.getCoordinateSequenceFactory().create(coordinates) : null)
100295             } else if (hasInterface(arguments[0], CoordinateSequence)) {
100296               var coordinates$1 = arguments[0];
100297               return new LinearRing(coordinates$1, this)
100298             }
100299           }
100300         };
100301         GeometryFactory.prototype.createMultiPolygon = function createMultiPolygon () {
100302           if (arguments.length === 0) {
100303             return new MultiPolygon(null, this)
100304           } else if (arguments.length === 1) {
100305             var polygons = arguments[0];
100306             return new MultiPolygon(polygons, this)
100307           }
100308         };
100309         GeometryFactory.prototype.createMultiPoint = function createMultiPoint () {
100310             var this$1 = this;
100311
100312           if (arguments.length === 0) {
100313             return new MultiPoint(null, this)
100314           } else if (arguments.length === 1) {
100315             if (arguments[0] instanceof Array) {
100316               var point = arguments[0];
100317               return new MultiPoint(point, this)
100318             } else if (arguments[0] instanceof Array) {
100319               var coordinates = arguments[0];
100320               return this.createMultiPoint(coordinates !== null ? this.getCoordinateSequenceFactory().create(coordinates) : null)
100321             } else if (hasInterface(arguments[0], CoordinateSequence)) {
100322               var coordinates$1 = arguments[0];
100323               if (coordinates$1 === null) {
100324                 return this.createMultiPoint(new Array(0).fill(null))
100325               }
100326               var points = new Array(coordinates$1.size()).fill(null);
100327               for (var i = 0; i < coordinates$1.size(); i++) {
100328                 var ptSeq = this$1.getCoordinateSequenceFactory().create(1, coordinates$1.getDimension());
100329                 CoordinateSequences.copy(coordinates$1, i, ptSeq, 0, 1);
100330                 points[i] = this$1.createPoint(ptSeq);
100331               }
100332               return this.createMultiPoint(points)
100333             }
100334           }
100335         };
100336         GeometryFactory.prototype.interfaces_ = function interfaces_ () {
100337           return [Serializable]
100338         };
100339         GeometryFactory.prototype.getClass = function getClass () {
100340           return GeometryFactory
100341         };
100342         GeometryFactory.toMultiPolygonArray = function toMultiPolygonArray (multiPolygons) {
100343           var multiPolygonArray = new Array(multiPolygons.size()).fill(null);
100344           return multiPolygons.toArray(multiPolygonArray)
100345         };
100346         GeometryFactory.toGeometryArray = function toGeometryArray (geometries) {
100347           if (geometries === null) { return null }
100348           var geometryArray = new Array(geometries.size()).fill(null);
100349           return geometries.toArray(geometryArray)
100350         };
100351         GeometryFactory.getDefaultCoordinateSequenceFactory = function getDefaultCoordinateSequenceFactory () {
100352           return CoordinateArraySequenceFactory.instance()
100353         };
100354         GeometryFactory.toMultiLineStringArray = function toMultiLineStringArray (multiLineStrings) {
100355           var multiLineStringArray = new Array(multiLineStrings.size()).fill(null);
100356           return multiLineStrings.toArray(multiLineStringArray)
100357         };
100358         GeometryFactory.toLineStringArray = function toLineStringArray (lineStrings) {
100359           var lineStringArray = new Array(lineStrings.size()).fill(null);
100360           return lineStrings.toArray(lineStringArray)
100361         };
100362         GeometryFactory.toMultiPointArray = function toMultiPointArray (multiPoints) {
100363           var multiPointArray = new Array(multiPoints.size()).fill(null);
100364           return multiPoints.toArray(multiPointArray)
100365         };
100366         GeometryFactory.toLinearRingArray = function toLinearRingArray (linearRings) {
100367           var linearRingArray = new Array(linearRings.size()).fill(null);
100368           return linearRings.toArray(linearRingArray)
100369         };
100370         GeometryFactory.toPointArray = function toPointArray (points) {
100371           var pointArray = new Array(points.size()).fill(null);
100372           return points.toArray(pointArray)
100373         };
100374         GeometryFactory.toPolygonArray = function toPolygonArray (polygons) {
100375           var polygonArray = new Array(polygons.size()).fill(null);
100376           return polygons.toArray(polygonArray)
100377         };
100378         GeometryFactory.createPointFromInternalCoord = function createPointFromInternalCoord (coord, exemplar) {
100379           exemplar.getPrecisionModel().makePrecise(coord);
100380           return exemplar.getFactory().createPoint(coord)
100381         };
100382         staticAccessors$2.serialVersionUID.get = function () { return -6820524753094095635 };
100383
100384         Object.defineProperties( GeometryFactory, staticAccessors$2 );
100385
100386         var geometryTypes = ['Point', 'MultiPoint', 'LineString', 'MultiLineString', 'Polygon', 'MultiPolygon'];
100387
100388         /**
100389          * Class for reading and writing Well-Known Text.Create a new parser for GeoJSON
100390          * NOTE: Adapted from OpenLayers 2.11 implementation.
100391          */
100392
100393         /**
100394          * Create a new parser for GeoJSON
100395          *
100396          * @param {GeometryFactory} geometryFactory
100397          * @return An instance of GeoJsonParser.
100398          * @constructor
100399          * @private
100400          */
100401         var GeoJSONParser = function GeoJSONParser (geometryFactory) {
100402           this.geometryFactory = geometryFactory || new GeometryFactory();
100403         };
100404         /**
100405          * Deserialize a GeoJSON object and return the Geometry or Feature(Collection) with JSTS Geometries
100406          *
100407          * @param {}
100408          *        A GeoJSON object.
100409          * @return {} A Geometry instance or object representing a Feature(Collection) with Geometry instances.
100410          * @private
100411          */
100412         GeoJSONParser.prototype.read = function read (json) {
100413           var obj;
100414           if (typeof json === 'string') {
100415             obj = JSON.parse(json);
100416           } else {
100417             obj = json;
100418           }
100419
100420           var type = obj.type;
100421
100422           if (!parse$2[type]) {
100423             throw new Error('Unknown GeoJSON type: ' + obj.type)
100424           }
100425
100426           if (geometryTypes.indexOf(type) !== -1) {
100427             return parse$2[type].apply(this, [obj.coordinates])
100428           } else if (type === 'GeometryCollection') {
100429             return parse$2[type].apply(this, [obj.geometries])
100430           }
100431
100432           // feature or feature collection
100433           return parse$2[type].apply(this, [obj])
100434         };
100435
100436         /**
100437          * Serialize a Geometry object into GeoJSON
100438          *
100439          * @param {Geometry}
100440          *        geometry A Geometry or array of Geometries.
100441          * @return {Object} A GeoJSON object represting the input Geometry/Geometries.
100442          * @private
100443          */
100444         GeoJSONParser.prototype.write = function write (geometry) {
100445           var type = geometry.getGeometryType();
100446
100447           if (!extract[type]) {
100448             throw new Error('Geometry is not supported')
100449           }
100450
100451           return extract[type].apply(this, [geometry])
100452         };
100453
100454         var parse$2 = {
100455           /**
100456            * Parse a GeoJSON Feature object
100457            *
100458            * @param {Object}
100459            *          obj Object to parse.
100460            *
100461            * @return {Object} Feature with geometry/bbox converted to JSTS Geometries.
100462            */
100463           Feature: function (obj) {
100464             var feature = {};
100465
100466             // copy features
100467             for (var key in obj) {
100468               feature[key] = obj[key];
100469             }
100470
100471             // parse geometry
100472             if (obj.geometry) {
100473               var type = obj.geometry.type;
100474               if (!parse$2[type]) {
100475                 throw new Error('Unknown GeoJSON type: ' + obj.type)
100476               }
100477               feature.geometry = this.read(obj.geometry);
100478             }
100479
100480             // bbox
100481             if (obj.bbox) {
100482               feature.bbox = parse$2.bbox.apply(this, [obj.bbox]);
100483             }
100484
100485             return feature
100486           },
100487
100488           /**
100489            * Parse a GeoJSON FeatureCollection object
100490            *
100491            * @param {Object}
100492            *          obj Object to parse.
100493            *
100494            * @return {Object} FeatureCollection with geometry/bbox converted to JSTS Geometries.
100495            */
100496           FeatureCollection: function (obj) {
100497             var this$1 = this;
100498
100499             var featureCollection = {};
100500
100501             if (obj.features) {
100502               featureCollection.features = [];
100503
100504               for (var i = 0; i < obj.features.length; ++i) {
100505                 featureCollection.features.push(this$1.read(obj.features[i]));
100506               }
100507             }
100508
100509             if (obj.bbox) {
100510               featureCollection.bbox = this.parse.bbox.apply(this, [obj.bbox]);
100511             }
100512
100513             return featureCollection
100514           },
100515
100516           /**
100517            * Convert the ordinates in an array to an array of Coordinates
100518            *
100519            * @param {Array}
100520            *          array Array with {Number}s.
100521            *
100522            * @return {Array} Array with Coordinates.
100523            */
100524           coordinates: function (array) {
100525             var coordinates = [];
100526             for (var i = 0; i < array.length; ++i) {
100527               var sub = array[i];
100528               coordinates.push(new Coordinate(sub[0], sub[1]));
100529             }
100530             return coordinates
100531           },
100532
100533           /**
100534            * Convert the bbox to a LinearRing
100535            *
100536            * @param {Array}
100537            *          array Array with [xMin, yMin, xMax, yMax].
100538            *
100539            * @return {Array} Array with Coordinates.
100540            */
100541           bbox: function (array) {
100542             return this.geometryFactory.createLinearRing([
100543               new Coordinate(array[0], array[1]),
100544               new Coordinate(array[2], array[1]),
100545               new Coordinate(array[2], array[3]),
100546               new Coordinate(array[0], array[3]),
100547               new Coordinate(array[0], array[1])
100548             ])
100549           },
100550
100551           /**
100552            * Convert an Array with ordinates to a Point
100553            *
100554            * @param {Array}
100555            *          array Array with ordinates.
100556            *
100557            * @return {Point} Point.
100558            */
100559           Point: function (array) {
100560             var coordinate = new Coordinate(array[0], array[1]);
100561             return this.geometryFactory.createPoint(coordinate)
100562           },
100563
100564           /**
100565            * Convert an Array with coordinates to a MultiPoint
100566            *
100567            * @param {Array}
100568            *          array Array with coordinates.
100569            *
100570            * @return {MultiPoint} MultiPoint.
100571            */
100572           MultiPoint: function (array) {
100573             var this$1 = this;
100574
100575             var points = [];
100576             for (var i = 0; i < array.length; ++i) {
100577               points.push(parse$2.Point.apply(this$1, [array[i]]));
100578             }
100579             return this.geometryFactory.createMultiPoint(points)
100580           },
100581
100582           /**
100583            * Convert an Array with coordinates to a LineString
100584            *
100585            * @param {Array}
100586            *          array Array with coordinates.
100587            *
100588            * @return {LineString} LineString.
100589            */
100590           LineString: function (array) {
100591             var coordinates = parse$2.coordinates.apply(this, [array]);
100592             return this.geometryFactory.createLineString(coordinates)
100593           },
100594
100595           /**
100596            * Convert an Array with coordinates to a MultiLineString
100597            *
100598            * @param {Array}
100599            *          array Array with coordinates.
100600            *
100601            * @return {MultiLineString} MultiLineString.
100602            */
100603           MultiLineString: function (array) {
100604             var this$1 = this;
100605
100606             var lineStrings = [];
100607             for (var i = 0; i < array.length; ++i) {
100608               lineStrings.push(parse$2.LineString.apply(this$1, [array[i]]));
100609             }
100610             return this.geometryFactory.createMultiLineString(lineStrings)
100611           },
100612
100613           /**
100614            * Convert an Array to a Polygon
100615            *
100616            * @param {Array}
100617            *          array Array with shell and holes.
100618            *
100619            * @return {Polygon} Polygon.
100620            */
100621           Polygon: function (array) {
100622             var this$1 = this;
100623
100624             var shellCoordinates = parse$2.coordinates.apply(this, [array[0]]);
100625             var shell = this.geometryFactory.createLinearRing(shellCoordinates);
100626             var holes = [];
100627             for (var i = 1; i < array.length; ++i) {
100628               var hole = array[i];
100629               var coordinates = parse$2.coordinates.apply(this$1, [hole]);
100630               var linearRing = this$1.geometryFactory.createLinearRing(coordinates);
100631               holes.push(linearRing);
100632             }
100633             return this.geometryFactory.createPolygon(shell, holes)
100634           },
100635
100636           /**
100637            * Convert an Array to a MultiPolygon
100638            *
100639            * @param {Array}
100640            *          array Array of arrays with shell and rings.
100641            *
100642            * @return {MultiPolygon} MultiPolygon.
100643            */
100644           MultiPolygon: function (array) {
100645             var this$1 = this;
100646
100647             var polygons = [];
100648             for (var i = 0; i < array.length; ++i) {
100649               var polygon = array[i];
100650               polygons.push(parse$2.Polygon.apply(this$1, [polygon]));
100651             }
100652             return this.geometryFactory.createMultiPolygon(polygons)
100653           },
100654
100655           /**
100656            * Convert an Array to a GeometryCollection
100657            *
100658            * @param {Array}
100659            *          array Array of GeoJSON geometries.
100660            *
100661            * @return {GeometryCollection} GeometryCollection.
100662            */
100663           GeometryCollection: function (array) {
100664             var this$1 = this;
100665
100666             var geometries = [];
100667             for (var i = 0; i < array.length; ++i) {
100668               var geometry = array[i];
100669               geometries.push(this$1.read(geometry));
100670             }
100671             return this.geometryFactory.createGeometryCollection(geometries)
100672           }
100673         };
100674
100675         var extract = {
100676           /**
100677            * Convert a Coordinate to an Array
100678            *
100679            * @param {Coordinate}
100680            *          coordinate Coordinate to convert.
100681            *
100682            * @return {Array} Array of ordinates.
100683            */
100684           coordinate: function (coordinate) {
100685             return [coordinate.x, coordinate.y]
100686           },
100687
100688           /**
100689            * Convert a Point to a GeoJSON object
100690            *
100691            * @param {Point}
100692            *          point Point to convert.
100693            *
100694            * @return {Array} Array of 2 ordinates (paired to a coordinate).
100695            */
100696           Point: function (point) {
100697             var array = extract.coordinate.apply(this, [point.getCoordinate()]);
100698             return {
100699               type: 'Point',
100700               coordinates: array
100701             }
100702           },
100703
100704           /**
100705            * Convert a MultiPoint to a GeoJSON object
100706            *
100707            * @param {MultiPoint}
100708            *          multipoint MultiPoint to convert.
100709            *
100710            * @return {Array} Array of coordinates.
100711            */
100712           MultiPoint: function (multipoint) {
100713             var this$1 = this;
100714
100715             var array = [];
100716             for (var i = 0; i < multipoint._geometries.length; ++i) {
100717               var point = multipoint._geometries[i];
100718               var geoJson = extract.Point.apply(this$1, [point]);
100719               array.push(geoJson.coordinates);
100720             }
100721             return {
100722               type: 'MultiPoint',
100723               coordinates: array
100724             }
100725           },
100726
100727           /**
100728            * Convert a LineString to a GeoJSON object
100729            *
100730            * @param {LineString}
100731            *          linestring LineString to convert.
100732            *
100733            * @return {Array} Array of coordinates.
100734            */
100735           LineString: function (linestring) {
100736             var this$1 = this;
100737
100738             var array = [];
100739             var coordinates = linestring.getCoordinates();
100740             for (var i = 0; i < coordinates.length; ++i) {
100741               var coordinate = coordinates[i];
100742               array.push(extract.coordinate.apply(this$1, [coordinate]));
100743             }
100744             return {
100745               type: 'LineString',
100746               coordinates: array
100747             }
100748           },
100749
100750           /**
100751            * Convert a MultiLineString to a GeoJSON object
100752            *
100753            * @param {MultiLineString}
100754            *          multilinestring MultiLineString to convert.
100755            *
100756            * @return {Array} Array of Array of coordinates.
100757            */
100758           MultiLineString: function (multilinestring) {
100759             var this$1 = this;
100760
100761             var array = [];
100762             for (var i = 0; i < multilinestring._geometries.length; ++i) {
100763               var linestring = multilinestring._geometries[i];
100764               var geoJson = extract.LineString.apply(this$1, [linestring]);
100765               array.push(geoJson.coordinates);
100766             }
100767             return {
100768               type: 'MultiLineString',
100769               coordinates: array
100770             }
100771           },
100772
100773           /**
100774            * Convert a Polygon to a GeoJSON object
100775            *
100776            * @param {Polygon}
100777            *          polygon Polygon to convert.
100778            *
100779            * @return {Array} Array with shell, holes.
100780            */
100781           Polygon: function (polygon) {
100782             var this$1 = this;
100783
100784             var array = [];
100785             var shellGeoJson = extract.LineString.apply(this, [polygon._shell]);
100786             array.push(shellGeoJson.coordinates);
100787             for (var i = 0; i < polygon._holes.length; ++i) {
100788               var hole = polygon._holes[i];
100789               var holeGeoJson = extract.LineString.apply(this$1, [hole]);
100790               array.push(holeGeoJson.coordinates);
100791             }
100792             return {
100793               type: 'Polygon',
100794               coordinates: array
100795             }
100796           },
100797
100798           /**
100799            * Convert a MultiPolygon to a GeoJSON object
100800            *
100801            * @param {MultiPolygon}
100802            *          multipolygon MultiPolygon to convert.
100803            *
100804            * @return {Array} Array of polygons.
100805            */
100806           MultiPolygon: function (multipolygon) {
100807             var this$1 = this;
100808
100809             var array = [];
100810             for (var i = 0; i < multipolygon._geometries.length; ++i) {
100811               var polygon = multipolygon._geometries[i];
100812               var geoJson = extract.Polygon.apply(this$1, [polygon]);
100813               array.push(geoJson.coordinates);
100814             }
100815             return {
100816               type: 'MultiPolygon',
100817               coordinates: array
100818             }
100819           },
100820
100821           /**
100822            * Convert a GeometryCollection to a GeoJSON object
100823            *
100824            * @param {GeometryCollection}
100825            *          collection GeometryCollection to convert.
100826            *
100827            * @return {Array} Array of geometries.
100828            */
100829           GeometryCollection: function (collection) {
100830             var this$1 = this;
100831
100832             var array = [];
100833             for (var i = 0; i < collection._geometries.length; ++i) {
100834               var geometry = collection._geometries[i];
100835               var type = geometry.getGeometryType();
100836               array.push(extract[type].apply(this$1, [geometry]));
100837             }
100838             return {
100839               type: 'GeometryCollection',
100840               geometries: array
100841             }
100842           }
100843         };
100844
100845         /**
100846          * Converts a geometry in GeoJSON to a {@link Geometry}.
100847          */
100848
100849         /**
100850          * A <code>GeoJSONReader</code> is parameterized by a <code>GeometryFactory</code>,
100851          * to allow it to create <code>Geometry</code> objects of the appropriate
100852          * implementation. In particular, the <code>GeometryFactory</code> determines
100853          * the <code>PrecisionModel</code> and <code>SRID</code> that is used.
100854          *
100855          * @param {GeometryFactory} geometryFactory
100856          * @constructor
100857          */
100858         var GeoJSONReader = function GeoJSONReader (geometryFactory) {
100859           this.geometryFactory = geometryFactory || new GeometryFactory();
100860           this.precisionModel = this.geometryFactory.getPrecisionModel();
100861           this.parser = new GeoJSONParser(this.geometryFactory);
100862         };
100863         /**
100864          * Reads a GeoJSON representation of a {@link Geometry}
100865          *
100866          * Will also parse GeoJSON Features/FeatureCollections as custom objects.
100867          *
100868          * @param {Object|String} geoJson a GeoJSON Object or String.
100869          * @return {Geometry|Object} a <code>Geometry or Feature/FeatureCollection representation.</code>
100870          * @memberof GeoJSONReader
100871          */
100872         GeoJSONReader.prototype.read = function read (geoJson) {
100873           var geometry = this.parser.read(geoJson);
100874
100875           if (this.precisionModel.getType() === PrecisionModel.FIXED) {
100876             this.reducePrecision(geometry);
100877           }
100878
100879           return geometry
100880         };
100881
100882         // NOTE: this is a hack
100883         GeoJSONReader.prototype.reducePrecision = function reducePrecision (geometry) {
100884             var this$1 = this;
100885
100886           var i, len;
100887
100888           if (geometry.coordinate) {
100889             this.precisionModel.makePrecise(geometry.coordinate);
100890           } else if (geometry.points) {
100891             for (i = 0, len = geometry.points.length; i < len; i++) {
100892               this$1.precisionModel.makePrecise(geometry.points[i]);
100893             }
100894           } else if (geometry.geometries) {
100895             for (i = 0, len = geometry.geometries.length; i < len; i++) {
100896               this$1.reducePrecision(geometry.geometries[i]);
100897             }
100898           }
100899         };
100900
100901         /**
100902          * @module GeoJSONWriter
100903          */
100904
100905         /**
100906          * Writes the GeoJSON representation of a {@link Geometry}. The
100907          * The GeoJSON format is defined <A
100908          * HREF="http://geojson.org/geojson-spec.html">here</A>.
100909          */
100910
100911         /**
100912          * The <code>GeoJSONWriter</code> outputs coordinates rounded to the precision
100913          * model. Only the maximum number of decimal places necessary to represent the
100914          * ordinates to the required precision will be output.
100915          *
100916          * @param {GeometryFactory} geometryFactory
100917          * @constructor
100918          */
100919         var GeoJSONWriter = function GeoJSONWriter () {
100920           this.parser = new GeoJSONParser(this.geometryFactory);
100921         };
100922         /**
100923          * Converts a <code>Geometry</code> to its GeoJSON representation.
100924          *
100925          * @param {Geometry}
100926          *        geometry a <code>Geometry</code> to process.
100927          * @return {Object} The GeoJSON representation of the Geometry.
100928          * @memberof GeoJSONWriter
100929          */
100930         GeoJSONWriter.prototype.write = function write (geometry) {
100931           return this.parser.write(geometry)
100932         };
100933
100934         /* eslint-disable no-undef */
100935
100936         // io
100937
100938         var Position = function Position () {};
100939
100940         var staticAccessors$20 = { ON: { configurable: true },LEFT: { configurable: true },RIGHT: { configurable: true } };
100941
100942         Position.prototype.interfaces_ = function interfaces_ () {
100943           return []
100944         };
100945         Position.prototype.getClass = function getClass () {
100946           return Position
100947         };
100948         Position.opposite = function opposite (position) {
100949           if (position === Position.LEFT) { return Position.RIGHT }
100950           if (position === Position.RIGHT) { return Position.LEFT }
100951           return position
100952         };
100953         staticAccessors$20.ON.get = function () { return 0 };
100954         staticAccessors$20.LEFT.get = function () { return 1 };
100955         staticAccessors$20.RIGHT.get = function () { return 2 };
100956
100957         Object.defineProperties( Position, staticAccessors$20 );
100958
100959         /**
100960          * @param {string=} message Optional message
100961          * @extends {Error}
100962          * @constructor
100963          * @private
100964          */
100965         function EmptyStackException (message) {
100966           this.message = message || '';
100967         }
100968         EmptyStackException.prototype = new Error();
100969
100970         /**
100971          * @type {string}
100972          */
100973         EmptyStackException.prototype.name = 'EmptyStackException';
100974
100975         /**
100976          * @see http://download.oracle.com/javase/6/docs/api/java/util/Stack.html
100977          *
100978          * @extends {List}
100979          * @constructor
100980          * @private
100981          */
100982         function Stack () {
100983           /**
100984            * @type {Array}
100985            * @private
100986            */
100987           this.array_ = [];
100988         }
100989         Stack.prototype = new List();
100990
100991         /**
100992          * @override
100993          */
100994         Stack.prototype.add = function (e) {
100995           this.array_.push(e);
100996           return true
100997         };
100998
100999         /**
101000          * @override
101001          */
101002         Stack.prototype.get = function (index) {
101003           if (index < 0 || index >= this.size()) {
101004             throw new Error()
101005           }
101006
101007           return this.array_[index]
101008         };
101009
101010         /**
101011          * Pushes an item onto the top of this stack.
101012          * @param {Object} e
101013          * @return {Object}
101014          */
101015         Stack.prototype.push = function (e) {
101016           this.array_.push(e);
101017           return e
101018         };
101019
101020         /**
101021          * Pushes an item onto the top of this stack.
101022          * @param {Object} e
101023          * @return {Object}
101024          */
101025         Stack.prototype.pop = function (e) {
101026           if (this.array_.length === 0) {
101027             throw new EmptyStackException()
101028           }
101029
101030           return this.array_.pop()
101031         };
101032
101033         /**
101034          * Looks at the object at the top of this stack without removing it from the
101035          * stack.
101036          * @return {Object}
101037          */
101038         Stack.prototype.peek = function () {
101039           if (this.array_.length === 0) {
101040             throw new EmptyStackException()
101041           }
101042
101043           return this.array_[this.array_.length - 1]
101044         };
101045
101046         /**
101047          * Tests if this stack is empty.
101048          * @return {boolean} true if and only if this stack contains no items; false
101049          *         otherwise.
101050          */
101051         Stack.prototype.empty = function () {
101052           if (this.array_.length === 0) {
101053             return true
101054           } else {
101055             return false
101056           }
101057         };
101058
101059         /**
101060          * @return {boolean}
101061          */
101062         Stack.prototype.isEmpty = function () {
101063           return this.empty()
101064         };
101065
101066         /**
101067          * Returns the 1-based position where an object is on this stack. If the object
101068          * o occurs as an item in this stack, this method returns the distance from the
101069          * top of the stack of the occurrence nearest the top of the stack; the topmost
101070          * item on the stack is considered to be at distance 1. The equals method is
101071          * used to compare o to the items in this stack.
101072          *
101073          * NOTE: does not currently actually use equals. (=== is used)
101074          *
101075          * @param {Object} o
101076          * @return {number} the 1-based position from the top of the stack where the
101077          *         object is located; the return value -1 indicates that the object is
101078          *         not on the stack.
101079          */
101080         Stack.prototype.search = function (o) {
101081           return this.array_.indexOf(o)
101082         };
101083
101084         /**
101085          * @return {number}
101086          * @export
101087          */
101088         Stack.prototype.size = function () {
101089           return this.array_.length
101090         };
101091
101092         /**
101093          * @return {Array}
101094          */
101095         Stack.prototype.toArray = function () {
101096           var this$1 = this;
101097
101098           var array = [];
101099
101100           for (var i = 0, len = this.array_.length; i < len; i++) {
101101             array.push(this$1.array_[i]);
101102           }
101103
101104           return array
101105         };
101106
101107         var RightmostEdgeFinder = function RightmostEdgeFinder () {
101108           this._minIndex = -1;
101109           this._minCoord = null;
101110           this._minDe = null;
101111           this._orientedDe = null;
101112         };
101113         RightmostEdgeFinder.prototype.getCoordinate = function getCoordinate () {
101114           return this._minCoord
101115         };
101116         RightmostEdgeFinder.prototype.getRightmostSide = function getRightmostSide (de, index) {
101117           var side = this.getRightmostSideOfSegment(de, index);
101118           if (side < 0) { side = this.getRightmostSideOfSegment(de, index - 1); }
101119           if (side < 0) {
101120             this._minCoord = null;
101121             this.checkForRightmostCoordinate(de);
101122           }
101123           return side
101124         };
101125         RightmostEdgeFinder.prototype.findRightmostEdgeAtVertex = function findRightmostEdgeAtVertex () {
101126           var pts = this._minDe.getEdge().getCoordinates();
101127           Assert.isTrue(this._minIndex > 0 && this._minIndex < pts.length, 'rightmost point expected to be interior vertex of edge');
101128           var pPrev = pts[this._minIndex - 1];
101129           var pNext = pts[this._minIndex + 1];
101130           var orientation = CGAlgorithms.computeOrientation(this._minCoord, pNext, pPrev);
101131           var usePrev = false;
101132           if (pPrev.y < this._minCoord.y && pNext.y < this._minCoord.y && orientation === CGAlgorithms.COUNTERCLOCKWISE) {
101133             usePrev = true;
101134           } else if (pPrev.y > this._minCoord.y && pNext.y > this._minCoord.y && orientation === CGAlgorithms.CLOCKWISE) {
101135             usePrev = true;
101136           }
101137           if (usePrev) {
101138             this._minIndex = this._minIndex - 1;
101139           }
101140         };
101141         RightmostEdgeFinder.prototype.getRightmostSideOfSegment = function getRightmostSideOfSegment (de, i) {
101142           var e = de.getEdge();
101143           var coord = e.getCoordinates();
101144           if (i < 0 || i + 1 >= coord.length) { return -1 }
101145           if (coord[i].y === coord[i + 1].y) { return -1 }
101146           var pos = Position.LEFT;
101147           if (coord[i].y < coord[i + 1].y) { pos = Position.RIGHT; }
101148           return pos
101149         };
101150         RightmostEdgeFinder.prototype.getEdge = function getEdge () {
101151           return this._orientedDe
101152         };
101153         RightmostEdgeFinder.prototype.checkForRightmostCoordinate = function checkForRightmostCoordinate (de) {
101154             var this$1 = this;
101155
101156           var coord = de.getEdge().getCoordinates();
101157           for (var i = 0; i < coord.length - 1; i++) {
101158             if (this$1._minCoord === null || coord[i].x > this$1._minCoord.x) {
101159               this$1._minDe = de;
101160               this$1._minIndex = i;
101161               this$1._minCoord = coord[i];
101162             }
101163           }
101164         };
101165         RightmostEdgeFinder.prototype.findRightmostEdgeAtNode = function findRightmostEdgeAtNode () {
101166           var node = this._minDe.getNode();
101167           var star = node.getEdges();
101168           this._minDe = star.getRightmostEdge();
101169           if (!this._minDe.isForward()) {
101170             this._minDe = this._minDe.getSym();
101171             this._minIndex = this._minDe.getEdge().getCoordinates().length - 1;
101172           }
101173         };
101174         RightmostEdgeFinder.prototype.findEdge = function findEdge (dirEdgeList) {
101175             var this$1 = this;
101176
101177           for (var i = dirEdgeList.iterator(); i.hasNext();) {
101178             var de = i.next();
101179             if (!de.isForward()) { continue }
101180             this$1.checkForRightmostCoordinate(de);
101181           }
101182           Assert.isTrue(this._minIndex !== 0 || this._minCoord.equals(this._minDe.getCoordinate()), 'inconsistency in rightmost processing');
101183           if (this._minIndex === 0) {
101184             this.findRightmostEdgeAtNode();
101185           } else {
101186             this.findRightmostEdgeAtVertex();
101187           }
101188           this._orientedDe = this._minDe;
101189           var rightmostSide = this.getRightmostSide(this._minDe, this._minIndex);
101190           if (rightmostSide === Position.LEFT) {
101191             this._orientedDe = this._minDe.getSym();
101192           }
101193         };
101194         RightmostEdgeFinder.prototype.interfaces_ = function interfaces_ () {
101195           return []
101196         };
101197         RightmostEdgeFinder.prototype.getClass = function getClass () {
101198           return RightmostEdgeFinder
101199         };
101200
101201         var TopologyException = (function (RuntimeException$$1) {
101202           function TopologyException (msg, pt) {
101203             RuntimeException$$1.call(this, TopologyException.msgWithCoord(msg, pt));
101204             this.pt = pt ? new Coordinate(pt) : null;
101205             this.name = 'TopologyException';
101206           }
101207
101208           if ( RuntimeException$$1 ) TopologyException.__proto__ = RuntimeException$$1;
101209           TopologyException.prototype = Object.create( RuntimeException$$1 && RuntimeException$$1.prototype );
101210           TopologyException.prototype.constructor = TopologyException;
101211           TopologyException.prototype.getCoordinate = function getCoordinate () {
101212             return this.pt
101213           };
101214           TopologyException.prototype.interfaces_ = function interfaces_ () {
101215             return []
101216           };
101217           TopologyException.prototype.getClass = function getClass () {
101218             return TopologyException
101219           };
101220           TopologyException.msgWithCoord = function msgWithCoord (msg, pt) {
101221             if (!pt) { return msg + ' [ ' + pt + ' ]' }
101222             return msg
101223           };
101224
101225           return TopologyException;
101226         }(RuntimeException));
101227
101228         var LinkedList = function LinkedList () {
101229           this.array_ = [];
101230         };
101231         LinkedList.prototype.addLast = function addLast (e) {
101232           this.array_.push(e);
101233         };
101234         LinkedList.prototype.removeFirst = function removeFirst () {
101235           return this.array_.shift()
101236         };
101237         LinkedList.prototype.isEmpty = function isEmpty () {
101238           return this.array_.length === 0
101239         };
101240
101241         var BufferSubgraph = function BufferSubgraph () {
101242           this._finder = null;
101243           this._dirEdgeList = new ArrayList();
101244           this._nodes = new ArrayList();
101245           this._rightMostCoord = null;
101246           this._env = null;
101247           this._finder = new RightmostEdgeFinder();
101248         };
101249         BufferSubgraph.prototype.clearVisitedEdges = function clearVisitedEdges () {
101250           for (var it = this._dirEdgeList.iterator(); it.hasNext();) {
101251             var de = it.next();
101252             de.setVisited(false);
101253           }
101254         };
101255         BufferSubgraph.prototype.getRightmostCoordinate = function getRightmostCoordinate () {
101256           return this._rightMostCoord
101257         };
101258         BufferSubgraph.prototype.computeNodeDepth = function computeNodeDepth (n) {
101259             var this$1 = this;
101260
101261           var startEdge = null;
101262           for (var i = n.getEdges().iterator(); i.hasNext();) {
101263             var de = i.next();
101264             if (de.isVisited() || de.getSym().isVisited()) {
101265               startEdge = de;
101266               break
101267             }
101268           }
101269           if (startEdge === null) { throw new TopologyException('unable to find edge to compute depths at ' + n.getCoordinate()) }
101270           n.getEdges().computeDepths(startEdge);
101271           for (var i$1 = n.getEdges().iterator(); i$1.hasNext();) {
101272             var de$1 = i$1.next();
101273             de$1.setVisited(true);
101274             this$1.copySymDepths(de$1);
101275           }
101276         };
101277         BufferSubgraph.prototype.computeDepth = function computeDepth (outsideDepth) {
101278           this.clearVisitedEdges();
101279           var de = this._finder.getEdge();
101280           // const n = de.getNode()
101281           // const label = de.getLabel()
101282           de.setEdgeDepths(Position.RIGHT, outsideDepth);
101283           this.copySymDepths(de);
101284           this.computeDepths(de);
101285         };
101286         BufferSubgraph.prototype.create = function create (node) {
101287           this.addReachable(node);
101288           this._finder.findEdge(this._dirEdgeList);
101289           this._rightMostCoord = this._finder.getCoordinate();
101290         };
101291         BufferSubgraph.prototype.findResultEdges = function findResultEdges () {
101292           for (var it = this._dirEdgeList.iterator(); it.hasNext();) {
101293             var de = it.next();
101294             if (de.getDepth(Position.RIGHT) >= 1 && de.getDepth(Position.LEFT) <= 0 && !de.isInteriorAreaEdge()) {
101295               de.setInResult(true);
101296             }
101297           }
101298         };
101299         BufferSubgraph.prototype.computeDepths = function computeDepths (startEdge) {
101300             var this$1 = this;
101301
101302           var nodesVisited = new HashSet();
101303           var nodeQueue = new LinkedList();
101304           var startNode = startEdge.getNode();
101305           nodeQueue.addLast(startNode);
101306           nodesVisited.add(startNode);
101307           startEdge.setVisited(true);
101308           while (!nodeQueue.isEmpty()) {
101309             var n = nodeQueue.removeFirst();
101310             nodesVisited.add(n);
101311             this$1.computeNodeDepth(n);
101312             for (var i = n.getEdges().iterator(); i.hasNext();) {
101313               var de = i.next();
101314               var sym = de.getSym();
101315               if (sym.isVisited()) { continue }
101316               var adjNode = sym.getNode();
101317               if (!nodesVisited.contains(adjNode)) {
101318                 nodeQueue.addLast(adjNode);
101319                 nodesVisited.add(adjNode);
101320               }
101321             }
101322           }
101323         };
101324         BufferSubgraph.prototype.compareTo = function compareTo (o) {
101325           var graph = o;
101326           if (this._rightMostCoord.x < graph._rightMostCoord.x) {
101327             return -1
101328           }
101329           if (this._rightMostCoord.x > graph._rightMostCoord.x) {
101330             return 1
101331           }
101332           return 0
101333         };
101334         BufferSubgraph.prototype.getEnvelope = function getEnvelope () {
101335           if (this._env === null) {
101336             var edgeEnv = new Envelope();
101337             for (var it = this._dirEdgeList.iterator(); it.hasNext();) {
101338               var dirEdge = it.next();
101339               var pts = dirEdge.getEdge().getCoordinates();
101340               for (var i = 0; i < pts.length - 1; i++) {
101341                 edgeEnv.expandToInclude(pts[i]);
101342               }
101343             }
101344             this._env = edgeEnv;
101345           }
101346           return this._env
101347         };
101348         BufferSubgraph.prototype.addReachable = function addReachable (startNode) {
101349             var this$1 = this;
101350
101351           var nodeStack = new Stack();
101352           nodeStack.add(startNode);
101353           while (!nodeStack.empty()) {
101354             var node = nodeStack.pop();
101355             this$1.add(node, nodeStack);
101356           }
101357         };
101358         BufferSubgraph.prototype.copySymDepths = function copySymDepths (de) {
101359           var sym = de.getSym();
101360           sym.setDepth(Position.LEFT, de.getDepth(Position.RIGHT));
101361           sym.setDepth(Position.RIGHT, de.getDepth(Position.LEFT));
101362         };
101363         BufferSubgraph.prototype.add = function add (node, nodeStack) {
101364             var this$1 = this;
101365
101366           node.setVisited(true);
101367           this._nodes.add(node);
101368           for (var i = node.getEdges().iterator(); i.hasNext();) {
101369             var de = i.next();
101370             this$1._dirEdgeList.add(de);
101371             var sym = de.getSym();
101372             var symNode = sym.getNode();
101373             if (!symNode.isVisited()) { nodeStack.push(symNode); }
101374           }
101375         };
101376         BufferSubgraph.prototype.getNodes = function getNodes () {
101377           return this._nodes
101378         };
101379         BufferSubgraph.prototype.getDirectedEdges = function getDirectedEdges () {
101380           return this._dirEdgeList
101381         };
101382         BufferSubgraph.prototype.interfaces_ = function interfaces_ () {
101383           return [Comparable]
101384         };
101385         BufferSubgraph.prototype.getClass = function getClass () {
101386           return BufferSubgraph
101387         };
101388
101389         var TopologyLocation = function TopologyLocation () {
101390           var this$1 = this;
101391
101392           this.location = null;
101393           if (arguments.length === 1) {
101394             if (arguments[0] instanceof Array) {
101395               var location = arguments[0];
101396               this.init(location.length);
101397             } else if (Number.isInteger(arguments[0])) {
101398               var on = arguments[0];
101399               this.init(1);
101400               this.location[Position.ON] = on;
101401             } else if (arguments[0] instanceof TopologyLocation) {
101402               var gl = arguments[0];
101403               this.init(gl.location.length);
101404               if (gl !== null) {
101405                 for (var i = 0; i < this.location.length; i++) {
101406                   this$1.location[i] = gl.location[i];
101407                 }
101408               }
101409             }
101410           } else if (arguments.length === 3) {
101411             var on$1 = arguments[0];
101412             var left = arguments[1];
101413             var right = arguments[2];
101414             this.init(3);
101415             this.location[Position.ON] = on$1;
101416             this.location[Position.LEFT] = left;
101417             this.location[Position.RIGHT] = right;
101418           }
101419         };
101420         TopologyLocation.prototype.setAllLocations = function setAllLocations (locValue) {
101421             var this$1 = this;
101422
101423           for (var i = 0; i < this.location.length; i++) {
101424             this$1.location[i] = locValue;
101425           }
101426         };
101427         TopologyLocation.prototype.isNull = function isNull () {
101428             var this$1 = this;
101429
101430           for (var i = 0; i < this.location.length; i++) {
101431             if (this$1.location[i] !== Location.NONE) { return false }
101432           }
101433           return true
101434         };
101435         TopologyLocation.prototype.setAllLocationsIfNull = function setAllLocationsIfNull (locValue) {
101436             var this$1 = this;
101437
101438           for (var i = 0; i < this.location.length; i++) {
101439             if (this$1.location[i] === Location.NONE) { this$1.location[i] = locValue; }
101440           }
101441         };
101442         TopologyLocation.prototype.isLine = function isLine () {
101443           return this.location.length === 1
101444         };
101445         TopologyLocation.prototype.merge = function merge (gl) {
101446             var this$1 = this;
101447
101448           if (gl.location.length > this.location.length) {
101449             var newLoc = new Array(3).fill(null);
101450             newLoc[Position.ON] = this.location[Position.ON];
101451             newLoc[Position.LEFT] = Location.NONE;
101452             newLoc[Position.RIGHT] = Location.NONE;
101453             this.location = newLoc;
101454           }
101455           for (var i = 0; i < this.location.length; i++) {
101456             if (this$1.location[i] === Location.NONE && i < gl.location.length) { this$1.location[i] = gl.location[i]; }
101457           }
101458         };
101459         TopologyLocation.prototype.getLocations = function getLocations () {
101460           return this.location
101461         };
101462         TopologyLocation.prototype.flip = function flip () {
101463           if (this.location.length <= 1) { return null }
101464           var temp = this.location[Position.LEFT];
101465           this.location[Position.LEFT] = this.location[Position.RIGHT];
101466           this.location[Position.RIGHT] = temp;
101467         };
101468         TopologyLocation.prototype.toString = function toString () {
101469           var buf = new StringBuffer();
101470           if (this.location.length > 1) { buf.append(Location.toLocationSymbol(this.location[Position.LEFT])); }
101471           buf.append(Location.toLocationSymbol(this.location[Position.ON]));
101472           if (this.location.length > 1) { buf.append(Location.toLocationSymbol(this.location[Position.RIGHT])); }
101473           return buf.toString()
101474         };
101475         TopologyLocation.prototype.setLocations = function setLocations (on, left, right) {
101476           this.location[Position.ON] = on;
101477           this.location[Position.LEFT] = left;
101478           this.location[Position.RIGHT] = right;
101479         };
101480         TopologyLocation.prototype.get = function get (posIndex) {
101481           if (posIndex < this.location.length) { return this.location[posIndex] }
101482           return Location.NONE
101483         };
101484         TopologyLocation.prototype.isArea = function isArea () {
101485           return this.location.length > 1
101486         };
101487         TopologyLocation.prototype.isAnyNull = function isAnyNull () {
101488             var this$1 = this;
101489
101490           for (var i = 0; i < this.location.length; i++) {
101491             if (this$1.location[i] === Location.NONE) { return true }
101492           }
101493           return false
101494         };
101495         TopologyLocation.prototype.setLocation = function setLocation () {
101496           if (arguments.length === 1) {
101497             var locValue = arguments[0];
101498             this.setLocation(Position.ON, locValue);
101499           } else if (arguments.length === 2) {
101500             var locIndex = arguments[0];
101501             var locValue$1 = arguments[1];
101502             this.location[locIndex] = locValue$1;
101503           }
101504         };
101505         TopologyLocation.prototype.init = function init (size) {
101506           this.location = new Array(size).fill(null);
101507           this.setAllLocations(Location.NONE);
101508         };
101509         TopologyLocation.prototype.isEqualOnSide = function isEqualOnSide (le, locIndex) {
101510           return this.location[locIndex] === le.location[locIndex]
101511         };
101512         TopologyLocation.prototype.allPositionsEqual = function allPositionsEqual (loc) {
101513             var this$1 = this;
101514
101515           for (var i = 0; i < this.location.length; i++) {
101516             if (this$1.location[i] !== loc) { return false }
101517           }
101518           return true
101519         };
101520         TopologyLocation.prototype.interfaces_ = function interfaces_ () {
101521           return []
101522         };
101523         TopologyLocation.prototype.getClass = function getClass () {
101524           return TopologyLocation
101525         };
101526
101527         var Label = function Label () {
101528           this.elt = new Array(2).fill(null);
101529           if (arguments.length === 1) {
101530             if (Number.isInteger(arguments[0])) {
101531               var onLoc = arguments[0];
101532               this.elt[0] = new TopologyLocation(onLoc);
101533               this.elt[1] = new TopologyLocation(onLoc);
101534             } else if (arguments[0] instanceof Label) {
101535               var lbl = arguments[0];
101536               this.elt[0] = new TopologyLocation(lbl.elt[0]);
101537               this.elt[1] = new TopologyLocation(lbl.elt[1]);
101538             }
101539           } else if (arguments.length === 2) {
101540             var geomIndex = arguments[0];
101541             var onLoc$1 = arguments[1];
101542             this.elt[0] = new TopologyLocation(Location.NONE);
101543             this.elt[1] = new TopologyLocation(Location.NONE);
101544             this.elt[geomIndex].setLocation(onLoc$1);
101545           } else if (arguments.length === 3) {
101546             var onLoc$2 = arguments[0];
101547             var leftLoc = arguments[1];
101548             var rightLoc = arguments[2];
101549             this.elt[0] = new TopologyLocation(onLoc$2, leftLoc, rightLoc);
101550             this.elt[1] = new TopologyLocation(onLoc$2, leftLoc, rightLoc);
101551           } else if (arguments.length === 4) {
101552             var geomIndex$1 = arguments[0];
101553             var onLoc$3 = arguments[1];
101554             var leftLoc$1 = arguments[2];
101555             var rightLoc$1 = arguments[3];
101556             this.elt[0] = new TopologyLocation(Location.NONE, Location.NONE, Location.NONE);
101557             this.elt[1] = new TopologyLocation(Location.NONE, Location.NONE, Location.NONE);
101558             this.elt[geomIndex$1].setLocations(onLoc$3, leftLoc$1, rightLoc$1);
101559           }
101560         };
101561         Label.prototype.getGeometryCount = function getGeometryCount () {
101562           var count = 0;
101563           if (!this.elt[0].isNull()) { count++; }
101564           if (!this.elt[1].isNull()) { count++; }
101565           return count
101566         };
101567         Label.prototype.setAllLocations = function setAllLocations (geomIndex, location) {
101568           this.elt[geomIndex].setAllLocations(location);
101569         };
101570         Label.prototype.isNull = function isNull (geomIndex) {
101571           return this.elt[geomIndex].isNull()
101572         };
101573         Label.prototype.setAllLocationsIfNull = function setAllLocationsIfNull () {
101574           if (arguments.length === 1) {
101575             var location = arguments[0];
101576             this.setAllLocationsIfNull(0, location);
101577             this.setAllLocationsIfNull(1, location);
101578           } else if (arguments.length === 2) {
101579             var geomIndex = arguments[0];
101580             var location$1 = arguments[1];
101581             this.elt[geomIndex].setAllLocationsIfNull(location$1);
101582           }
101583         };
101584         Label.prototype.isLine = function isLine (geomIndex) {
101585           return this.elt[geomIndex].isLine()
101586         };
101587         Label.prototype.merge = function merge (lbl) {
101588             var this$1 = this;
101589
101590           for (var i = 0; i < 2; i++) {
101591             if (this$1.elt[i] === null && lbl.elt[i] !== null) {
101592               this$1.elt[i] = new TopologyLocation(lbl.elt[i]);
101593             } else {
101594               this$1.elt[i].merge(lbl.elt[i]);
101595             }
101596           }
101597         };
101598         Label.prototype.flip = function flip () {
101599           this.elt[0].flip();
101600           this.elt[1].flip();
101601         };
101602         Label.prototype.getLocation = function getLocation () {
101603           if (arguments.length === 1) {
101604             var geomIndex = arguments[0];
101605             return this.elt[geomIndex].get(Position.ON)
101606           } else if (arguments.length === 2) {
101607             var geomIndex$1 = arguments[0];
101608             var posIndex = arguments[1];
101609             return this.elt[geomIndex$1].get(posIndex)
101610           }
101611         };
101612         Label.prototype.toString = function toString () {
101613           var buf = new StringBuffer();
101614           if (this.elt[0] !== null) {
101615             buf.append('A:');
101616             buf.append(this.elt[0].toString());
101617           }
101618           if (this.elt[1] !== null) {
101619             buf.append(' B:');
101620             buf.append(this.elt[1].toString());
101621           }
101622           return buf.toString()
101623         };
101624         Label.prototype.isArea = function isArea () {
101625           if (arguments.length === 0) {
101626             return this.elt[0].isArea() || this.elt[1].isArea()
101627           } else if (arguments.length === 1) {
101628             var geomIndex = arguments[0];
101629             return this.elt[geomIndex].isArea()
101630           }
101631         };
101632         Label.prototype.isAnyNull = function isAnyNull (geomIndex) {
101633           return this.elt[geomIndex].isAnyNull()
101634         };
101635         Label.prototype.setLocation = function setLocation () {
101636           if (arguments.length === 2) {
101637             var geomIndex = arguments[0];
101638             var location = arguments[1];
101639             this.elt[geomIndex].setLocation(Position.ON, location);
101640           } else if (arguments.length === 3) {
101641             var geomIndex$1 = arguments[0];
101642             var posIndex = arguments[1];
101643             var location$1 = arguments[2];
101644             this.elt[geomIndex$1].setLocation(posIndex, location$1);
101645           }
101646         };
101647         Label.prototype.isEqualOnSide = function isEqualOnSide (lbl, side) {
101648           return this.elt[0].isEqualOnSide(lbl.elt[0], side) && this.elt[1].isEqualOnSide(lbl.elt[1], side)
101649         };
101650         Label.prototype.allPositionsEqual = function allPositionsEqual (geomIndex, loc) {
101651           return this.elt[geomIndex].allPositionsEqual(loc)
101652         };
101653         Label.prototype.toLine = function toLine (geomIndex) {
101654           if (this.elt[geomIndex].isArea()) { this.elt[geomIndex] = new TopologyLocation(this.elt[geomIndex].location[0]); }
101655         };
101656         Label.prototype.interfaces_ = function interfaces_ () {
101657           return []
101658         };
101659         Label.prototype.getClass = function getClass () {
101660           return Label
101661         };
101662         Label.toLineLabel = function toLineLabel (label) {
101663           var lineLabel = new Label(Location.NONE);
101664           for (var i = 0; i < 2; i++) {
101665             lineLabel.setLocation(i, label.getLocation(i));
101666           }
101667           return lineLabel
101668         };
101669
101670         var EdgeRing = function EdgeRing () {
101671           this._startDe = null;
101672           this._maxNodeDegree = -1;
101673           this._edges = new ArrayList();
101674           this._pts = new ArrayList();
101675           this._label = new Label(Location.NONE);
101676           this._ring = null;
101677           this._isHole = null;
101678           this._shell = null;
101679           this._holes = new ArrayList();
101680           this._geometryFactory = null;
101681           var start = arguments[0];
101682           var geometryFactory = arguments[1];
101683           this._geometryFactory = geometryFactory;
101684           this.computePoints(start);
101685           this.computeRing();
101686         };
101687         EdgeRing.prototype.computeRing = function computeRing () {
101688             var this$1 = this;
101689
101690           if (this._ring !== null) { return null }
101691           var coord = new Array(this._pts.size()).fill(null);
101692           for (var i = 0; i < this._pts.size(); i++) {
101693             coord[i] = this$1._pts.get(i);
101694           }
101695           this._ring = this._geometryFactory.createLinearRing(coord);
101696           this._isHole = CGAlgorithms.isCCW(this._ring.getCoordinates());
101697         };
101698         EdgeRing.prototype.isIsolated = function isIsolated () {
101699           return this._label.getGeometryCount() === 1
101700         };
101701         EdgeRing.prototype.computePoints = function computePoints (start) {
101702             var this$1 = this;
101703
101704           this._startDe = start;
101705           var de = start;
101706           var isFirstEdge = true;
101707           do {
101708             if (de === null) { throw new TopologyException('Found null DirectedEdge') }
101709             if (de.getEdgeRing() === this$1) { throw new TopologyException('Directed Edge visited twice during ring-building at ' + de.getCoordinate()) }
101710             this$1._edges.add(de);
101711             var label = de.getLabel();
101712             Assert.isTrue(label.isArea());
101713             this$1.mergeLabel(label);
101714             this$1.addPoints(de.getEdge(), de.isForward(), isFirstEdge);
101715             isFirstEdge = false;
101716             this$1.setEdgeRing(de, this$1);
101717             de = this$1.getNext(de);
101718           } while (de !== this._startDe)
101719         };
101720         EdgeRing.prototype.getLinearRing = function getLinearRing () {
101721           return this._ring
101722         };
101723         EdgeRing.prototype.getCoordinate = function getCoordinate (i) {
101724           return this._pts.get(i)
101725         };
101726         EdgeRing.prototype.computeMaxNodeDegree = function computeMaxNodeDegree () {
101727             var this$1 = this;
101728
101729           this._maxNodeDegree = 0;
101730           var de = this._startDe;
101731           do {
101732             var node = de.getNode();
101733             var degree = node.getEdges().getOutgoingDegree(this$1);
101734             if (degree > this$1._maxNodeDegree) { this$1._maxNodeDegree = degree; }
101735             de = this$1.getNext(de);
101736           } while (de !== this._startDe)
101737           this._maxNodeDegree *= 2;
101738         };
101739         EdgeRing.prototype.addPoints = function addPoints (edge, isForward, isFirstEdge) {
101740             var this$1 = this;
101741
101742           var edgePts = edge.getCoordinates();
101743           if (isForward) {
101744             var startIndex = 1;
101745             if (isFirstEdge) { startIndex = 0; }
101746             for (var i = startIndex; i < edgePts.length; i++) {
101747               this$1._pts.add(edgePts[i]);
101748             }
101749           } else {
101750             var startIndex$1 = edgePts.length - 2;
101751             if (isFirstEdge) { startIndex$1 = edgePts.length - 1; }
101752             for (var i$1 = startIndex$1; i$1 >= 0; i$1--) {
101753               this$1._pts.add(edgePts[i$1]);
101754             }
101755           }
101756         };
101757         EdgeRing.prototype.isHole = function isHole () {
101758           return this._isHole
101759         };
101760         EdgeRing.prototype.setInResult = function setInResult () {
101761           var de = this._startDe;
101762           do {
101763             de.getEdge().setInResult(true);
101764             de = de.getNext();
101765           } while (de !== this._startDe)
101766         };
101767         EdgeRing.prototype.containsPoint = function containsPoint (p) {
101768           var shell = this.getLinearRing();
101769           var env = shell.getEnvelopeInternal();
101770           if (!env.contains(p)) { return false }
101771           if (!CGAlgorithms.isPointInRing(p, shell.getCoordinates())) { return false }
101772           for (var i = this._holes.iterator(); i.hasNext();) {
101773             var hole = i.next();
101774             if (hole.containsPoint(p)) { return false }
101775           }
101776           return true
101777         };
101778         EdgeRing.prototype.addHole = function addHole (ring) {
101779           this._holes.add(ring);
101780         };
101781         EdgeRing.prototype.isShell = function isShell () {
101782           return this._shell === null
101783         };
101784         EdgeRing.prototype.getLabel = function getLabel () {
101785           return this._label
101786         };
101787         EdgeRing.prototype.getEdges = function getEdges () {
101788           return this._edges
101789         };
101790         EdgeRing.prototype.getMaxNodeDegree = function getMaxNodeDegree () {
101791           if (this._maxNodeDegree < 0) { this.computeMaxNodeDegree(); }
101792           return this._maxNodeDegree
101793         };
101794         EdgeRing.prototype.getShell = function getShell () {
101795           return this._shell
101796         };
101797         EdgeRing.prototype.mergeLabel = function mergeLabel () {
101798           if (arguments.length === 1) {
101799             var deLabel = arguments[0];
101800             this.mergeLabel(deLabel, 0);
101801             this.mergeLabel(deLabel, 1);
101802           } else if (arguments.length === 2) {
101803             var deLabel$1 = arguments[0];
101804             var geomIndex = arguments[1];
101805             var loc = deLabel$1.getLocation(geomIndex, Position.RIGHT);
101806             if (loc === Location.NONE) { return null }
101807             if (this._label.getLocation(geomIndex) === Location.NONE) {
101808               this._label.setLocation(geomIndex, loc);
101809               return null
101810             }
101811           }
101812         };
101813         EdgeRing.prototype.setShell = function setShell (shell) {
101814           this._shell = shell;
101815           if (shell !== null) { shell.addHole(this); }
101816         };
101817         EdgeRing.prototype.toPolygon = function toPolygon (geometryFactory) {
101818             var this$1 = this;
101819
101820           var holeLR = new Array(this._holes.size()).fill(null);
101821           for (var i = 0; i < this._holes.size(); i++) {
101822             holeLR[i] = this$1._holes.get(i).getLinearRing();
101823           }
101824           var poly = geometryFactory.createPolygon(this.getLinearRing(), holeLR);
101825           return poly
101826         };
101827         EdgeRing.prototype.interfaces_ = function interfaces_ () {
101828           return []
101829         };
101830         EdgeRing.prototype.getClass = function getClass () {
101831           return EdgeRing
101832         };
101833
101834         var MinimalEdgeRing = (function (EdgeRing$$1) {
101835           function MinimalEdgeRing () {
101836             var start = arguments[0];
101837             var geometryFactory = arguments[1];
101838             EdgeRing$$1.call(this, start, geometryFactory);
101839           }
101840
101841           if ( EdgeRing$$1 ) MinimalEdgeRing.__proto__ = EdgeRing$$1;
101842           MinimalEdgeRing.prototype = Object.create( EdgeRing$$1 && EdgeRing$$1.prototype );
101843           MinimalEdgeRing.prototype.constructor = MinimalEdgeRing;
101844           MinimalEdgeRing.prototype.setEdgeRing = function setEdgeRing (de, er) {
101845             de.setMinEdgeRing(er);
101846           };
101847           MinimalEdgeRing.prototype.getNext = function getNext (de) {
101848             return de.getNextMin()
101849           };
101850           MinimalEdgeRing.prototype.interfaces_ = function interfaces_ () {
101851             return []
101852           };
101853           MinimalEdgeRing.prototype.getClass = function getClass () {
101854             return MinimalEdgeRing
101855           };
101856
101857           return MinimalEdgeRing;
101858         }(EdgeRing));
101859
101860         var MaximalEdgeRing = (function (EdgeRing$$1) {
101861           function MaximalEdgeRing () {
101862             var start = arguments[0];
101863             var geometryFactory = arguments[1];
101864             EdgeRing$$1.call(this, start, geometryFactory);
101865           }
101866
101867           if ( EdgeRing$$1 ) MaximalEdgeRing.__proto__ = EdgeRing$$1;
101868           MaximalEdgeRing.prototype = Object.create( EdgeRing$$1 && EdgeRing$$1.prototype );
101869           MaximalEdgeRing.prototype.constructor = MaximalEdgeRing;
101870           MaximalEdgeRing.prototype.buildMinimalRings = function buildMinimalRings () {
101871             var this$1 = this;
101872
101873             var minEdgeRings = new ArrayList();
101874             var de = this._startDe;
101875             do {
101876               if (de.getMinEdgeRing() === null) {
101877                 var minEr = new MinimalEdgeRing(de, this$1._geometryFactory);
101878                 minEdgeRings.add(minEr);
101879               }
101880               de = de.getNext();
101881             } while (de !== this._startDe)
101882             return minEdgeRings
101883           };
101884           MaximalEdgeRing.prototype.setEdgeRing = function setEdgeRing (de, er) {
101885             de.setEdgeRing(er);
101886           };
101887           MaximalEdgeRing.prototype.linkDirectedEdgesForMinimalEdgeRings = function linkDirectedEdgesForMinimalEdgeRings () {
101888             var this$1 = this;
101889
101890             var de = this._startDe;
101891             do {
101892               var node = de.getNode();
101893               node.getEdges().linkMinimalDirectedEdges(this$1);
101894               de = de.getNext();
101895             } while (de !== this._startDe)
101896           };
101897           MaximalEdgeRing.prototype.getNext = function getNext (de) {
101898             return de.getNext()
101899           };
101900           MaximalEdgeRing.prototype.interfaces_ = function interfaces_ () {
101901             return []
101902           };
101903           MaximalEdgeRing.prototype.getClass = function getClass () {
101904             return MaximalEdgeRing
101905           };
101906
101907           return MaximalEdgeRing;
101908         }(EdgeRing));
101909
101910         var GraphComponent = function GraphComponent () {
101911           this._label = null;
101912           this._isInResult = false;
101913           this._isCovered = false;
101914           this._isCoveredSet = false;
101915           this._isVisited = false;
101916           if (arguments.length === 0) ; else if (arguments.length === 1) {
101917             var label = arguments[0];
101918             this._label = label;
101919           }
101920         };
101921         GraphComponent.prototype.setVisited = function setVisited (isVisited) {
101922           this._isVisited = isVisited;
101923         };
101924         GraphComponent.prototype.setInResult = function setInResult (isInResult) {
101925           this._isInResult = isInResult;
101926         };
101927         GraphComponent.prototype.isCovered = function isCovered () {
101928           return this._isCovered
101929         };
101930         GraphComponent.prototype.isCoveredSet = function isCoveredSet () {
101931           return this._isCoveredSet
101932         };
101933         GraphComponent.prototype.setLabel = function setLabel (label) {
101934           this._label = label;
101935         };
101936         GraphComponent.prototype.getLabel = function getLabel () {
101937           return this._label
101938         };
101939         GraphComponent.prototype.setCovered = function setCovered (isCovered) {
101940           this._isCovered = isCovered;
101941           this._isCoveredSet = true;
101942         };
101943         GraphComponent.prototype.updateIM = function updateIM (im) {
101944           Assert.isTrue(this._label.getGeometryCount() >= 2, 'found partial label');
101945           this.computeIM(im);
101946         };
101947         GraphComponent.prototype.isInResult = function isInResult () {
101948           return this._isInResult
101949         };
101950         GraphComponent.prototype.isVisited = function isVisited () {
101951           return this._isVisited
101952         };
101953         GraphComponent.prototype.interfaces_ = function interfaces_ () {
101954           return []
101955         };
101956         GraphComponent.prototype.getClass = function getClass () {
101957           return GraphComponent
101958         };
101959
101960         var Node$1 = (function (GraphComponent$$1) {
101961           function Node () {
101962             GraphComponent$$1.call(this);
101963             this._coord = null;
101964             this._edges = null;
101965             var coord = arguments[0];
101966             var edges = arguments[1];
101967             this._coord = coord;
101968             this._edges = edges;
101969             this._label = new Label(0, Location.NONE);
101970           }
101971
101972           if ( GraphComponent$$1 ) Node.__proto__ = GraphComponent$$1;
101973           Node.prototype = Object.create( GraphComponent$$1 && GraphComponent$$1.prototype );
101974           Node.prototype.constructor = Node;
101975           Node.prototype.isIncidentEdgeInResult = function isIncidentEdgeInResult () {
101976             for (var it = this.getEdges().getEdges().iterator(); it.hasNext();) {
101977               var de = it.next();
101978               if (de.getEdge().isInResult()) { return true }
101979             }
101980             return false
101981           };
101982           Node.prototype.isIsolated = function isIsolated () {
101983             return this._label.getGeometryCount() === 1
101984           };
101985           Node.prototype.getCoordinate = function getCoordinate () {
101986             return this._coord
101987           };
101988           Node.prototype.print = function print (out) {
101989             out.println('node ' + this._coord + ' lbl: ' + this._label);
101990           };
101991           Node.prototype.computeIM = function computeIM (im) {};
101992           Node.prototype.computeMergedLocation = function computeMergedLocation (label2, eltIndex) {
101993             var loc = Location.NONE;
101994             loc = this._label.getLocation(eltIndex);
101995             if (!label2.isNull(eltIndex)) {
101996               var nLoc = label2.getLocation(eltIndex);
101997               if (loc !== Location.BOUNDARY) { loc = nLoc; }
101998             }
101999             return loc
102000           };
102001           Node.prototype.setLabel = function setLabel () {
102002             if (arguments.length === 2) {
102003               var argIndex = arguments[0];
102004               var onLocation = arguments[1];
102005               if (this._label === null) {
102006                 this._label = new Label(argIndex, onLocation);
102007               } else { this._label.setLocation(argIndex, onLocation); }
102008             } else { return GraphComponent$$1.prototype.setLabel.apply(this, arguments) }
102009           };
102010           Node.prototype.getEdges = function getEdges () {
102011             return this._edges
102012           };
102013           Node.prototype.mergeLabel = function mergeLabel () {
102014             var this$1 = this;
102015
102016             if (arguments[0] instanceof Node) {
102017               var n = arguments[0];
102018               this.mergeLabel(n._label);
102019             } else if (arguments[0] instanceof Label) {
102020               var label2 = arguments[0];
102021               for (var i = 0; i < 2; i++) {
102022                 var loc = this$1.computeMergedLocation(label2, i);
102023                 var thisLoc = this$1._label.getLocation(i);
102024                 if (thisLoc === Location.NONE) { this$1._label.setLocation(i, loc); }
102025               }
102026             }
102027           };
102028           Node.prototype.add = function add (e) {
102029             this._edges.insert(e);
102030             e.setNode(this);
102031           };
102032           Node.prototype.setLabelBoundary = function setLabelBoundary (argIndex) {
102033             if (this._label === null) { return null }
102034             var loc = Location.NONE;
102035             if (this._label !== null) { loc = this._label.getLocation(argIndex); }
102036             var newLoc = null;
102037             switch (loc) {
102038               case Location.BOUNDARY:
102039                 newLoc = Location.INTERIOR;
102040                 break
102041               case Location.INTERIOR:
102042                 newLoc = Location.BOUNDARY;
102043                 break
102044               default:
102045                 newLoc = Location.BOUNDARY;
102046                 break
102047             }
102048             this._label.setLocation(argIndex, newLoc);
102049           };
102050           Node.prototype.interfaces_ = function interfaces_ () {
102051             return []
102052           };
102053           Node.prototype.getClass = function getClass () {
102054             return Node
102055           };
102056
102057           return Node;
102058         }(GraphComponent));
102059
102060         var NodeMap = function NodeMap () {
102061           this.nodeMap = new TreeMap();
102062           this.nodeFact = null;
102063           var nodeFact = arguments[0];
102064           this.nodeFact = nodeFact;
102065         };
102066         NodeMap.prototype.find = function find (coord) {
102067           return this.nodeMap.get(coord)
102068         };
102069         NodeMap.prototype.addNode = function addNode () {
102070           if (arguments[0] instanceof Coordinate) {
102071             var coord = arguments[0];
102072             var node = this.nodeMap.get(coord);
102073             if (node === null) {
102074               node = this.nodeFact.createNode(coord);
102075               this.nodeMap.put(coord, node);
102076             }
102077             return node
102078           } else if (arguments[0] instanceof Node$1) {
102079             var n = arguments[0];
102080             var node$1 = this.nodeMap.get(n.getCoordinate());
102081             if (node$1 === null) {
102082               this.nodeMap.put(n.getCoordinate(), n);
102083               return n
102084             }
102085             node$1.mergeLabel(n);
102086             return node$1
102087           }
102088         };
102089         NodeMap.prototype.print = function print (out) {
102090           for (var it = this.iterator(); it.hasNext();) {
102091             var n = it.next();
102092             n.print(out);
102093           }
102094         };
102095         NodeMap.prototype.iterator = function iterator () {
102096           return this.nodeMap.values().iterator()
102097         };
102098         NodeMap.prototype.values = function values () {
102099           return this.nodeMap.values()
102100         };
102101         NodeMap.prototype.getBoundaryNodes = function getBoundaryNodes (geomIndex) {
102102           var bdyNodes = new ArrayList();
102103           for (var i = this.iterator(); i.hasNext();) {
102104             var node = i.next();
102105             if (node.getLabel().getLocation(geomIndex) === Location.BOUNDARY) { bdyNodes.add(node); }
102106           }
102107           return bdyNodes
102108         };
102109         NodeMap.prototype.add = function add (e) {
102110           var p = e.getCoordinate();
102111           var n = this.addNode(p);
102112           n.add(e);
102113         };
102114         NodeMap.prototype.interfaces_ = function interfaces_ () {
102115           return []
102116         };
102117         NodeMap.prototype.getClass = function getClass () {
102118           return NodeMap
102119         };
102120
102121         var Quadrant = function Quadrant () {};
102122
102123         var staticAccessors$21 = { NE: { configurable: true },NW: { configurable: true },SW: { configurable: true },SE: { configurable: true } };
102124
102125         Quadrant.prototype.interfaces_ = function interfaces_ () {
102126           return []
102127         };
102128         Quadrant.prototype.getClass = function getClass () {
102129           return Quadrant
102130         };
102131         Quadrant.isNorthern = function isNorthern (quad) {
102132           return quad === Quadrant.NE || quad === Quadrant.NW
102133         };
102134         Quadrant.isOpposite = function isOpposite (quad1, quad2) {
102135           if (quad1 === quad2) { return false }
102136           var diff = (quad1 - quad2 + 4) % 4;
102137           if (diff === 2) { return true }
102138           return false
102139         };
102140         Quadrant.commonHalfPlane = function commonHalfPlane (quad1, quad2) {
102141           if (quad1 === quad2) { return quad1 }
102142           var diff = (quad1 - quad2 + 4) % 4;
102143           if (diff === 2) { return -1 }
102144           var min = quad1 < quad2 ? quad1 : quad2;
102145           var max = quad1 > quad2 ? quad1 : quad2;
102146           if (min === 0 && max === 3) { return 3 }
102147           return min
102148         };
102149         Quadrant.isInHalfPlane = function isInHalfPlane (quad, halfPlane) {
102150           if (halfPlane === Quadrant.SE) {
102151             return quad === Quadrant.SE || quad === Quadrant.SW
102152           }
102153           return quad === halfPlane || quad === halfPlane + 1
102154         };
102155         Quadrant.quadrant = function quadrant () {
102156           if (typeof arguments[0] === 'number' && typeof arguments[1] === 'number') {
102157             var dx = arguments[0];
102158             var dy = arguments[1];
102159             if (dx === 0.0 && dy === 0.0) { throw new IllegalArgumentException('Cannot compute the quadrant for point ( ' + dx + ', ' + dy + ' )') }
102160             if (dx >= 0.0) {
102161               if (dy >= 0.0) { return Quadrant.NE; } else { return Quadrant.SE }
102162             } else {
102163               if (dy >= 0.0) { return Quadrant.NW; } else { return Quadrant.SW }
102164             }
102165           } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof Coordinate) {
102166             var p0 = arguments[0];
102167             var p1 = arguments[1];
102168             if (p1.x === p0.x && p1.y === p0.y) { throw new IllegalArgumentException('Cannot compute the quadrant for two identical points ' + p0) }
102169             if (p1.x >= p0.x) {
102170               if (p1.y >= p0.y) { return Quadrant.NE; } else { return Quadrant.SE }
102171             } else {
102172               if (p1.y >= p0.y) { return Quadrant.NW; } else { return Quadrant.SW }
102173             }
102174           }
102175         };
102176         staticAccessors$21.NE.get = function () { return 0 };
102177         staticAccessors$21.NW.get = function () { return 1 };
102178         staticAccessors$21.SW.get = function () { return 2 };
102179         staticAccessors$21.SE.get = function () { return 3 };
102180
102181         Object.defineProperties( Quadrant, staticAccessors$21 );
102182
102183         var EdgeEnd = function EdgeEnd () {
102184           this._edge = null;
102185           this._label = null;
102186           this._node = null;
102187           this._p0 = null;
102188           this._p1 = null;
102189           this._dx = null;
102190           this._dy = null;
102191           this._quadrant = null;
102192           if (arguments.length === 1) {
102193             var edge = arguments[0];
102194             this._edge = edge;
102195           } else if (arguments.length === 3) {
102196             var edge$1 = arguments[0];
102197             var p0 = arguments[1];
102198             var p1 = arguments[2];
102199             var label = null;
102200             this._edge = edge$1;
102201             this.init(p0, p1);
102202             this._label = label;
102203           } else if (arguments.length === 4) {
102204             var edge$2 = arguments[0];
102205             var p0$1 = arguments[1];
102206             var p1$1 = arguments[2];
102207             var label$1 = arguments[3];
102208             this._edge = edge$2;
102209             this.init(p0$1, p1$1);
102210             this._label = label$1;
102211           }
102212         };
102213         EdgeEnd.prototype.compareDirection = function compareDirection (e) {
102214           if (this._dx === e._dx && this._dy === e._dy) { return 0 }
102215           if (this._quadrant > e._quadrant) { return 1 }
102216           if (this._quadrant < e._quadrant) { return -1 }
102217           return CGAlgorithms.computeOrientation(e._p0, e._p1, this._p1)
102218         };
102219         EdgeEnd.prototype.getDy = function getDy () {
102220           return this._dy
102221         };
102222         EdgeEnd.prototype.getCoordinate = function getCoordinate () {
102223           return this._p0
102224         };
102225         EdgeEnd.prototype.setNode = function setNode (node) {
102226           this._node = node;
102227         };
102228         EdgeEnd.prototype.print = function print (out) {
102229           var angle = Math.atan2(this._dy, this._dx);
102230           var className = this.getClass().getName();
102231           var lastDotPos = className.lastIndexOf('.');
102232           var name = className.substring(lastDotPos + 1);
102233           out.print('  ' + name + ': ' + this._p0 + ' - ' + this._p1 + ' ' + this._quadrant + ':' + angle + '   ' + this._label);
102234         };
102235         EdgeEnd.prototype.compareTo = function compareTo (obj) {
102236           var e = obj;
102237           return this.compareDirection(e)
102238         };
102239         EdgeEnd.prototype.getDirectedCoordinate = function getDirectedCoordinate () {
102240           return this._p1
102241         };
102242         EdgeEnd.prototype.getDx = function getDx () {
102243           return this._dx
102244         };
102245         EdgeEnd.prototype.getLabel = function getLabel () {
102246           return this._label
102247         };
102248         EdgeEnd.prototype.getEdge = function getEdge () {
102249           return this._edge
102250         };
102251         EdgeEnd.prototype.getQuadrant = function getQuadrant () {
102252           return this._quadrant
102253         };
102254         EdgeEnd.prototype.getNode = function getNode () {
102255           return this._node
102256         };
102257         EdgeEnd.prototype.toString = function toString () {
102258           var angle = Math.atan2(this._dy, this._dx);
102259           var className = this.getClass().getName();
102260           var lastDotPos = className.lastIndexOf('.');
102261           var name = className.substring(lastDotPos + 1);
102262           return '  ' + name + ': ' + this._p0 + ' - ' + this._p1 + ' ' + this._quadrant + ':' + angle + '   ' + this._label
102263         };
102264         EdgeEnd.prototype.computeLabel = function computeLabel (boundaryNodeRule) {};
102265         EdgeEnd.prototype.init = function init (p0, p1) {
102266           this._p0 = p0;
102267           this._p1 = p1;
102268           this._dx = p1.x - p0.x;
102269           this._dy = p1.y - p0.y;
102270           this._quadrant = Quadrant.quadrant(this._dx, this._dy);
102271           Assert.isTrue(!(this._dx === 0 && this._dy === 0), 'EdgeEnd with identical endpoints found');
102272         };
102273         EdgeEnd.prototype.interfaces_ = function interfaces_ () {
102274           return [Comparable]
102275         };
102276         EdgeEnd.prototype.getClass = function getClass () {
102277           return EdgeEnd
102278         };
102279
102280         var DirectedEdge = (function (EdgeEnd$$1) {
102281           function DirectedEdge () {
102282             var edge = arguments[0];
102283             var isForward = arguments[1];
102284             EdgeEnd$$1.call(this, edge);
102285             this._isForward = null;
102286             this._isInResult = false;
102287             this._isVisited = false;
102288             this._sym = null;
102289             this._next = null;
102290             this._nextMin = null;
102291             this._edgeRing = null;
102292             this._minEdgeRing = null;
102293             this._depth = [0, -999, -999];
102294             this._isForward = isForward;
102295             if (isForward) {
102296               this.init(edge.getCoordinate(0), edge.getCoordinate(1));
102297             } else {
102298               var n = edge.getNumPoints() - 1;
102299               this.init(edge.getCoordinate(n), edge.getCoordinate(n - 1));
102300             }
102301             this.computeDirectedLabel();
102302           }
102303
102304           if ( EdgeEnd$$1 ) DirectedEdge.__proto__ = EdgeEnd$$1;
102305           DirectedEdge.prototype = Object.create( EdgeEnd$$1 && EdgeEnd$$1.prototype );
102306           DirectedEdge.prototype.constructor = DirectedEdge;
102307           DirectedEdge.prototype.getNextMin = function getNextMin () {
102308             return this._nextMin
102309           };
102310           DirectedEdge.prototype.getDepth = function getDepth (position) {
102311             return this._depth[position]
102312           };
102313           DirectedEdge.prototype.setVisited = function setVisited (isVisited) {
102314             this._isVisited = isVisited;
102315           };
102316           DirectedEdge.prototype.computeDirectedLabel = function computeDirectedLabel () {
102317             this._label = new Label(this._edge.getLabel());
102318             if (!this._isForward) { this._label.flip(); }
102319           };
102320           DirectedEdge.prototype.getNext = function getNext () {
102321             return this._next
102322           };
102323           DirectedEdge.prototype.setDepth = function setDepth (position, depthVal) {
102324             if (this._depth[position] !== -999) {
102325               if (this._depth[position] !== depthVal) { throw new TopologyException('assigned depths do not match', this.getCoordinate()) }
102326             }
102327             this._depth[position] = depthVal;
102328           };
102329           DirectedEdge.prototype.isInteriorAreaEdge = function isInteriorAreaEdge () {
102330             var this$1 = this;
102331
102332             var isInteriorAreaEdge = true;
102333             for (var i = 0; i < 2; i++) {
102334               if (!(this$1._label.isArea(i) && this$1._label.getLocation(i, Position.LEFT) === Location.INTERIOR && this$1._label.getLocation(i, Position.RIGHT) === Location.INTERIOR)) {
102335                 isInteriorAreaEdge = false;
102336               }
102337             }
102338             return isInteriorAreaEdge
102339           };
102340           DirectedEdge.prototype.setNextMin = function setNextMin (nextMin) {
102341             this._nextMin = nextMin;
102342           };
102343           DirectedEdge.prototype.print = function print (out) {
102344             EdgeEnd$$1.prototype.print.call(this, out);
102345             out.print(' ' + this._depth[Position.LEFT] + '/' + this._depth[Position.RIGHT]);
102346             out.print(' (' + this.getDepthDelta() + ')');
102347             if (this._isInResult) { out.print(' inResult'); }
102348           };
102349           DirectedEdge.prototype.setMinEdgeRing = function setMinEdgeRing (minEdgeRing) {
102350             this._minEdgeRing = minEdgeRing;
102351           };
102352           DirectedEdge.prototype.isLineEdge = function isLineEdge () {
102353             var isLine = this._label.isLine(0) || this._label.isLine(1);
102354             var isExteriorIfArea0 = !this._label.isArea(0) || this._label.allPositionsEqual(0, Location.EXTERIOR);
102355             var isExteriorIfArea1 = !this._label.isArea(1) || this._label.allPositionsEqual(1, Location.EXTERIOR);
102356             return isLine && isExteriorIfArea0 && isExteriorIfArea1
102357           };
102358           DirectedEdge.prototype.setEdgeRing = function setEdgeRing (edgeRing) {
102359             this._edgeRing = edgeRing;
102360           };
102361           DirectedEdge.prototype.getMinEdgeRing = function getMinEdgeRing () {
102362             return this._minEdgeRing
102363           };
102364           DirectedEdge.prototype.getDepthDelta = function getDepthDelta () {
102365             var depthDelta = this._edge.getDepthDelta();
102366             if (!this._isForward) { depthDelta = -depthDelta; }
102367             return depthDelta
102368           };
102369           DirectedEdge.prototype.setInResult = function setInResult (isInResult) {
102370             this._isInResult = isInResult;
102371           };
102372           DirectedEdge.prototype.getSym = function getSym () {
102373             return this._sym
102374           };
102375           DirectedEdge.prototype.isForward = function isForward () {
102376             return this._isForward
102377           };
102378           DirectedEdge.prototype.getEdge = function getEdge () {
102379             return this._edge
102380           };
102381           DirectedEdge.prototype.printEdge = function printEdge (out) {
102382             this.print(out);
102383             out.print(' ');
102384             if (this._isForward) { this._edge.print(out); } else { this._edge.printReverse(out); }
102385           };
102386           DirectedEdge.prototype.setSym = function setSym (de) {
102387             this._sym = de;
102388           };
102389           DirectedEdge.prototype.setVisitedEdge = function setVisitedEdge (isVisited) {
102390             this.setVisited(isVisited);
102391             this._sym.setVisited(isVisited);
102392           };
102393           DirectedEdge.prototype.setEdgeDepths = function setEdgeDepths (position, depth) {
102394             var depthDelta = this.getEdge().getDepthDelta();
102395             if (!this._isForward) { depthDelta = -depthDelta; }
102396             var directionFactor = 1;
102397             if (position === Position.LEFT) { directionFactor = -1; }
102398             var oppositePos = Position.opposite(position);
102399             var delta = depthDelta * directionFactor;
102400             var oppositeDepth = depth + delta;
102401             this.setDepth(position, depth);
102402             this.setDepth(oppositePos, oppositeDepth);
102403           };
102404           DirectedEdge.prototype.getEdgeRing = function getEdgeRing () {
102405             return this._edgeRing
102406           };
102407           DirectedEdge.prototype.isInResult = function isInResult () {
102408             return this._isInResult
102409           };
102410           DirectedEdge.prototype.setNext = function setNext (next) {
102411             this._next = next;
102412           };
102413           DirectedEdge.prototype.isVisited = function isVisited () {
102414             return this._isVisited
102415           };
102416           DirectedEdge.prototype.interfaces_ = function interfaces_ () {
102417             return []
102418           };
102419           DirectedEdge.prototype.getClass = function getClass () {
102420             return DirectedEdge
102421           };
102422           DirectedEdge.depthFactor = function depthFactor (currLocation, nextLocation) {
102423             if (currLocation === Location.EXTERIOR && nextLocation === Location.INTERIOR) { return 1; } else if (currLocation === Location.INTERIOR && nextLocation === Location.EXTERIOR) { return -1 }
102424             return 0
102425           };
102426
102427           return DirectedEdge;
102428         }(EdgeEnd));
102429
102430         var NodeFactory = function NodeFactory () {};
102431
102432         NodeFactory.prototype.createNode = function createNode (coord) {
102433           return new Node$1(coord, null)
102434         };
102435         NodeFactory.prototype.interfaces_ = function interfaces_ () {
102436           return []
102437         };
102438         NodeFactory.prototype.getClass = function getClass () {
102439           return NodeFactory
102440         };
102441
102442         var PlanarGraph = function PlanarGraph () {
102443           this._edges = new ArrayList();
102444           this._nodes = null;
102445           this._edgeEndList = new ArrayList();
102446           if (arguments.length === 0) {
102447             this._nodes = new NodeMap(new NodeFactory());
102448           } else if (arguments.length === 1) {
102449             var nodeFact = arguments[0];
102450             this._nodes = new NodeMap(nodeFact);
102451           }
102452         };
102453         PlanarGraph.prototype.printEdges = function printEdges (out) {
102454             var this$1 = this;
102455
102456           out.println('Edges:');
102457           for (var i = 0; i < this._edges.size(); i++) {
102458             out.println('edge ' + i + ':');
102459             var e = this$1._edges.get(i);
102460             e.print(out);
102461             e.eiList.print(out);
102462           }
102463         };
102464         PlanarGraph.prototype.find = function find (coord) {
102465           return this._nodes.find(coord)
102466         };
102467         PlanarGraph.prototype.addNode = function addNode () {
102468           if (arguments[0] instanceof Node$1) {
102469             var node = arguments[0];
102470             return this._nodes.addNode(node)
102471           } else if (arguments[0] instanceof Coordinate) {
102472             var coord = arguments[0];
102473             return this._nodes.addNode(coord)
102474           }
102475         };
102476         PlanarGraph.prototype.getNodeIterator = function getNodeIterator () {
102477           return this._nodes.iterator()
102478         };
102479         PlanarGraph.prototype.linkResultDirectedEdges = function linkResultDirectedEdges () {
102480           for (var nodeit = this._nodes.iterator(); nodeit.hasNext();) {
102481             var node = nodeit.next();
102482             node.getEdges().linkResultDirectedEdges();
102483           }
102484         };
102485         PlanarGraph.prototype.debugPrintln = function debugPrintln (o) {
102486           System.out.println(o);
102487         };
102488         PlanarGraph.prototype.isBoundaryNode = function isBoundaryNode (geomIndex, coord) {
102489           var node = this._nodes.find(coord);
102490           if (node === null) { return false }
102491           var label = node.getLabel();
102492           if (label !== null && label.getLocation(geomIndex) === Location.BOUNDARY) { return true }
102493           return false
102494         };
102495         PlanarGraph.prototype.linkAllDirectedEdges = function linkAllDirectedEdges () {
102496           for (var nodeit = this._nodes.iterator(); nodeit.hasNext();) {
102497             var node = nodeit.next();
102498             node.getEdges().linkAllDirectedEdges();
102499           }
102500         };
102501         PlanarGraph.prototype.matchInSameDirection = function matchInSameDirection (p0, p1, ep0, ep1) {
102502           if (!p0.equals(ep0)) { return false }
102503           if (CGAlgorithms.computeOrientation(p0, p1, ep1) === CGAlgorithms.COLLINEAR && Quadrant.quadrant(p0, p1) === Quadrant.quadrant(ep0, ep1)) { return true }
102504           return false
102505         };
102506         PlanarGraph.prototype.getEdgeEnds = function getEdgeEnds () {
102507           return this._edgeEndList
102508         };
102509         PlanarGraph.prototype.debugPrint = function debugPrint (o) {
102510           System.out.print(o);
102511         };
102512         PlanarGraph.prototype.getEdgeIterator = function getEdgeIterator () {
102513           return this._edges.iterator()
102514         };
102515         PlanarGraph.prototype.findEdgeInSameDirection = function findEdgeInSameDirection (p0, p1) {
102516             var this$1 = this;
102517
102518           for (var i = 0; i < this._edges.size(); i++) {
102519             var e = this$1._edges.get(i);
102520             var eCoord = e.getCoordinates();
102521             if (this$1.matchInSameDirection(p0, p1, eCoord[0], eCoord[1])) { return e }
102522             if (this$1.matchInSameDirection(p0, p1, eCoord[eCoord.length - 1], eCoord[eCoord.length - 2])) { return e }
102523           }
102524           return null
102525         };
102526         PlanarGraph.prototype.insertEdge = function insertEdge (e) {
102527           this._edges.add(e);
102528         };
102529         PlanarGraph.prototype.findEdgeEnd = function findEdgeEnd (e) {
102530           for (var i = this.getEdgeEnds().iterator(); i.hasNext();) {
102531             var ee = i.next();
102532             if (ee.getEdge() === e) { return ee }
102533           }
102534           return null
102535         };
102536         PlanarGraph.prototype.addEdges = function addEdges (edgesToAdd) {
102537             var this$1 = this;
102538
102539           for (var it = edgesToAdd.iterator(); it.hasNext();) {
102540             var e = it.next();
102541             this$1._edges.add(e);
102542             var de1 = new DirectedEdge(e, true);
102543             var de2 = new DirectedEdge(e, false);
102544             de1.setSym(de2);
102545             de2.setSym(de1);
102546             this$1.add(de1);
102547             this$1.add(de2);
102548           }
102549         };
102550         PlanarGraph.prototype.add = function add (e) {
102551           this._nodes.add(e);
102552           this._edgeEndList.add(e);
102553         };
102554         PlanarGraph.prototype.getNodes = function getNodes () {
102555           return this._nodes.values()
102556         };
102557         PlanarGraph.prototype.findEdge = function findEdge (p0, p1) {
102558             var this$1 = this;
102559
102560           for (var i = 0; i < this._edges.size(); i++) {
102561             var e = this$1._edges.get(i);
102562             var eCoord = e.getCoordinates();
102563             if (p0.equals(eCoord[0]) && p1.equals(eCoord[1])) { return e }
102564           }
102565           return null
102566         };
102567         PlanarGraph.prototype.interfaces_ = function interfaces_ () {
102568           return []
102569         };
102570         PlanarGraph.prototype.getClass = function getClass () {
102571           return PlanarGraph
102572         };
102573         PlanarGraph.linkResultDirectedEdges = function linkResultDirectedEdges (nodes) {
102574           for (var nodeit = nodes.iterator(); nodeit.hasNext();) {
102575             var node = nodeit.next();
102576             node.getEdges().linkResultDirectedEdges();
102577           }
102578         };
102579
102580         var PolygonBuilder = function PolygonBuilder () {
102581           this._geometryFactory = null;
102582           this._shellList = new ArrayList();
102583           var geometryFactory = arguments[0];
102584           this._geometryFactory = geometryFactory;
102585         };
102586         PolygonBuilder.prototype.sortShellsAndHoles = function sortShellsAndHoles (edgeRings, shellList, freeHoleList) {
102587           for (var it = edgeRings.iterator(); it.hasNext();) {
102588             var er = it.next();
102589             if (er.isHole()) {
102590               freeHoleList.add(er);
102591             } else {
102592               shellList.add(er);
102593             }
102594           }
102595         };
102596         PolygonBuilder.prototype.computePolygons = function computePolygons (shellList) {
102597             var this$1 = this;
102598
102599           var resultPolyList = new ArrayList();
102600           for (var it = shellList.iterator(); it.hasNext();) {
102601             var er = it.next();
102602             var poly = er.toPolygon(this$1._geometryFactory);
102603             resultPolyList.add(poly);
102604           }
102605           return resultPolyList
102606         };
102607         PolygonBuilder.prototype.placeFreeHoles = function placeFreeHoles (shellList, freeHoleList) {
102608             var this$1 = this;
102609
102610           for (var it = freeHoleList.iterator(); it.hasNext();) {
102611             var hole = it.next();
102612             if (hole.getShell() === null) {
102613               var shell = this$1.findEdgeRingContaining(hole, shellList);
102614               if (shell === null) { throw new TopologyException('unable to assign hole to a shell', hole.getCoordinate(0)) }
102615               hole.setShell(shell);
102616             }
102617           }
102618         };
102619         PolygonBuilder.prototype.buildMinimalEdgeRings = function buildMinimalEdgeRings (maxEdgeRings, shellList, freeHoleList) {
102620             var this$1 = this;
102621
102622           var edgeRings = new ArrayList();
102623           for (var it = maxEdgeRings.iterator(); it.hasNext();) {
102624             var er = it.next();
102625             if (er.getMaxNodeDegree() > 2) {
102626               er.linkDirectedEdgesForMinimalEdgeRings();
102627               var minEdgeRings = er.buildMinimalRings();
102628               var shell = this$1.findShell(minEdgeRings);
102629               if (shell !== null) {
102630                 this$1.placePolygonHoles(shell, minEdgeRings);
102631                 shellList.add(shell);
102632               } else {
102633                 freeHoleList.addAll(minEdgeRings);
102634               }
102635             } else {
102636               edgeRings.add(er);
102637             }
102638           }
102639           return edgeRings
102640         };
102641         PolygonBuilder.prototype.containsPoint = function containsPoint (p) {
102642           for (var it = this._shellList.iterator(); it.hasNext();) {
102643             var er = it.next();
102644             if (er.containsPoint(p)) { return true }
102645           }
102646           return false
102647         };
102648         PolygonBuilder.prototype.buildMaximalEdgeRings = function buildMaximalEdgeRings (dirEdges) {
102649             var this$1 = this;
102650
102651           var maxEdgeRings = new ArrayList();
102652           for (var it = dirEdges.iterator(); it.hasNext();) {
102653             var de = it.next();
102654             if (de.isInResult() && de.getLabel().isArea()) {
102655               if (de.getEdgeRing() === null) {
102656                 var er = new MaximalEdgeRing(de, this$1._geometryFactory);
102657                 maxEdgeRings.add(er);
102658                 er.setInResult();
102659               }
102660             }
102661           }
102662           return maxEdgeRings
102663         };
102664         PolygonBuilder.prototype.placePolygonHoles = function placePolygonHoles (shell, minEdgeRings) {
102665           for (var it = minEdgeRings.iterator(); it.hasNext();) {
102666             var er = it.next();
102667             if (er.isHole()) {
102668               er.setShell(shell);
102669             }
102670           }
102671         };
102672         PolygonBuilder.prototype.getPolygons = function getPolygons () {
102673           var resultPolyList = this.computePolygons(this._shellList);
102674           return resultPolyList
102675         };
102676         PolygonBuilder.prototype.findEdgeRingContaining = function findEdgeRingContaining (testEr, shellList) {
102677           var testRing = testEr.getLinearRing();
102678           var testEnv = testRing.getEnvelopeInternal();
102679           var testPt = testRing.getCoordinateN(0);
102680           var minShell = null;
102681           var minEnv = null;
102682           for (var it = shellList.iterator(); it.hasNext();) {
102683             var tryShell = it.next();
102684             var tryRing = tryShell.getLinearRing();
102685             var tryEnv = tryRing.getEnvelopeInternal();
102686             if (minShell !== null) { minEnv = minShell.getLinearRing().getEnvelopeInternal(); }
102687             var isContained = false;
102688             if (tryEnv.contains(testEnv) && CGAlgorithms.isPointInRing(testPt, tryRing.getCoordinates())) { isContained = true; }
102689             if (isContained) {
102690               if (minShell === null || minEnv.contains(tryEnv)) {
102691                 minShell = tryShell;
102692               }
102693             }
102694           }
102695           return minShell
102696         };
102697         PolygonBuilder.prototype.findShell = function findShell (minEdgeRings) {
102698           var shellCount = 0;
102699           var shell = null;
102700           for (var it = minEdgeRings.iterator(); it.hasNext();) {
102701             var er = it.next();
102702             if (!er.isHole()) {
102703               shell = er;
102704               shellCount++;
102705             }
102706           }
102707           Assert.isTrue(shellCount <= 1, 'found two shells in MinimalEdgeRing list');
102708           return shell
102709         };
102710         PolygonBuilder.prototype.add = function add () {
102711           if (arguments.length === 1) {
102712             var graph = arguments[0];
102713             this.add(graph.getEdgeEnds(), graph.getNodes());
102714           } else if (arguments.length === 2) {
102715             var dirEdges = arguments[0];
102716             var nodes = arguments[1];
102717             PlanarGraph.linkResultDirectedEdges(nodes);
102718             var maxEdgeRings = this.buildMaximalEdgeRings(dirEdges);
102719             var freeHoleList = new ArrayList();
102720             var edgeRings = this.buildMinimalEdgeRings(maxEdgeRings, this._shellList, freeHoleList);
102721             this.sortShellsAndHoles(edgeRings, this._shellList, freeHoleList);
102722             this.placeFreeHoles(this._shellList, freeHoleList);
102723           }
102724         };
102725         PolygonBuilder.prototype.interfaces_ = function interfaces_ () {
102726           return []
102727         };
102728         PolygonBuilder.prototype.getClass = function getClass () {
102729           return PolygonBuilder
102730         };
102731
102732         var Boundable = function Boundable () {};
102733
102734         Boundable.prototype.getBounds = function getBounds () {};
102735         Boundable.prototype.interfaces_ = function interfaces_ () {
102736           return []
102737         };
102738         Boundable.prototype.getClass = function getClass () {
102739           return Boundable
102740         };
102741
102742         var ItemBoundable = function ItemBoundable () {
102743           this._bounds = null;
102744           this._item = null;
102745           var bounds = arguments[0];
102746           var item = arguments[1];
102747           this._bounds = bounds;
102748           this._item = item;
102749         };
102750         ItemBoundable.prototype.getItem = function getItem () {
102751           return this._item
102752         };
102753         ItemBoundable.prototype.getBounds = function getBounds () {
102754           return this._bounds
102755         };
102756         ItemBoundable.prototype.interfaces_ = function interfaces_ () {
102757           return [Boundable, Serializable]
102758         };
102759         ItemBoundable.prototype.getClass = function getClass () {
102760           return ItemBoundable
102761         };
102762
102763         var PriorityQueue = function PriorityQueue () {
102764           this._size = null;
102765           this._items = null;
102766           this._size = 0;
102767           this._items = new ArrayList();
102768           this._items.add(null);
102769         };
102770         PriorityQueue.prototype.poll = function poll () {
102771           if (this.isEmpty()) { return null }
102772           var minItem = this._items.get(1);
102773           this._items.set(1, this._items.get(this._size));
102774           this._size -= 1;
102775           this.reorder(1);
102776           return minItem
102777         };
102778         PriorityQueue.prototype.size = function size () {
102779           return this._size
102780         };
102781         PriorityQueue.prototype.reorder = function reorder (hole) {
102782             var this$1 = this;
102783
102784           var child = null;
102785           var tmp = this._items.get(hole);
102786           for (; hole * 2 <= this._size; hole = child) {
102787             child = hole * 2;
102788             if (child !== this$1._size && this$1._items.get(child + 1).compareTo(this$1._items.get(child)) < 0) { child++; }
102789             if (this$1._items.get(child).compareTo(tmp) < 0) { this$1._items.set(hole, this$1._items.get(child)); } else { break }
102790           }
102791           this._items.set(hole, tmp);
102792         };
102793         PriorityQueue.prototype.clear = function clear () {
102794           this._size = 0;
102795           this._items.clear();
102796         };
102797         PriorityQueue.prototype.isEmpty = function isEmpty () {
102798           return this._size === 0
102799         };
102800         PriorityQueue.prototype.add = function add (x) {
102801             var this$1 = this;
102802
102803           this._items.add(null);
102804           this._size += 1;
102805           var hole = this._size;
102806           this._items.set(0, x);
102807           for (; x.compareTo(this._items.get(Math.trunc(hole / 2))) < 0; hole /= 2) {
102808             this$1._items.set(hole, this$1._items.get(Math.trunc(hole / 2)));
102809           }
102810           this._items.set(hole, x);
102811         };
102812         PriorityQueue.prototype.interfaces_ = function interfaces_ () {
102813           return []
102814         };
102815         PriorityQueue.prototype.getClass = function getClass () {
102816           return PriorityQueue
102817         };
102818
102819         var ItemVisitor = function ItemVisitor () {};
102820
102821         ItemVisitor.prototype.visitItem = function visitItem (item) {};
102822         ItemVisitor.prototype.interfaces_ = function interfaces_ () {
102823           return []
102824         };
102825         ItemVisitor.prototype.getClass = function getClass () {
102826           return ItemVisitor
102827         };
102828
102829         var SpatialIndex = function SpatialIndex () {};
102830
102831         SpatialIndex.prototype.insert = function insert (itemEnv, item) {};
102832         SpatialIndex.prototype.remove = function remove (itemEnv, item) {};
102833         SpatialIndex.prototype.query = function query () {
102834           // if (arguments.length === 1) {
102835           // const searchEnv = arguments[0]
102836           // } else if (arguments.length === 2) {
102837           // const searchEnv = arguments[0]
102838           // const visitor = arguments[1]
102839           // }
102840         };
102841         SpatialIndex.prototype.interfaces_ = function interfaces_ () {
102842           return []
102843         };
102844         SpatialIndex.prototype.getClass = function getClass () {
102845           return SpatialIndex
102846         };
102847
102848         var AbstractNode = function AbstractNode () {
102849           this._childBoundables = new ArrayList();
102850           this._bounds = null;
102851           this._level = null;
102852           if (arguments.length === 0) ; else if (arguments.length === 1) {
102853             var level = arguments[0];
102854             this._level = level;
102855           }
102856         };
102857
102858         var staticAccessors$22 = { serialVersionUID: { configurable: true } };
102859         AbstractNode.prototype.getLevel = function getLevel () {
102860           return this._level
102861         };
102862         AbstractNode.prototype.size = function size () {
102863           return this._childBoundables.size()
102864         };
102865         AbstractNode.prototype.getChildBoundables = function getChildBoundables () {
102866           return this._childBoundables
102867         };
102868         AbstractNode.prototype.addChildBoundable = function addChildBoundable (childBoundable) {
102869           Assert.isTrue(this._bounds === null);
102870           this._childBoundables.add(childBoundable);
102871         };
102872         AbstractNode.prototype.isEmpty = function isEmpty () {
102873           return this._childBoundables.isEmpty()
102874         };
102875         AbstractNode.prototype.getBounds = function getBounds () {
102876           if (this._bounds === null) {
102877             this._bounds = this.computeBounds();
102878           }
102879           return this._bounds
102880         };
102881         AbstractNode.prototype.interfaces_ = function interfaces_ () {
102882           return [Boundable, Serializable]
102883         };
102884         AbstractNode.prototype.getClass = function getClass () {
102885           return AbstractNode
102886         };
102887         staticAccessors$22.serialVersionUID.get = function () { return 6493722185909573708 };
102888
102889         Object.defineProperties( AbstractNode, staticAccessors$22 );
102890
102891         var Collections = function Collections () {};
102892
102893         Collections.reverseOrder = function reverseOrder () {
102894           return {
102895             compare: function compare (a, b) {
102896               return b.compareTo(a)
102897             }
102898           }
102899         };
102900         Collections.min = function min (l) {
102901           Collections.sort(l);
102902           return l.get(0)
102903         };
102904         Collections.sort = function sort (l, c) {
102905           var a = l.toArray();
102906           if (c) {
102907             Arrays.sort(a, c);
102908           } else {
102909             Arrays.sort(a);
102910           }
102911           var i = l.iterator();
102912           for (var pos = 0, alen = a.length; pos < alen; pos++) {
102913             i.next();
102914             i.set(a[pos]);
102915           }
102916         };
102917         Collections.singletonList = function singletonList (o) {
102918           var arrayList = new ArrayList();
102919           arrayList.add(o);
102920           return arrayList
102921         };
102922
102923         var BoundablePair = function BoundablePair () {
102924           this._boundable1 = null;
102925           this._boundable2 = null;
102926           this._distance = null;
102927           this._itemDistance = null;
102928           var boundable1 = arguments[0];
102929           var boundable2 = arguments[1];
102930           var itemDistance = arguments[2];
102931           this._boundable1 = boundable1;
102932           this._boundable2 = boundable2;
102933           this._itemDistance = itemDistance;
102934           this._distance = this.distance();
102935         };
102936         BoundablePair.prototype.expandToQueue = function expandToQueue (priQ, minDistance) {
102937           var isComp1 = BoundablePair.isComposite(this._boundable1);
102938           var isComp2 = BoundablePair.isComposite(this._boundable2);
102939           if (isComp1 && isComp2) {
102940             if (BoundablePair.area(this._boundable1) > BoundablePair.area(this._boundable2)) {
102941               this.expand(this._boundable1, this._boundable2, priQ, minDistance);
102942               return null
102943             } else {
102944               this.expand(this._boundable2, this._boundable1, priQ, minDistance);
102945               return null
102946             }
102947           } else if (isComp1) {
102948             this.expand(this._boundable1, this._boundable2, priQ, minDistance);
102949             return null
102950           } else if (isComp2) {
102951             this.expand(this._boundable2, this._boundable1, priQ, minDistance);
102952             return null
102953           }
102954           throw new IllegalArgumentException('neither boundable is composite')
102955         };
102956         BoundablePair.prototype.isLeaves = function isLeaves () {
102957           return !(BoundablePair.isComposite(this._boundable1) || BoundablePair.isComposite(this._boundable2))
102958         };
102959         BoundablePair.prototype.compareTo = function compareTo (o) {
102960           var nd = o;
102961           if (this._distance < nd._distance) { return -1 }
102962           if (this._distance > nd._distance) { return 1 }
102963           return 0
102964         };
102965         BoundablePair.prototype.expand = function expand (bndComposite, bndOther, priQ, minDistance) {
102966             var this$1 = this;
102967
102968           var children = bndComposite.getChildBoundables();
102969           for (var i = children.iterator(); i.hasNext();) {
102970             var child = i.next();
102971             var bp = new BoundablePair(child, bndOther, this$1._itemDistance);
102972             if (bp.getDistance() < minDistance) {
102973               priQ.add(bp);
102974             }
102975           }
102976         };
102977         BoundablePair.prototype.getBoundable = function getBoundable (i) {
102978           if (i === 0) { return this._boundable1 }
102979           return this._boundable2
102980         };
102981         BoundablePair.prototype.getDistance = function getDistance () {
102982           return this._distance
102983         };
102984         BoundablePair.prototype.distance = function distance () {
102985           if (this.isLeaves()) {
102986             return this._itemDistance.distance(this._boundable1, this._boundable2)
102987           }
102988           return this._boundable1.getBounds().distance(this._boundable2.getBounds())
102989         };
102990         BoundablePair.prototype.interfaces_ = function interfaces_ () {
102991           return [Comparable]
102992         };
102993         BoundablePair.prototype.getClass = function getClass () {
102994           return BoundablePair
102995         };
102996         BoundablePair.area = function area (b) {
102997           return b.getBounds().getArea()
102998         };
102999         BoundablePair.isComposite = function isComposite (item) {
103000           return item instanceof AbstractNode
103001         };
103002
103003         var AbstractSTRtree = function AbstractSTRtree () {
103004           this._root = null;
103005           this._built = false;
103006           this._itemBoundables = new ArrayList();
103007           this._nodeCapacity = null;
103008           if (arguments.length === 0) {
103009             var nodeCapacity = AbstractSTRtree.DEFAULT_NODE_CAPACITY;
103010             this._nodeCapacity = nodeCapacity;
103011           } else if (arguments.length === 1) {
103012             var nodeCapacity$1 = arguments[0];
103013             Assert.isTrue(nodeCapacity$1 > 1, 'Node capacity must be greater than 1');
103014             this._nodeCapacity = nodeCapacity$1;
103015           }
103016         };
103017
103018         var staticAccessors$23 = { IntersectsOp: { configurable: true },serialVersionUID: { configurable: true },DEFAULT_NODE_CAPACITY: { configurable: true } };
103019         AbstractSTRtree.prototype.getNodeCapacity = function getNodeCapacity () {
103020           return this._nodeCapacity
103021         };
103022         AbstractSTRtree.prototype.lastNode = function lastNode (nodes) {
103023           return nodes.get(nodes.size() - 1)
103024         };
103025         AbstractSTRtree.prototype.size = function size () {
103026             var this$1 = this;
103027
103028           if (arguments.length === 0) {
103029             if (this.isEmpty()) {
103030               return 0
103031             }
103032             this.build();
103033             return this.size(this._root)
103034           } else if (arguments.length === 1) {
103035             var node = arguments[0];
103036             var size = 0;
103037             for (var i = node.getChildBoundables().iterator(); i.hasNext();) {
103038               var childBoundable = i.next();
103039               if (childBoundable instanceof AbstractNode) {
103040                 size += this$1.size(childBoundable);
103041               } else if (childBoundable instanceof ItemBoundable) {
103042                 size += 1;
103043               }
103044             }
103045             return size
103046           }
103047         };
103048         AbstractSTRtree.prototype.removeItem = function removeItem (node, item) {
103049           var childToRemove = null;
103050           for (var i = node.getChildBoundables().iterator(); i.hasNext();) {
103051             var childBoundable = i.next();
103052             if (childBoundable instanceof ItemBoundable) {
103053               if (childBoundable.getItem() === item) { childToRemove = childBoundable; }
103054             }
103055           }
103056           if (childToRemove !== null) {
103057             node.getChildBoundables().remove(childToRemove);
103058             return true
103059           }
103060           return false
103061         };
103062         AbstractSTRtree.prototype.itemsTree = function itemsTree () {
103063             var this$1 = this;
103064
103065           if (arguments.length === 0) {
103066             this.build();
103067             var valuesTree = this.itemsTree(this._root);
103068             if (valuesTree === null) { return new ArrayList() }
103069             return valuesTree
103070           } else if (arguments.length === 1) {
103071             var node = arguments[0];
103072             var valuesTreeForNode = new ArrayList();
103073             for (var i = node.getChildBoundables().iterator(); i.hasNext();) {
103074               var childBoundable = i.next();
103075               if (childBoundable instanceof AbstractNode) {
103076                 var valuesTreeForChild = this$1.itemsTree(childBoundable);
103077                 if (valuesTreeForChild !== null) { valuesTreeForNode.add(valuesTreeForChild); }
103078               } else if (childBoundable instanceof ItemBoundable) {
103079                 valuesTreeForNode.add(childBoundable.getItem());
103080               } else {
103081                 Assert.shouldNeverReachHere();
103082               }
103083             }
103084             if (valuesTreeForNode.size() <= 0) { return null }
103085             return valuesTreeForNode
103086           }
103087         };
103088         AbstractSTRtree.prototype.insert = function insert (bounds, item) {
103089           Assert.isTrue(!this._built, 'Cannot insert items into an STR packed R-tree after it has been built.');
103090           this._itemBoundables.add(new ItemBoundable(bounds, item));
103091         };
103092         AbstractSTRtree.prototype.boundablesAtLevel = function boundablesAtLevel () {
103093             var this$1 = this;
103094
103095           if (arguments.length === 1) {
103096             var level = arguments[0];
103097             var boundables = new ArrayList();
103098             this.boundablesAtLevel(level, this._root, boundables);
103099             return boundables
103100           } else if (arguments.length === 3) {
103101             var level$1 = arguments[0];
103102             var top = arguments[1];
103103             var boundables$1 = arguments[2];
103104             Assert.isTrue(level$1 > -2);
103105             if (top.getLevel() === level$1) {
103106               boundables$1.add(top);
103107               return null
103108             }
103109             for (var i = top.getChildBoundables().iterator(); i.hasNext();) {
103110               var boundable = i.next();
103111               if (boundable instanceof AbstractNode) {
103112                 this$1.boundablesAtLevel(level$1, boundable, boundables$1);
103113               } else {
103114                 Assert.isTrue(boundable instanceof ItemBoundable);
103115                 if (level$1 === -1) {
103116                   boundables$1.add(boundable);
103117                 }
103118               }
103119             }
103120             return null
103121           }
103122         };
103123         AbstractSTRtree.prototype.query = function query () {
103124             var this$1 = this;
103125
103126           if (arguments.length === 1) {
103127             var searchBounds = arguments[0];
103128             this.build();
103129             var matches = new ArrayList();
103130             if (this.isEmpty()) {
103131               return matches
103132             }
103133             if (this.getIntersectsOp().intersects(this._root.getBounds(), searchBounds)) {
103134               this.query(searchBounds, this._root, matches);
103135             }
103136             return matches
103137           } else if (arguments.length === 2) {
103138             var searchBounds$1 = arguments[0];
103139             var visitor = arguments[1];
103140             this.build();
103141             if (this.isEmpty()) {
103142               return null
103143             }
103144             if (this.getIntersectsOp().intersects(this._root.getBounds(), searchBounds$1)) {
103145               this.query(searchBounds$1, this._root, visitor);
103146             }
103147           } else if (arguments.length === 3) {
103148             if (hasInterface(arguments[2], ItemVisitor) && (arguments[0] instanceof Object && arguments[1] instanceof AbstractNode)) {
103149               var searchBounds$2 = arguments[0];
103150               var node = arguments[1];
103151               var visitor$1 = arguments[2];
103152               var childBoundables = node.getChildBoundables();
103153               for (var i = 0; i < childBoundables.size(); i++) {
103154                 var childBoundable = childBoundables.get(i);
103155                 if (!this$1.getIntersectsOp().intersects(childBoundable.getBounds(), searchBounds$2)) {
103156                   continue
103157                 }
103158                 if (childBoundable instanceof AbstractNode) {
103159                   this$1.query(searchBounds$2, childBoundable, visitor$1);
103160                 } else if (childBoundable instanceof ItemBoundable) {
103161                   visitor$1.visitItem(childBoundable.getItem());
103162                 } else {
103163                   Assert.shouldNeverReachHere();
103164                 }
103165               }
103166             } else if (hasInterface(arguments[2], List) && (arguments[0] instanceof Object && arguments[1] instanceof AbstractNode)) {
103167               var searchBounds$3 = arguments[0];
103168               var node$1 = arguments[1];
103169               var matches$1 = arguments[2];
103170               var childBoundables$1 = node$1.getChildBoundables();
103171               for (var i$1 = 0; i$1 < childBoundables$1.size(); i$1++) {
103172                 var childBoundable$1 = childBoundables$1.get(i$1);
103173                 if (!this$1.getIntersectsOp().intersects(childBoundable$1.getBounds(), searchBounds$3)) {
103174                   continue
103175                 }
103176                 if (childBoundable$1 instanceof AbstractNode) {
103177                   this$1.query(searchBounds$3, childBoundable$1, matches$1);
103178                 } else if (childBoundable$1 instanceof ItemBoundable) {
103179                   matches$1.add(childBoundable$1.getItem());
103180                 } else {
103181                   Assert.shouldNeverReachHere();
103182                 }
103183               }
103184             }
103185           }
103186         };
103187         AbstractSTRtree.prototype.build = function build () {
103188           if (this._built) { return null }
103189           this._root = this._itemBoundables.isEmpty() ? this.createNode(0) : this.createHigherLevels(this._itemBoundables, -1);
103190           this._itemBoundables = null;
103191           this._built = true;
103192         };
103193         AbstractSTRtree.prototype.getRoot = function getRoot () {
103194           this.build();
103195           return this._root
103196         };
103197         AbstractSTRtree.prototype.remove = function remove () {
103198             var this$1 = this;
103199
103200           if (arguments.length === 2) {
103201             var searchBounds = arguments[0];
103202             var item = arguments[1];
103203             this.build();
103204             if (this.getIntersectsOp().intersects(this._root.getBounds(), searchBounds)) {
103205               return this.remove(searchBounds, this._root, item)
103206             }
103207             return false
103208           } else if (arguments.length === 3) {
103209             var searchBounds$1 = arguments[0];
103210             var node = arguments[1];
103211             var item$1 = arguments[2];
103212             var found = this.removeItem(node, item$1);
103213             if (found) { return true }
103214             var childToPrune = null;
103215             for (var i = node.getChildBoundables().iterator(); i.hasNext();) {
103216               var childBoundable = i.next();
103217               if (!this$1.getIntersectsOp().intersects(childBoundable.getBounds(), searchBounds$1)) {
103218                 continue
103219               }
103220               if (childBoundable instanceof AbstractNode) {
103221                 found = this$1.remove(searchBounds$1, childBoundable, item$1);
103222                 if (found) {
103223                   childToPrune = childBoundable;
103224                   break
103225                 }
103226               }
103227             }
103228             if (childToPrune !== null) {
103229               if (childToPrune.getChildBoundables().isEmpty()) {
103230                 node.getChildBoundables().remove(childToPrune);
103231               }
103232             }
103233             return found
103234           }
103235         };
103236         AbstractSTRtree.prototype.createHigherLevels = function createHigherLevels (boundablesOfALevel, level) {
103237           Assert.isTrue(!boundablesOfALevel.isEmpty());
103238           var parentBoundables = this.createParentBoundables(boundablesOfALevel, level + 1);
103239           if (parentBoundables.size() === 1) {
103240             return parentBoundables.get(0)
103241           }
103242           return this.createHigherLevels(parentBoundables, level + 1)
103243         };
103244         AbstractSTRtree.prototype.depth = function depth () {
103245             var this$1 = this;
103246
103247           if (arguments.length === 0) {
103248             if (this.isEmpty()) {
103249               return 0
103250             }
103251             this.build();
103252             return this.depth(this._root)
103253           } else if (arguments.length === 1) {
103254             var node = arguments[0];
103255             var maxChildDepth = 0;
103256             for (var i = node.getChildBoundables().iterator(); i.hasNext();) {
103257               var childBoundable = i.next();
103258               if (childBoundable instanceof AbstractNode) {
103259                 var childDepth = this$1.depth(childBoundable);
103260                 if (childDepth > maxChildDepth) { maxChildDepth = childDepth; }
103261               }
103262             }
103263             return maxChildDepth + 1
103264           }
103265         };
103266         AbstractSTRtree.prototype.createParentBoundables = function createParentBoundables (childBoundables, newLevel) {
103267             var this$1 = this;
103268
103269           Assert.isTrue(!childBoundables.isEmpty());
103270           var parentBoundables = new ArrayList();
103271           parentBoundables.add(this.createNode(newLevel));
103272           var sortedChildBoundables = new ArrayList(childBoundables);
103273           Collections.sort(sortedChildBoundables, this.getComparator());
103274           for (var i = sortedChildBoundables.iterator(); i.hasNext();) {
103275             var childBoundable = i.next();
103276             if (this$1.lastNode(parentBoundables).getChildBoundables().size() === this$1.getNodeCapacity()) {
103277               parentBoundables.add(this$1.createNode(newLevel));
103278             }
103279             this$1.lastNode(parentBoundables).addChildBoundable(childBoundable);
103280           }
103281           return parentBoundables
103282         };
103283         AbstractSTRtree.prototype.isEmpty = function isEmpty () {
103284           if (!this._built) { return this._itemBoundables.isEmpty() }
103285           return this._root.isEmpty()
103286         };
103287         AbstractSTRtree.prototype.interfaces_ = function interfaces_ () {
103288           return [Serializable]
103289         };
103290         AbstractSTRtree.prototype.getClass = function getClass () {
103291           return AbstractSTRtree
103292         };
103293         AbstractSTRtree.compareDoubles = function compareDoubles (a, b) {
103294           return a > b ? 1 : a < b ? -1 : 0
103295         };
103296         staticAccessors$23.IntersectsOp.get = function () { return IntersectsOp };
103297         staticAccessors$23.serialVersionUID.get = function () { return -3886435814360241337 };
103298         staticAccessors$23.DEFAULT_NODE_CAPACITY.get = function () { return 10 };
103299
103300         Object.defineProperties( AbstractSTRtree, staticAccessors$23 );
103301
103302         var IntersectsOp = function IntersectsOp () {};
103303
103304         var ItemDistance = function ItemDistance () {};
103305
103306         ItemDistance.prototype.distance = function distance (item1, item2) {};
103307         ItemDistance.prototype.interfaces_ = function interfaces_ () {
103308           return []
103309         };
103310         ItemDistance.prototype.getClass = function getClass () {
103311           return ItemDistance
103312         };
103313
103314         var STRtree = (function (AbstractSTRtree$$1) {
103315           function STRtree (nodeCapacity) {
103316             nodeCapacity = nodeCapacity || STRtree.DEFAULT_NODE_CAPACITY;
103317             AbstractSTRtree$$1.call(this, nodeCapacity);
103318           }
103319
103320           if ( AbstractSTRtree$$1 ) STRtree.__proto__ = AbstractSTRtree$$1;
103321           STRtree.prototype = Object.create( AbstractSTRtree$$1 && AbstractSTRtree$$1.prototype );
103322           STRtree.prototype.constructor = STRtree;
103323
103324           var staticAccessors = { STRtreeNode: { configurable: true },serialVersionUID: { configurable: true },xComparator: { configurable: true },yComparator: { configurable: true },intersectsOp: { configurable: true },DEFAULT_NODE_CAPACITY: { configurable: true } };
103325           STRtree.prototype.createParentBoundablesFromVerticalSlices = function createParentBoundablesFromVerticalSlices (verticalSlices, newLevel) {
103326             var this$1 = this;
103327
103328             Assert.isTrue(verticalSlices.length > 0);
103329             var parentBoundables = new ArrayList();
103330             for (var i = 0; i < verticalSlices.length; i++) {
103331               parentBoundables.addAll(this$1.createParentBoundablesFromVerticalSlice(verticalSlices[i], newLevel));
103332             }
103333             return parentBoundables
103334           };
103335           STRtree.prototype.createNode = function createNode (level) {
103336             return new STRtreeNode(level)
103337           };
103338           STRtree.prototype.size = function size () {
103339             if (arguments.length === 0) {
103340               return AbstractSTRtree$$1.prototype.size.call(this)
103341             } else { return AbstractSTRtree$$1.prototype.size.apply(this, arguments) }
103342           };
103343           STRtree.prototype.insert = function insert () {
103344             if (arguments.length === 2) {
103345               var itemEnv = arguments[0];
103346               var item = arguments[1];
103347               if (itemEnv.isNull()) {
103348                 return null
103349               }
103350               AbstractSTRtree$$1.prototype.insert.call(this, itemEnv, item);
103351             } else { return AbstractSTRtree$$1.prototype.insert.apply(this, arguments) }
103352           };
103353           STRtree.prototype.getIntersectsOp = function getIntersectsOp () {
103354             return STRtree.intersectsOp
103355           };
103356           STRtree.prototype.verticalSlices = function verticalSlices (childBoundables, sliceCount) {
103357             var sliceCapacity = Math.trunc(Math.ceil(childBoundables.size() / sliceCount));
103358             var slices = new Array(sliceCount).fill(null);
103359             var i = childBoundables.iterator();
103360             for (var j = 0; j < sliceCount; j++) {
103361               slices[j] = new ArrayList();
103362               var boundablesAddedToSlice = 0;
103363               while (i.hasNext() && boundablesAddedToSlice < sliceCapacity) {
103364                 var childBoundable = i.next();
103365                 slices[j].add(childBoundable);
103366                 boundablesAddedToSlice++;
103367               }
103368             }
103369             return slices
103370           };
103371           STRtree.prototype.query = function query () {
103372             if (arguments.length === 1) {
103373               var searchEnv = arguments[0];
103374               return AbstractSTRtree$$1.prototype.query.call(this, searchEnv)
103375             } else if (arguments.length === 2) {
103376               var searchEnv$1 = arguments[0];
103377               var visitor = arguments[1];
103378               AbstractSTRtree$$1.prototype.query.call(this, searchEnv$1, visitor);
103379             } else if (arguments.length === 3) {
103380               if (hasInterface(arguments[2], ItemVisitor) && (arguments[0] instanceof Object && arguments[1] instanceof AbstractNode)) {
103381                 var searchBounds = arguments[0];
103382                 var node = arguments[1];
103383                 var visitor$1 = arguments[2];
103384                 AbstractSTRtree$$1.prototype.query.call(this, searchBounds, node, visitor$1);
103385               } else if (hasInterface(arguments[2], List) && (arguments[0] instanceof Object && arguments[1] instanceof AbstractNode)) {
103386                 var searchBounds$1 = arguments[0];
103387                 var node$1 = arguments[1];
103388                 var matches = arguments[2];
103389                 AbstractSTRtree$$1.prototype.query.call(this, searchBounds$1, node$1, matches);
103390               }
103391             }
103392           };
103393           STRtree.prototype.getComparator = function getComparator () {
103394             return STRtree.yComparator
103395           };
103396           STRtree.prototype.createParentBoundablesFromVerticalSlice = function createParentBoundablesFromVerticalSlice (childBoundables, newLevel) {
103397             return AbstractSTRtree$$1.prototype.createParentBoundables.call(this, childBoundables, newLevel)
103398           };
103399           STRtree.prototype.remove = function remove () {
103400             if (arguments.length === 2) {
103401               var itemEnv = arguments[0];
103402               var item = arguments[1];
103403               return AbstractSTRtree$$1.prototype.remove.call(this, itemEnv, item)
103404             } else { return AbstractSTRtree$$1.prototype.remove.apply(this, arguments) }
103405           };
103406           STRtree.prototype.depth = function depth () {
103407             if (arguments.length === 0) {
103408               return AbstractSTRtree$$1.prototype.depth.call(this)
103409             } else { return AbstractSTRtree$$1.prototype.depth.apply(this, arguments) }
103410           };
103411           STRtree.prototype.createParentBoundables = function createParentBoundables (childBoundables, newLevel) {
103412             Assert.isTrue(!childBoundables.isEmpty());
103413             var minLeafCount = Math.trunc(Math.ceil(childBoundables.size() / this.getNodeCapacity()));
103414             var sortedChildBoundables = new ArrayList(childBoundables);
103415             Collections.sort(sortedChildBoundables, STRtree.xComparator);
103416             var verticalSlices = this.verticalSlices(sortedChildBoundables, Math.trunc(Math.ceil(Math.sqrt(minLeafCount))));
103417             return this.createParentBoundablesFromVerticalSlices(verticalSlices, newLevel)
103418           };
103419           STRtree.prototype.nearestNeighbour = function nearestNeighbour () {
103420             if (arguments.length === 1) {
103421               if (hasInterface(arguments[0], ItemDistance)) {
103422                 var itemDist = arguments[0];
103423                 var bp = new BoundablePair(this.getRoot(), this.getRoot(), itemDist);
103424                 return this.nearestNeighbour(bp)
103425               } else if (arguments[0] instanceof BoundablePair) {
103426                 var initBndPair = arguments[0];
103427                 return this.nearestNeighbour(initBndPair, Double.POSITIVE_INFINITY)
103428               }
103429             } else if (arguments.length === 2) {
103430               if (arguments[0] instanceof STRtree && hasInterface(arguments[1], ItemDistance)) {
103431                 var tree = arguments[0];
103432                 var itemDist$1 = arguments[1];
103433                 var bp$1 = new BoundablePair(this.getRoot(), tree.getRoot(), itemDist$1);
103434                 return this.nearestNeighbour(bp$1)
103435               } else if (arguments[0] instanceof BoundablePair && typeof arguments[1] === 'number') {
103436                 var initBndPair$1 = arguments[0];
103437                 var maxDistance = arguments[1];
103438                 var distanceLowerBound = maxDistance;
103439                 var minPair = null;
103440                 var priQ = new PriorityQueue();
103441                 priQ.add(initBndPair$1);
103442                 while (!priQ.isEmpty() && distanceLowerBound > 0.0) {
103443                   var bndPair = priQ.poll();
103444                   var currentDistance = bndPair.getDistance();
103445                   if (currentDistance >= distanceLowerBound) { break }
103446                   if (bndPair.isLeaves()) {
103447                     distanceLowerBound = currentDistance;
103448                     minPair = bndPair;
103449                   } else {
103450                     bndPair.expandToQueue(priQ, distanceLowerBound);
103451                   }
103452                 }
103453                 return [minPair.getBoundable(0).getItem(), minPair.getBoundable(1).getItem()]
103454               }
103455             } else if (arguments.length === 3) {
103456               var env = arguments[0];
103457               var item = arguments[1];
103458               var itemDist$2 = arguments[2];
103459               var bnd = new ItemBoundable(env, item);
103460               var bp$2 = new BoundablePair(this.getRoot(), bnd, itemDist$2);
103461               return this.nearestNeighbour(bp$2)[0]
103462             }
103463           };
103464           STRtree.prototype.interfaces_ = function interfaces_ () {
103465             return [SpatialIndex, Serializable]
103466           };
103467           STRtree.prototype.getClass = function getClass () {
103468             return STRtree
103469           };
103470           STRtree.centreX = function centreX (e) {
103471             return STRtree.avg(e.getMinX(), e.getMaxX())
103472           };
103473           STRtree.avg = function avg (a, b) {
103474             return (a + b) / 2
103475           };
103476           STRtree.centreY = function centreY (e) {
103477             return STRtree.avg(e.getMinY(), e.getMaxY())
103478           };
103479           staticAccessors.STRtreeNode.get = function () { return STRtreeNode };
103480           staticAccessors.serialVersionUID.get = function () { return 259274702368956900 };
103481           staticAccessors.xComparator.get = function () {
103482             return {
103483               interfaces_: function () {
103484                 return [Comparator]
103485               },
103486               compare: function (o1, o2) {
103487                 return AbstractSTRtree$$1.compareDoubles(STRtree.centreX(o1.getBounds()), STRtree.centreX(o2.getBounds()))
103488               }
103489             }
103490           };
103491           staticAccessors.yComparator.get = function () {
103492             return {
103493               interfaces_: function () {
103494                 return [Comparator]
103495               },
103496               compare: function (o1, o2) {
103497                 return AbstractSTRtree$$1.compareDoubles(STRtree.centreY(o1.getBounds()), STRtree.centreY(o2.getBounds()))
103498               }
103499             }
103500           };
103501           staticAccessors.intersectsOp.get = function () {
103502             return {
103503               interfaces_: function () {
103504                 return [AbstractSTRtree$$1.IntersectsOp]
103505               },
103506               intersects: function (aBounds, bBounds) {
103507                 return aBounds.intersects(bBounds)
103508               }
103509             }
103510           };
103511           staticAccessors.DEFAULT_NODE_CAPACITY.get = function () { return 10 };
103512
103513           Object.defineProperties( STRtree, staticAccessors );
103514
103515           return STRtree;
103516         }(AbstractSTRtree));
103517
103518         var STRtreeNode = (function (AbstractNode$$1) {
103519           function STRtreeNode () {
103520             var level = arguments[0];
103521             AbstractNode$$1.call(this, level);
103522           }
103523
103524           if ( AbstractNode$$1 ) STRtreeNode.__proto__ = AbstractNode$$1;
103525           STRtreeNode.prototype = Object.create( AbstractNode$$1 && AbstractNode$$1.prototype );
103526           STRtreeNode.prototype.constructor = STRtreeNode;
103527           STRtreeNode.prototype.computeBounds = function computeBounds () {
103528             var bounds = null;
103529             for (var i = this.getChildBoundables().iterator(); i.hasNext();) {
103530               var childBoundable = i.next();
103531               if (bounds === null) {
103532                 bounds = new Envelope(childBoundable.getBounds());
103533               } else {
103534                 bounds.expandToInclude(childBoundable.getBounds());
103535               }
103536             }
103537             return bounds
103538           };
103539           STRtreeNode.prototype.interfaces_ = function interfaces_ () {
103540             return []
103541           };
103542           STRtreeNode.prototype.getClass = function getClass () {
103543             return STRtreeNode
103544           };
103545
103546           return STRtreeNode;
103547         }(AbstractNode));
103548
103549         var SegmentPointComparator = function SegmentPointComparator () {};
103550
103551         SegmentPointComparator.prototype.interfaces_ = function interfaces_ () {
103552           return []
103553         };
103554         SegmentPointComparator.prototype.getClass = function getClass () {
103555           return SegmentPointComparator
103556         };
103557         SegmentPointComparator.relativeSign = function relativeSign (x0, x1) {
103558           if (x0 < x1) { return -1 }
103559           if (x0 > x1) { return 1 }
103560           return 0
103561         };
103562         SegmentPointComparator.compare = function compare (octant, p0, p1) {
103563           if (p0.equals2D(p1)) { return 0 }
103564           var xSign = SegmentPointComparator.relativeSign(p0.x, p1.x);
103565           var ySign = SegmentPointComparator.relativeSign(p0.y, p1.y);
103566           switch (octant) {
103567             case 0:
103568               return SegmentPointComparator.compareValue(xSign, ySign)
103569             case 1:
103570               return SegmentPointComparator.compareValue(ySign, xSign)
103571             case 2:
103572               return SegmentPointComparator.compareValue(ySign, -xSign)
103573             case 3:
103574               return SegmentPointComparator.compareValue(-xSign, ySign)
103575             case 4:
103576               return SegmentPointComparator.compareValue(-xSign, -ySign)
103577             case 5:
103578               return SegmentPointComparator.compareValue(-ySign, -xSign)
103579             case 6:
103580               return SegmentPointComparator.compareValue(-ySign, xSign)
103581             case 7:
103582               return SegmentPointComparator.compareValue(xSign, -ySign)
103583           }
103584           Assert.shouldNeverReachHere('invalid octant value');
103585           return 0
103586         };
103587         SegmentPointComparator.compareValue = function compareValue (compareSign0, compareSign1) {
103588           if (compareSign0 < 0) { return -1 }
103589           if (compareSign0 > 0) { return 1 }
103590           if (compareSign1 < 0) { return -1 }
103591           if (compareSign1 > 0) { return 1 }
103592           return 0
103593         };
103594
103595         var SegmentNode = function SegmentNode () {
103596           this._segString = null;
103597           this.coord = null;
103598           this.segmentIndex = null;
103599           this._segmentOctant = null;
103600           this._isInterior = null;
103601           var segString = arguments[0];
103602           var coord = arguments[1];
103603           var segmentIndex = arguments[2];
103604           var segmentOctant = arguments[3];
103605           this._segString = segString;
103606           this.coord = new Coordinate(coord);
103607           this.segmentIndex = segmentIndex;
103608           this._segmentOctant = segmentOctant;
103609           this._isInterior = !coord.equals2D(segString.getCoordinate(segmentIndex));
103610         };
103611         SegmentNode.prototype.getCoordinate = function getCoordinate () {
103612           return this.coord
103613         };
103614         SegmentNode.prototype.print = function print (out) {
103615           out.print(this.coord);
103616           out.print(' seg # = ' + this.segmentIndex);
103617         };
103618         SegmentNode.prototype.compareTo = function compareTo (obj) {
103619           var other = obj;
103620           if (this.segmentIndex < other.segmentIndex) { return -1 }
103621           if (this.segmentIndex > other.segmentIndex) { return 1 }
103622           if (this.coord.equals2D(other.coord)) { return 0 }
103623           return SegmentPointComparator.compare(this._segmentOctant, this.coord, other.coord)
103624         };
103625         SegmentNode.prototype.isEndPoint = function isEndPoint (maxSegmentIndex) {
103626           if (this.segmentIndex === 0 && !this._isInterior) { return true }
103627           if (this.segmentIndex === maxSegmentIndex) { return true }
103628           return false
103629         };
103630         SegmentNode.prototype.isInterior = function isInterior () {
103631           return this._isInterior
103632         };
103633         SegmentNode.prototype.interfaces_ = function interfaces_ () {
103634           return [Comparable]
103635         };
103636         SegmentNode.prototype.getClass = function getClass () {
103637           return SegmentNode
103638         };
103639
103640         // import Iterator from '../../../../java/util/Iterator'
103641         var SegmentNodeList = function SegmentNodeList () {
103642           this._nodeMap = new TreeMap();
103643           this._edge = null;
103644           var edge = arguments[0];
103645           this._edge = edge;
103646         };
103647         SegmentNodeList.prototype.getSplitCoordinates = function getSplitCoordinates () {
103648             var this$1 = this;
103649
103650           var coordList = new CoordinateList();
103651           this.addEndpoints();
103652           var it = this.iterator();
103653           var eiPrev = it.next();
103654           while (it.hasNext()) {
103655             var ei = it.next();
103656             this$1.addEdgeCoordinates(eiPrev, ei, coordList);
103657             eiPrev = ei;
103658           }
103659           return coordList.toCoordinateArray()
103660         };
103661         SegmentNodeList.prototype.addCollapsedNodes = function addCollapsedNodes () {
103662             var this$1 = this;
103663
103664           var collapsedVertexIndexes = new ArrayList();
103665           this.findCollapsesFromInsertedNodes(collapsedVertexIndexes);
103666           this.findCollapsesFromExistingVertices(collapsedVertexIndexes);
103667           for (var it = collapsedVertexIndexes.iterator(); it.hasNext();) {
103668             var vertexIndex = it.next().intValue();
103669             this$1.add(this$1._edge.getCoordinate(vertexIndex), vertexIndex);
103670           }
103671         };
103672         SegmentNodeList.prototype.print = function print (out) {
103673           out.println('Intersections:');
103674           for (var it = this.iterator(); it.hasNext();) {
103675             var ei = it.next();
103676             ei.print(out);
103677           }
103678         };
103679         SegmentNodeList.prototype.findCollapsesFromExistingVertices = function findCollapsesFromExistingVertices (collapsedVertexIndexes) {
103680             var this$1 = this;
103681
103682           for (var i = 0; i < this._edge.size() - 2; i++) {
103683             var p0 = this$1._edge.getCoordinate(i);
103684             // const p1 = this._edge.getCoordinate(i + 1)
103685             var p2 = this$1._edge.getCoordinate(i + 2);
103686             if (p0.equals2D(p2)) {
103687               collapsedVertexIndexes.add(new Integer(i + 1));
103688             }
103689           }
103690         };
103691         SegmentNodeList.prototype.addEdgeCoordinates = function addEdgeCoordinates (ei0, ei1, coordList) {
103692             var this$1 = this;
103693
103694           // let npts = ei1.segmentIndex - ei0.segmentIndex + 2
103695           var lastSegStartPt = this._edge.getCoordinate(ei1.segmentIndex);
103696           var useIntPt1 = ei1.isInterior() || !ei1.coord.equals2D(lastSegStartPt);
103697           // if (!useIntPt1) {
103698           // npts--
103699           // }
103700           // const ipt = 0
103701           coordList.add(new Coordinate(ei0.coord), false);
103702           for (var i = ei0.segmentIndex + 1; i <= ei1.segmentIndex; i++) {
103703             coordList.add(this$1._edge.getCoordinate(i));
103704           }
103705           if (useIntPt1) {
103706             coordList.add(new Coordinate(ei1.coord));
103707           }
103708         };
103709         SegmentNodeList.prototype.iterator = function iterator () {
103710           return this._nodeMap.values().iterator()
103711         };
103712         SegmentNodeList.prototype.addSplitEdges = function addSplitEdges (edgeList) {
103713             var this$1 = this;
103714
103715           this.addEndpoints();
103716           this.addCollapsedNodes();
103717           var it = this.iterator();
103718           var eiPrev = it.next();
103719           while (it.hasNext()) {
103720             var ei = it.next();
103721             var newEdge = this$1.createSplitEdge(eiPrev, ei);
103722             edgeList.add(newEdge);
103723             eiPrev = ei;
103724           }
103725         };
103726         SegmentNodeList.prototype.findCollapseIndex = function findCollapseIndex (ei0, ei1, collapsedVertexIndex) {
103727           if (!ei0.coord.equals2D(ei1.coord)) { return false }
103728           var numVerticesBetween = ei1.segmentIndex - ei0.segmentIndex;
103729           if (!ei1.isInterior()) {
103730             numVerticesBetween--;
103731           }
103732           if (numVerticesBetween === 1) {
103733             collapsedVertexIndex[0] = ei0.segmentIndex + 1;
103734             return true
103735           }
103736           return false
103737         };
103738         SegmentNodeList.prototype.findCollapsesFromInsertedNodes = function findCollapsesFromInsertedNodes (collapsedVertexIndexes) {
103739             var this$1 = this;
103740
103741           var collapsedVertexIndex = new Array(1).fill(null);
103742           var it = this.iterator();
103743           var eiPrev = it.next();
103744           while (it.hasNext()) {
103745             var ei = it.next();
103746             var isCollapsed = this$1.findCollapseIndex(eiPrev, ei, collapsedVertexIndex);
103747             if (isCollapsed) { collapsedVertexIndexes.add(new Integer(collapsedVertexIndex[0])); }
103748             eiPrev = ei;
103749           }
103750         };
103751         SegmentNodeList.prototype.getEdge = function getEdge () {
103752           return this._edge
103753         };
103754         SegmentNodeList.prototype.addEndpoints = function addEndpoints () {
103755           var maxSegIndex = this._edge.size() - 1;
103756           this.add(this._edge.getCoordinate(0), 0);
103757           this.add(this._edge.getCoordinate(maxSegIndex), maxSegIndex);
103758         };
103759         SegmentNodeList.prototype.createSplitEdge = function createSplitEdge (ei0, ei1) {
103760             var this$1 = this;
103761
103762           var npts = ei1.segmentIndex - ei0.segmentIndex + 2;
103763           var lastSegStartPt = this._edge.getCoordinate(ei1.segmentIndex);
103764           var useIntPt1 = ei1.isInterior() || !ei1.coord.equals2D(lastSegStartPt);
103765           if (!useIntPt1) {
103766             npts--;
103767           }
103768           var pts = new Array(npts).fill(null);
103769           var ipt = 0;
103770           pts[ipt++] = new Coordinate(ei0.coord);
103771           for (var i = ei0.segmentIndex + 1; i <= ei1.segmentIndex; i++) {
103772             pts[ipt++] = this$1._edge.getCoordinate(i);
103773           }
103774           if (useIntPt1) { pts[ipt] = new Coordinate(ei1.coord); }
103775           return new NodedSegmentString(pts, this._edge.getData())
103776         };
103777         SegmentNodeList.prototype.add = function add (intPt, segmentIndex) {
103778           var eiNew = new SegmentNode(this._edge, intPt, segmentIndex, this._edge.getSegmentOctant(segmentIndex));
103779           var ei = this._nodeMap.get(eiNew);
103780           if (ei !== null) {
103781             Assert.isTrue(ei.coord.equals2D(intPt), 'Found equal nodes with different coordinates');
103782             return ei
103783           }
103784           this._nodeMap.put(eiNew, eiNew);
103785           return eiNew
103786         };
103787         SegmentNodeList.prototype.checkSplitEdgesCorrectness = function checkSplitEdgesCorrectness (splitEdges) {
103788           var edgePts = this._edge.getCoordinates();
103789           var split0 = splitEdges.get(0);
103790           var pt0 = split0.getCoordinate(0);
103791           if (!pt0.equals2D(edgePts[0])) { throw new RuntimeException('bad split edge start point at ' + pt0) }
103792           var splitn = splitEdges.get(splitEdges.size() - 1);
103793           var splitnPts = splitn.getCoordinates();
103794           var ptn = splitnPts[splitnPts.length - 1];
103795           if (!ptn.equals2D(edgePts[edgePts.length - 1])) { throw new RuntimeException('bad split edge end point at ' + ptn) }
103796         };
103797         SegmentNodeList.prototype.interfaces_ = function interfaces_ () {
103798           return []
103799         };
103800         SegmentNodeList.prototype.getClass = function getClass () {
103801           return SegmentNodeList
103802         };
103803
103804
103805
103806         // class NodeVertexIterator {
103807         //   constructor () {
103808         //     this._nodeList = null
103809         //     this._edge = null
103810         //     this._nodeIt = null
103811         //     this._currNode = null
103812         //     this._nextNode = null
103813         //     this._currSegIndex = 0
103814         //     let nodeList = arguments[0]
103815         //     this._nodeList = nodeList
103816         //     this._edge = nodeList.getEdge()
103817         //     this._nodeIt = nodeList.iterator()
103818         //     this.readNextNode()
103819         //   }
103820         //   next () {
103821         //     if (this._currNode === null) {
103822         //       this._currNode = this._nextNode
103823         //       this._currSegIndex = this._currNode.segmentIndex
103824         //       this.readNextNode()
103825         //       return this._currNode
103826         //     }
103827         //     if (this._nextNode === null) return null
103828         //     if (this._nextNode.segmentIndex === this._currNode.segmentIndex) {
103829         //       this._currNode = this._nextNode
103830         //       this._currSegIndex = this._currNode.segmentIndex
103831         //       this.readNextNode()
103832         //       return this._currNode
103833         //     }
103834         //     if (this._nextNode.segmentIndex > this._currNode.segmentIndex) {}
103835         //     return null
103836         //   }
103837         //   remove () {
103838         //     // throw new UnsupportedOperationException(this.getClass().getName())
103839         //   }
103840         //   hasNext () {
103841         //     if (this._nextNode === null) return false
103842         //     return true
103843         //   }
103844         //   readNextNode () {
103845         //     if (this._nodeIt.hasNext()) this._nextNode = this._nodeIt.next(); else this._nextNode = null
103846         //   }
103847         //   interfaces_ () {
103848         //     return [Iterator]
103849         //   }
103850         //   getClass () {
103851         //     return NodeVertexIterator
103852         //   }
103853         // }
103854
103855         var Octant = function Octant () {};
103856
103857         Octant.prototype.interfaces_ = function interfaces_ () {
103858           return []
103859         };
103860         Octant.prototype.getClass = function getClass () {
103861           return Octant
103862         };
103863         Octant.octant = function octant () {
103864           if (typeof arguments[0] === 'number' && typeof arguments[1] === 'number') {
103865             var dx = arguments[0];
103866             var dy = arguments[1];
103867             if (dx === 0.0 && dy === 0.0) { throw new IllegalArgumentException('Cannot compute the octant for point ( ' + dx + ', ' + dy + ' )') }
103868             var adx = Math.abs(dx);
103869             var ady = Math.abs(dy);
103870             if (dx >= 0) {
103871               if (dy >= 0) {
103872                 if (adx >= ady) { return 0; } else { return 1 }
103873               } else {
103874                 if (adx >= ady) { return 7; } else { return 6 }
103875               }
103876             } else {
103877               if (dy >= 0) {
103878                 if (adx >= ady) { return 3; } else { return 2 }
103879               } else {
103880                 if (adx >= ady) { return 4; } else { return 5 }
103881               }
103882             }
103883           } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof Coordinate) {
103884             var p0 = arguments[0];
103885             var p1 = arguments[1];
103886             var dx$1 = p1.x - p0.x;
103887             var dy$1 = p1.y - p0.y;
103888             if (dx$1 === 0.0 && dy$1 === 0.0) { throw new IllegalArgumentException('Cannot compute the octant for two identical points ' + p0) }
103889             return Octant.octant(dx$1, dy$1)
103890           }
103891         };
103892
103893         var SegmentString = function SegmentString () {};
103894
103895         SegmentString.prototype.getCoordinates = function getCoordinates () {};
103896         SegmentString.prototype.size = function size () {};
103897         SegmentString.prototype.getCoordinate = function getCoordinate (i) {};
103898         SegmentString.prototype.isClosed = function isClosed () {};
103899         SegmentString.prototype.setData = function setData (data) {};
103900         SegmentString.prototype.getData = function getData () {};
103901         SegmentString.prototype.interfaces_ = function interfaces_ () {
103902           return []
103903         };
103904         SegmentString.prototype.getClass = function getClass () {
103905           return SegmentString
103906         };
103907
103908         var NodableSegmentString = function NodableSegmentString () {};
103909
103910         NodableSegmentString.prototype.addIntersection = function addIntersection (intPt, segmentIndex) {};
103911         NodableSegmentString.prototype.interfaces_ = function interfaces_ () {
103912           return [SegmentString]
103913         };
103914         NodableSegmentString.prototype.getClass = function getClass () {
103915           return NodableSegmentString
103916         };
103917
103918         var NodedSegmentString = function NodedSegmentString () {
103919           this._nodeList = new SegmentNodeList(this);
103920           this._pts = null;
103921           this._data = null;
103922           var pts = arguments[0];
103923           var data = arguments[1];
103924           this._pts = pts;
103925           this._data = data;
103926         };
103927         NodedSegmentString.prototype.getCoordinates = function getCoordinates () {
103928           return this._pts
103929         };
103930         NodedSegmentString.prototype.size = function size () {
103931           return this._pts.length
103932         };
103933         NodedSegmentString.prototype.getCoordinate = function getCoordinate (i) {
103934           return this._pts[i]
103935         };
103936         NodedSegmentString.prototype.isClosed = function isClosed () {
103937           return this._pts[0].equals(this._pts[this._pts.length - 1])
103938         };
103939         NodedSegmentString.prototype.getSegmentOctant = function getSegmentOctant (index) {
103940           if (index === this._pts.length - 1) { return -1 }
103941           return this.safeOctant(this.getCoordinate(index), this.getCoordinate(index + 1))
103942         };
103943         NodedSegmentString.prototype.setData = function setData (data) {
103944           this._data = data;
103945         };
103946         NodedSegmentString.prototype.safeOctant = function safeOctant (p0, p1) {
103947           if (p0.equals2D(p1)) { return 0 }
103948           return Octant.octant(p0, p1)
103949         };
103950         NodedSegmentString.prototype.getData = function getData () {
103951           return this._data
103952         };
103953         NodedSegmentString.prototype.addIntersection = function addIntersection () {
103954           if (arguments.length === 2) {
103955             var intPt$1 = arguments[0];
103956             var segmentIndex = arguments[1];
103957             this.addIntersectionNode(intPt$1, segmentIndex);
103958           } else if (arguments.length === 4) {
103959             var li = arguments[0];
103960             var segmentIndex$1 = arguments[1];
103961             // const geomIndex = arguments[2]
103962             var intIndex = arguments[3];
103963             var intPt = new Coordinate(li.getIntersection(intIndex));
103964             this.addIntersection(intPt, segmentIndex$1);
103965           }
103966         };
103967         NodedSegmentString.prototype.toString = function toString () {
103968           return WKTWriter.toLineString(new CoordinateArraySequence(this._pts))
103969         };
103970         NodedSegmentString.prototype.getNodeList = function getNodeList () {
103971           return this._nodeList
103972         };
103973         NodedSegmentString.prototype.addIntersectionNode = function addIntersectionNode (intPt, segmentIndex) {
103974           var normalizedSegmentIndex = segmentIndex;
103975           var nextSegIndex = normalizedSegmentIndex + 1;
103976           if (nextSegIndex < this._pts.length) {
103977             var nextPt = this._pts[nextSegIndex];
103978             if (intPt.equals2D(nextPt)) {
103979               normalizedSegmentIndex = nextSegIndex;
103980             }
103981           }
103982           var ei = this._nodeList.add(intPt, normalizedSegmentIndex);
103983           return ei
103984         };
103985         NodedSegmentString.prototype.addIntersections = function addIntersections (li, segmentIndex, geomIndex) {
103986             var this$1 = this;
103987
103988           for (var i = 0; i < li.getIntersectionNum(); i++) {
103989             this$1.addIntersection(li, segmentIndex, geomIndex, i);
103990           }
103991         };
103992         NodedSegmentString.prototype.interfaces_ = function interfaces_ () {
103993           return [NodableSegmentString]
103994         };
103995         NodedSegmentString.prototype.getClass = function getClass () {
103996           return NodedSegmentString
103997         };
103998         NodedSegmentString.getNodedSubstrings = function getNodedSubstrings () {
103999           if (arguments.length === 1) {
104000             var segStrings = arguments[0];
104001             var resultEdgelist = new ArrayList();
104002             NodedSegmentString.getNodedSubstrings(segStrings, resultEdgelist);
104003             return resultEdgelist
104004           } else if (arguments.length === 2) {
104005             var segStrings$1 = arguments[0];
104006             var resultEdgelist$1 = arguments[1];
104007             for (var i = segStrings$1.iterator(); i.hasNext();) {
104008               var ss = i.next();
104009               ss.getNodeList().addSplitEdges(resultEdgelist$1);
104010             }
104011           }
104012         };
104013
104014         var LineSegment = function LineSegment () {
104015           this.p0 = null;
104016           this.p1 = null;
104017           if (arguments.length === 0) {
104018             this.p0 = new Coordinate();
104019             this.p1 = new Coordinate();
104020           } else if (arguments.length === 1) {
104021             var ls = arguments[0];
104022             this.p0 = new Coordinate(ls.p0);
104023             this.p1 = new Coordinate(ls.p1);
104024           } else if (arguments.length === 2) {
104025             this.p0 = arguments[0];
104026             this.p1 = arguments[1];
104027           } else if (arguments.length === 4) {
104028             var x0 = arguments[0];
104029             var y0 = arguments[1];
104030             var x1 = arguments[2];
104031             var y1 = arguments[3];
104032             this.p0 = new Coordinate(x0, y0);
104033             this.p1 = new Coordinate(x1, y1);
104034           }
104035         };
104036
104037         var staticAccessors$24 = { serialVersionUID: { configurable: true } };
104038         LineSegment.prototype.minX = function minX () {
104039           return Math.min(this.p0.x, this.p1.x)
104040         };
104041         LineSegment.prototype.orientationIndex = function orientationIndex () {
104042           if (arguments[0] instanceof LineSegment) {
104043             var seg = arguments[0];
104044             var orient0 = CGAlgorithms.orientationIndex(this.p0, this.p1, seg.p0);
104045             var orient1 = CGAlgorithms.orientationIndex(this.p0, this.p1, seg.p1);
104046             if (orient0 >= 0 && orient1 >= 0) { return Math.max(orient0, orient1) }
104047             if (orient0 <= 0 && orient1 <= 0) { return Math.max(orient0, orient1) }
104048             return 0
104049           } else if (arguments[0] instanceof Coordinate) {
104050             var p = arguments[0];
104051             return CGAlgorithms.orientationIndex(this.p0, this.p1, p)
104052           }
104053         };
104054         LineSegment.prototype.toGeometry = function toGeometry (geomFactory) {
104055           return geomFactory.createLineString([this.p0, this.p1])
104056         };
104057         LineSegment.prototype.isVertical = function isVertical () {
104058           return this.p0.x === this.p1.x
104059         };
104060         LineSegment.prototype.equals = function equals (o) {
104061           if (!(o instanceof LineSegment)) {
104062             return false
104063           }
104064           var other = o;
104065           return this.p0.equals(other.p0) && this.p1.equals(other.p1)
104066         };
104067         LineSegment.prototype.intersection = function intersection (line) {
104068           var li = new RobustLineIntersector();
104069           li.computeIntersection(this.p0, this.p1, line.p0, line.p1);
104070           if (li.hasIntersection()) { return li.getIntersection(0) }
104071           return null
104072         };
104073         LineSegment.prototype.project = function project () {
104074           if (arguments[0] instanceof Coordinate) {
104075             var p = arguments[0];
104076             if (p.equals(this.p0) || p.equals(this.p1)) { return new Coordinate(p) }
104077             var r = this.projectionFactor(p);
104078             var coord = new Coordinate();
104079             coord.x = this.p0.x + r * (this.p1.x - this.p0.x);
104080             coord.y = this.p0.y + r * (this.p1.y - this.p0.y);
104081             return coord
104082           } else if (arguments[0] instanceof LineSegment) {
104083             var seg = arguments[0];
104084             var pf0 = this.projectionFactor(seg.p0);
104085             var pf1 = this.projectionFactor(seg.p1);
104086             if (pf0 >= 1.0 && pf1 >= 1.0) { return null }
104087             if (pf0 <= 0.0 && pf1 <= 0.0) { return null }
104088             var newp0 = this.project(seg.p0);
104089             if (pf0 < 0.0) { newp0 = this.p0; }
104090             if (pf0 > 1.0) { newp0 = this.p1; }
104091             var newp1 = this.project(seg.p1);
104092             if (pf1 < 0.0) { newp1 = this.p0; }
104093             if (pf1 > 1.0) { newp1 = this.p1; }
104094             return new LineSegment(newp0, newp1)
104095           }
104096         };
104097         LineSegment.prototype.normalize = function normalize () {
104098           if (this.p1.compareTo(this.p0) < 0) { this.reverse(); }
104099         };
104100         LineSegment.prototype.angle = function angle () {
104101           return Math.atan2(this.p1.y - this.p0.y, this.p1.x - this.p0.x)
104102         };
104103         LineSegment.prototype.getCoordinate = function getCoordinate (i) {
104104           if (i === 0) { return this.p0 }
104105           return this.p1
104106         };
104107         LineSegment.prototype.distancePerpendicular = function distancePerpendicular (p) {
104108           return CGAlgorithms.distancePointLinePerpendicular(p, this.p0, this.p1)
104109         };
104110         LineSegment.prototype.minY = function minY () {
104111           return Math.min(this.p0.y, this.p1.y)
104112         };
104113         LineSegment.prototype.midPoint = function midPoint () {
104114           return LineSegment.midPoint(this.p0, this.p1)
104115         };
104116         LineSegment.prototype.projectionFactor = function projectionFactor (p) {
104117           if (p.equals(this.p0)) { return 0.0 }
104118           if (p.equals(this.p1)) { return 1.0 }
104119           var dx = this.p1.x - this.p0.x;
104120           var dy = this.p1.y - this.p0.y;
104121           var len = dx * dx + dy * dy;
104122           if (len <= 0.0) { return Double.NaN }
104123           var r = ((p.x - this.p0.x) * dx + (p.y - this.p0.y) * dy) / len;
104124           return r
104125         };
104126         LineSegment.prototype.closestPoints = function closestPoints (line) {
104127           var intPt = this.intersection(line);
104128           if (intPt !== null) {
104129             return [intPt, intPt]
104130           }
104131           var closestPt = new Array(2).fill(null);
104132           var minDistance = Double.MAX_VALUE;
104133           var dist = null;
104134           var close00 = this.closestPoint(line.p0);
104135           minDistance = close00.distance(line.p0);
104136           closestPt[0] = close00;
104137           closestPt[1] = line.p0;
104138           var close01 = this.closestPoint(line.p1);
104139           dist = close01.distance(line.p1);
104140           if (dist < minDistance) {
104141             minDistance = dist;
104142             closestPt[0] = close01;
104143             closestPt[1] = line.p1;
104144           }
104145           var close10 = line.closestPoint(this.p0);
104146           dist = close10.distance(this.p0);
104147           if (dist < minDistance) {
104148             minDistance = dist;
104149             closestPt[0] = this.p0;
104150             closestPt[1] = close10;
104151           }
104152           var close11 = line.closestPoint(this.p1);
104153           dist = close11.distance(this.p1);
104154           if (dist < minDistance) {
104155             minDistance = dist;
104156             closestPt[0] = this.p1;
104157             closestPt[1] = close11;
104158           }
104159           return closestPt
104160         };
104161         LineSegment.prototype.closestPoint = function closestPoint (p) {
104162           var factor = this.projectionFactor(p);
104163           if (factor > 0 && factor < 1) {
104164             return this.project(p)
104165           }
104166           var dist0 = this.p0.distance(p);
104167           var dist1 = this.p1.distance(p);
104168           if (dist0 < dist1) { return this.p0 }
104169           return this.p1
104170         };
104171         LineSegment.prototype.maxX = function maxX () {
104172           return Math.max(this.p0.x, this.p1.x)
104173         };
104174         LineSegment.prototype.getLength = function getLength () {
104175           return this.p0.distance(this.p1)
104176         };
104177         LineSegment.prototype.compareTo = function compareTo (o) {
104178           var other = o;
104179           var comp0 = this.p0.compareTo(other.p0);
104180           if (comp0 !== 0) { return comp0 }
104181           return this.p1.compareTo(other.p1)
104182         };
104183         LineSegment.prototype.reverse = function reverse () {
104184           var temp = this.p0;
104185           this.p0 = this.p1;
104186           this.p1 = temp;
104187         };
104188         LineSegment.prototype.equalsTopo = function equalsTopo (other) {
104189           return this.p0.equals(other.p0) &&
104190                 (this.p1.equals(other.p1) || this.p0.equals(other.p1)) &&
104191                  this.p1.equals(other.p0)
104192         };
104193         LineSegment.prototype.lineIntersection = function lineIntersection (line) {
104194           try {
104195             var intPt = HCoordinate.intersection(this.p0, this.p1, line.p0, line.p1);
104196             return intPt
104197           } catch (ex) {
104198             if (ex instanceof NotRepresentableException) ; else { throw ex }
104199           } finally {}
104200           return null
104201         };
104202         LineSegment.prototype.maxY = function maxY () {
104203           return Math.max(this.p0.y, this.p1.y)
104204         };
104205         LineSegment.prototype.pointAlongOffset = function pointAlongOffset (segmentLengthFraction, offsetDistance) {
104206           var segx = this.p0.x + segmentLengthFraction * (this.p1.x - this.p0.x);
104207           var segy = this.p0.y + segmentLengthFraction * (this.p1.y - this.p0.y);
104208           var dx = this.p1.x - this.p0.x;
104209           var dy = this.p1.y - this.p0.y;
104210           var len = Math.sqrt(dx * dx + dy * dy);
104211           var ux = 0.0;
104212           var uy = 0.0;
104213           if (offsetDistance !== 0.0) {
104214             if (len <= 0.0) { throw new Error('Cannot compute offset from zero-length line segment') }
104215             ux = offsetDistance * dx / len;
104216             uy = offsetDistance * dy / len;
104217           }
104218           var offsetx = segx - uy;
104219           var offsety = segy + ux;
104220           var coord = new Coordinate(offsetx, offsety);
104221           return coord
104222         };
104223         LineSegment.prototype.setCoordinates = function setCoordinates () {
104224           if (arguments.length === 1) {
104225             var ls = arguments[0];
104226             this.setCoordinates(ls.p0, ls.p1);
104227           } else if (arguments.length === 2) {
104228             var p0 = arguments[0];
104229             var p1 = arguments[1];
104230             this.p0.x = p0.x;
104231             this.p0.y = p0.y;
104232             this.p1.x = p1.x;
104233             this.p1.y = p1.y;
104234           }
104235         };
104236         LineSegment.prototype.segmentFraction = function segmentFraction (inputPt) {
104237           var segFrac = this.projectionFactor(inputPt);
104238           if (segFrac < 0.0) { segFrac = 0.0; } else if (segFrac > 1.0 || Double.isNaN(segFrac)) { segFrac = 1.0; }
104239           return segFrac
104240         };
104241         LineSegment.prototype.toString = function toString () {
104242           return 'LINESTRING( ' + this.p0.x + ' ' + this.p0.y + ', ' + this.p1.x + ' ' + this.p1.y + ')'
104243         };
104244         LineSegment.prototype.isHorizontal = function isHorizontal () {
104245           return this.p0.y === this.p1.y
104246         };
104247         LineSegment.prototype.distance = function distance () {
104248           if (arguments[0] instanceof LineSegment) {
104249             var ls = arguments[0];
104250             return CGAlgorithms.distanceLineLine(this.p0, this.p1, ls.p0, ls.p1)
104251           } else if (arguments[0] instanceof Coordinate) {
104252             var p = arguments[0];
104253             return CGAlgorithms.distancePointLine(p, this.p0, this.p1)
104254           }
104255         };
104256         LineSegment.prototype.pointAlong = function pointAlong (segmentLengthFraction) {
104257           var coord = new Coordinate();
104258           coord.x = this.p0.x + segmentLengthFraction * (this.p1.x - this.p0.x);
104259           coord.y = this.p0.y + segmentLengthFraction * (this.p1.y - this.p0.y);
104260           return coord
104261         };
104262         LineSegment.prototype.hashCode = function hashCode () {
104263           var bits0 = Double.doubleToLongBits(this.p0.x);
104264           bits0 ^= Double.doubleToLongBits(this.p0.y) * 31;
104265           var hash0 = Math.trunc(bits0) ^ Math.trunc(bits0 >> 32);
104266           var bits1 = Double.doubleToLongBits(this.p1.x);
104267           bits1 ^= Double.doubleToLongBits(this.p1.y) * 31;
104268           var hash1 = Math.trunc(bits1) ^ Math.trunc(bits1 >> 32);
104269           return hash0 ^ hash1
104270         };
104271         LineSegment.prototype.interfaces_ = function interfaces_ () {
104272           return [Comparable, Serializable]
104273         };
104274         LineSegment.prototype.getClass = function getClass () {
104275           return LineSegment
104276         };
104277         LineSegment.midPoint = function midPoint (p0, p1) {
104278           return new Coordinate((p0.x + p1.x) / 2, (p0.y + p1.y) / 2)
104279         };
104280         staticAccessors$24.serialVersionUID.get = function () { return 3252005833466256227 };
104281
104282         Object.defineProperties( LineSegment, staticAccessors$24 );
104283
104284         var MonotoneChainOverlapAction = function MonotoneChainOverlapAction () {
104285           this.tempEnv1 = new Envelope();
104286           this.tempEnv2 = new Envelope();
104287           this._overlapSeg1 = new LineSegment();
104288           this._overlapSeg2 = new LineSegment();
104289         };
104290         MonotoneChainOverlapAction.prototype.overlap = function overlap () {
104291           if (arguments.length === 2) ; else if (arguments.length === 4) {
104292             var mc1 = arguments[0];
104293             var start1 = arguments[1];
104294             var mc2 = arguments[2];
104295             var start2 = arguments[3];
104296             mc1.getLineSegment(start1, this._overlapSeg1);
104297             mc2.getLineSegment(start2, this._overlapSeg2);
104298             this.overlap(this._overlapSeg1, this._overlapSeg2);
104299           }
104300         };
104301         MonotoneChainOverlapAction.prototype.interfaces_ = function interfaces_ () {
104302           return []
104303         };
104304         MonotoneChainOverlapAction.prototype.getClass = function getClass () {
104305           return MonotoneChainOverlapAction
104306         };
104307
104308         var MonotoneChain = function MonotoneChain () {
104309           this._pts = null;
104310           this._start = null;
104311           this._end = null;
104312           this._env = null;
104313           this._context = null;
104314           this._id = null;
104315           var pts = arguments[0];
104316           var start = arguments[1];
104317           var end = arguments[2];
104318           var context = arguments[3];
104319           this._pts = pts;
104320           this._start = start;
104321           this._end = end;
104322           this._context = context;
104323         };
104324         MonotoneChain.prototype.getLineSegment = function getLineSegment (index, ls) {
104325           ls.p0 = this._pts[index];
104326           ls.p1 = this._pts[index + 1];
104327         };
104328         MonotoneChain.prototype.computeSelect = function computeSelect (searchEnv, start0, end0, mcs) {
104329           var p0 = this._pts[start0];
104330           var p1 = this._pts[end0];
104331           mcs.tempEnv1.init(p0, p1);
104332           if (end0 - start0 === 1) {
104333             mcs.select(this, start0);
104334             return null
104335           }
104336           if (!searchEnv.intersects(mcs.tempEnv1)) { return null }
104337           var mid = Math.trunc((start0 + end0) / 2);
104338           if (start0 < mid) {
104339             this.computeSelect(searchEnv, start0, mid, mcs);
104340           }
104341           if (mid < end0) {
104342             this.computeSelect(searchEnv, mid, end0, mcs);
104343           }
104344         };
104345         MonotoneChain.prototype.getCoordinates = function getCoordinates () {
104346             var this$1 = this;
104347
104348           var coord = new Array(this._end - this._start + 1).fill(null);
104349           var index = 0;
104350           for (var i = this._start; i <= this._end; i++) {
104351             coord[index++] = this$1._pts[i];
104352           }
104353           return coord
104354         };
104355         MonotoneChain.prototype.computeOverlaps = function computeOverlaps (mc, mco) {
104356           this.computeOverlapsInternal(this._start, this._end, mc, mc._start, mc._end, mco);
104357         };
104358         MonotoneChain.prototype.setId = function setId (id) {
104359           this._id = id;
104360         };
104361         MonotoneChain.prototype.select = function select (searchEnv, mcs) {
104362           this.computeSelect(searchEnv, this._start, this._end, mcs);
104363         };
104364         MonotoneChain.prototype.getEnvelope = function getEnvelope () {
104365           if (this._env === null) {
104366             var p0 = this._pts[this._start];
104367             var p1 = this._pts[this._end];
104368             this._env = new Envelope(p0, p1);
104369           }
104370           return this._env
104371         };
104372         MonotoneChain.prototype.getEndIndex = function getEndIndex () {
104373           return this._end
104374         };
104375         MonotoneChain.prototype.getStartIndex = function getStartIndex () {
104376           return this._start
104377         };
104378         MonotoneChain.prototype.getContext = function getContext () {
104379           return this._context
104380         };
104381         MonotoneChain.prototype.getId = function getId () {
104382           return this._id
104383         };
104384         MonotoneChain.prototype.computeOverlapsInternal = function computeOverlapsInternal (start0, end0, mc, start1, end1, mco) {
104385           var p00 = this._pts[start0];
104386           var p01 = this._pts[end0];
104387           var p10 = mc._pts[start1];
104388           var p11 = mc._pts[end1];
104389           if (end0 - start0 === 1 && end1 - start1 === 1) {
104390             mco.overlap(this, start0, mc, start1);
104391             return null
104392           }
104393           mco.tempEnv1.init(p00, p01);
104394           mco.tempEnv2.init(p10, p11);
104395           if (!mco.tempEnv1.intersects(mco.tempEnv2)) { return null }
104396           var mid0 = Math.trunc((start0 + end0) / 2);
104397           var mid1 = Math.trunc((start1 + end1) / 2);
104398           if (start0 < mid0) {
104399             if (start1 < mid1) { this.computeOverlapsInternal(start0, mid0, mc, start1, mid1, mco); }
104400             if (mid1 < end1) { this.computeOverlapsInternal(start0, mid0, mc, mid1, end1, mco); }
104401           }
104402           if (mid0 < end0) {
104403             if (start1 < mid1) { this.computeOverlapsInternal(mid0, end0, mc, start1, mid1, mco); }
104404             if (mid1 < end1) { this.computeOverlapsInternal(mid0, end0, mc, mid1, end1, mco); }
104405           }
104406         };
104407         MonotoneChain.prototype.interfaces_ = function interfaces_ () {
104408           return []
104409         };
104410         MonotoneChain.prototype.getClass = function getClass () {
104411           return MonotoneChain
104412         };
104413
104414         var MonotoneChainBuilder = function MonotoneChainBuilder () {};
104415
104416         MonotoneChainBuilder.prototype.interfaces_ = function interfaces_ () {
104417           return []
104418         };
104419         MonotoneChainBuilder.prototype.getClass = function getClass () {
104420           return MonotoneChainBuilder
104421         };
104422         MonotoneChainBuilder.getChainStartIndices = function getChainStartIndices (pts) {
104423           var start = 0;
104424           var startIndexList = new ArrayList();
104425           startIndexList.add(new Integer(start));
104426           do {
104427             var last = MonotoneChainBuilder.findChainEnd(pts, start);
104428             startIndexList.add(new Integer(last));
104429             start = last;
104430           } while (start < pts.length - 1)
104431           var startIndex = MonotoneChainBuilder.toIntArray(startIndexList);
104432           return startIndex
104433         };
104434         MonotoneChainBuilder.findChainEnd = function findChainEnd (pts, start) {
104435           var safeStart = start;
104436           while (safeStart < pts.length - 1 && pts[safeStart].equals2D(pts[safeStart + 1])) {
104437             safeStart++;
104438           }
104439           if (safeStart >= pts.length - 1) {
104440             return pts.length - 1
104441           }
104442           var chainQuad = Quadrant.quadrant(pts[safeStart], pts[safeStart + 1]);
104443           var last = start + 1;
104444           while (last < pts.length) {
104445             if (!pts[last - 1].equals2D(pts[last])) {
104446               var quad = Quadrant.quadrant(pts[last - 1], pts[last]);
104447               if (quad !== chainQuad) { break }
104448             }
104449             last++;
104450           }
104451           return last - 1
104452         };
104453         MonotoneChainBuilder.getChains = function getChains () {
104454           if (arguments.length === 1) {
104455             var pts = arguments[0];
104456             return MonotoneChainBuilder.getChains(pts, null)
104457           } else if (arguments.length === 2) {
104458             var pts$1 = arguments[0];
104459             var context = arguments[1];
104460             var mcList = new ArrayList();
104461             var startIndex = MonotoneChainBuilder.getChainStartIndices(pts$1);
104462             for (var i = 0; i < startIndex.length - 1; i++) {
104463               var mc = new MonotoneChain(pts$1, startIndex[i], startIndex[i + 1], context);
104464               mcList.add(mc);
104465             }
104466             return mcList
104467           }
104468         };
104469         MonotoneChainBuilder.toIntArray = function toIntArray (list) {
104470           var array = new Array(list.size()).fill(null);
104471           for (var i = 0; i < array.length; i++) {
104472             array[i] = list.get(i).intValue();
104473           }
104474           return array
104475         };
104476
104477         var Noder = function Noder () {};
104478
104479         Noder.prototype.computeNodes = function computeNodes (segStrings) {};
104480         Noder.prototype.getNodedSubstrings = function getNodedSubstrings () {};
104481         Noder.prototype.interfaces_ = function interfaces_ () {
104482           return []
104483         };
104484         Noder.prototype.getClass = function getClass () {
104485           return Noder
104486         };
104487
104488         var SinglePassNoder = function SinglePassNoder () {
104489           this._segInt = null;
104490           if (arguments.length === 0) ; else if (arguments.length === 1) {
104491             var segInt = arguments[0];
104492             this.setSegmentIntersector(segInt);
104493           }
104494         };
104495         SinglePassNoder.prototype.setSegmentIntersector = function setSegmentIntersector (segInt) {
104496           this._segInt = segInt;
104497         };
104498         SinglePassNoder.prototype.interfaces_ = function interfaces_ () {
104499           return [Noder]
104500         };
104501         SinglePassNoder.prototype.getClass = function getClass () {
104502           return SinglePassNoder
104503         };
104504
104505         var MCIndexNoder = (function (SinglePassNoder$$1) {
104506           function MCIndexNoder (si) {
104507             if (si) { SinglePassNoder$$1.call(this, si); }
104508             else { SinglePassNoder$$1.call(this); }
104509             this._monoChains = new ArrayList();
104510             this._index = new STRtree();
104511             this._idCounter = 0;
104512             this._nodedSegStrings = null;
104513             this._nOverlaps = 0;
104514           }
104515
104516           if ( SinglePassNoder$$1 ) MCIndexNoder.__proto__ = SinglePassNoder$$1;
104517           MCIndexNoder.prototype = Object.create( SinglePassNoder$$1 && SinglePassNoder$$1.prototype );
104518           MCIndexNoder.prototype.constructor = MCIndexNoder;
104519
104520           var staticAccessors = { SegmentOverlapAction: { configurable: true } };
104521           MCIndexNoder.prototype.getMonotoneChains = function getMonotoneChains () {
104522             return this._monoChains
104523           };
104524           MCIndexNoder.prototype.getNodedSubstrings = function getNodedSubstrings () {
104525             return NodedSegmentString.getNodedSubstrings(this._nodedSegStrings)
104526           };
104527           MCIndexNoder.prototype.getIndex = function getIndex () {
104528             return this._index
104529           };
104530           MCIndexNoder.prototype.add = function add (segStr) {
104531             var this$1 = this;
104532
104533             var segChains = MonotoneChainBuilder.getChains(segStr.getCoordinates(), segStr);
104534             for (var i = segChains.iterator(); i.hasNext();) {
104535               var mc = i.next();
104536               mc.setId(this$1._idCounter++);
104537               this$1._index.insert(mc.getEnvelope(), mc);
104538               this$1._monoChains.add(mc);
104539             }
104540           };
104541           MCIndexNoder.prototype.computeNodes = function computeNodes (inputSegStrings) {
104542             var this$1 = this;
104543
104544             this._nodedSegStrings = inputSegStrings;
104545             for (var i = inputSegStrings.iterator(); i.hasNext();) {
104546               this$1.add(i.next());
104547             }
104548             this.intersectChains();
104549           };
104550           MCIndexNoder.prototype.intersectChains = function intersectChains () {
104551             var this$1 = this;
104552
104553             var overlapAction = new SegmentOverlapAction(this._segInt);
104554             for (var i = this._monoChains.iterator(); i.hasNext();) {
104555               var queryChain = i.next();
104556               var overlapChains = this$1._index.query(queryChain.getEnvelope());
104557               for (var j = overlapChains.iterator(); j.hasNext();) {
104558                 var testChain = j.next();
104559                 if (testChain.getId() > queryChain.getId()) {
104560                   queryChain.computeOverlaps(testChain, overlapAction);
104561                   this$1._nOverlaps++;
104562                 }
104563                 if (this$1._segInt.isDone()) { return null }
104564               }
104565             }
104566           };
104567           MCIndexNoder.prototype.interfaces_ = function interfaces_ () {
104568             return []
104569           };
104570           MCIndexNoder.prototype.getClass = function getClass () {
104571             return MCIndexNoder
104572           };
104573           staticAccessors.SegmentOverlapAction.get = function () { return SegmentOverlapAction };
104574
104575           Object.defineProperties( MCIndexNoder, staticAccessors );
104576
104577           return MCIndexNoder;
104578         }(SinglePassNoder));
104579
104580         var SegmentOverlapAction = (function (MonotoneChainOverlapAction$$1) {
104581           function SegmentOverlapAction () {
104582             MonotoneChainOverlapAction$$1.call(this);
104583             this._si = null;
104584             var si = arguments[0];
104585             this._si = si;
104586           }
104587
104588           if ( MonotoneChainOverlapAction$$1 ) SegmentOverlapAction.__proto__ = MonotoneChainOverlapAction$$1;
104589           SegmentOverlapAction.prototype = Object.create( MonotoneChainOverlapAction$$1 && MonotoneChainOverlapAction$$1.prototype );
104590           SegmentOverlapAction.prototype.constructor = SegmentOverlapAction;
104591           SegmentOverlapAction.prototype.overlap = function overlap () {
104592             if (arguments.length === 4) {
104593               var mc1 = arguments[0];
104594               var start1 = arguments[1];
104595               var mc2 = arguments[2];
104596               var start2 = arguments[3];
104597               var ss1 = mc1.getContext();
104598               var ss2 = mc2.getContext();
104599               this._si.processIntersections(ss1, start1, ss2, start2);
104600             } else { return MonotoneChainOverlapAction$$1.prototype.overlap.apply(this, arguments) }
104601           };
104602           SegmentOverlapAction.prototype.interfaces_ = function interfaces_ () {
104603             return []
104604           };
104605           SegmentOverlapAction.prototype.getClass = function getClass () {
104606             return SegmentOverlapAction
104607           };
104608
104609           return SegmentOverlapAction;
104610         }(MonotoneChainOverlapAction));
104611
104612         var BufferParameters = function BufferParameters () {
104613           this._quadrantSegments = BufferParameters.DEFAULT_QUADRANT_SEGMENTS;
104614           this._endCapStyle = BufferParameters.CAP_ROUND;
104615           this._joinStyle = BufferParameters.JOIN_ROUND;
104616           this._mitreLimit = BufferParameters.DEFAULT_MITRE_LIMIT;
104617           this._isSingleSided = false;
104618           this._simplifyFactor = BufferParameters.DEFAULT_SIMPLIFY_FACTOR;
104619
104620           if (arguments.length === 0) ; else if (arguments.length === 1) {
104621             var quadrantSegments = arguments[0];
104622             this.setQuadrantSegments(quadrantSegments);
104623           } else if (arguments.length === 2) {
104624             var quadrantSegments$1 = arguments[0];
104625             var endCapStyle = arguments[1];
104626             this.setQuadrantSegments(quadrantSegments$1);
104627             this.setEndCapStyle(endCapStyle);
104628           } else if (arguments.length === 4) {
104629             var quadrantSegments$2 = arguments[0];
104630             var endCapStyle$1 = arguments[1];
104631             var joinStyle = arguments[2];
104632             var mitreLimit = arguments[3];
104633             this.setQuadrantSegments(quadrantSegments$2);
104634             this.setEndCapStyle(endCapStyle$1);
104635             this.setJoinStyle(joinStyle);
104636             this.setMitreLimit(mitreLimit);
104637           }
104638         };
104639
104640         var staticAccessors$25 = { CAP_ROUND: { configurable: true },CAP_FLAT: { configurable: true },CAP_SQUARE: { configurable: true },JOIN_ROUND: { configurable: true },JOIN_MITRE: { configurable: true },JOIN_BEVEL: { configurable: true },DEFAULT_QUADRANT_SEGMENTS: { configurable: true },DEFAULT_MITRE_LIMIT: { configurable: true },DEFAULT_SIMPLIFY_FACTOR: { configurable: true } };
104641         BufferParameters.prototype.getEndCapStyle = function getEndCapStyle () {
104642           return this._endCapStyle
104643         };
104644         BufferParameters.prototype.isSingleSided = function isSingleSided () {
104645           return this._isSingleSided
104646         };
104647         BufferParameters.prototype.setQuadrantSegments = function setQuadrantSegments (quadSegs) {
104648           this._quadrantSegments = quadSegs;
104649           if (this._quadrantSegments === 0) { this._joinStyle = BufferParameters.JOIN_BEVEL; }
104650           if (this._quadrantSegments < 0) {
104651             this._joinStyle = BufferParameters.JOIN_MITRE;
104652             this._mitreLimit = Math.abs(this._quadrantSegments);
104653           }
104654           if (quadSegs <= 0) {
104655             this._quadrantSegments = 1;
104656           }
104657           if (this._joinStyle !== BufferParameters.JOIN_ROUND) {
104658             this._quadrantSegments = BufferParameters.DEFAULT_QUADRANT_SEGMENTS;
104659           }
104660         };
104661         BufferParameters.prototype.getJoinStyle = function getJoinStyle () {
104662           return this._joinStyle
104663         };
104664         BufferParameters.prototype.setJoinStyle = function setJoinStyle (joinStyle) {
104665           this._joinStyle = joinStyle;
104666         };
104667         BufferParameters.prototype.setSimplifyFactor = function setSimplifyFactor (simplifyFactor) {
104668           this._simplifyFactor = simplifyFactor < 0 ? 0 : simplifyFactor;
104669         };
104670         BufferParameters.prototype.getSimplifyFactor = function getSimplifyFactor () {
104671           return this._simplifyFactor
104672         };
104673         BufferParameters.prototype.getQuadrantSegments = function getQuadrantSegments () {
104674           return this._quadrantSegments
104675         };
104676         BufferParameters.prototype.setEndCapStyle = function setEndCapStyle (endCapStyle) {
104677           this._endCapStyle = endCapStyle;
104678         };
104679         BufferParameters.prototype.getMitreLimit = function getMitreLimit () {
104680           return this._mitreLimit
104681         };
104682         BufferParameters.prototype.setMitreLimit = function setMitreLimit (mitreLimit) {
104683           this._mitreLimit = mitreLimit;
104684         };
104685         BufferParameters.prototype.setSingleSided = function setSingleSided (isSingleSided) {
104686           this._isSingleSided = isSingleSided;
104687         };
104688         BufferParameters.prototype.interfaces_ = function interfaces_ () {
104689           return []
104690         };
104691         BufferParameters.prototype.getClass = function getClass () {
104692           return BufferParameters
104693         };
104694         BufferParameters.bufferDistanceError = function bufferDistanceError (quadSegs) {
104695           var alpha = Math.PI / 2.0 / quadSegs;
104696           return 1 - Math.cos(alpha / 2.0)
104697         };
104698         staticAccessors$25.CAP_ROUND.get = function () { return 1 };
104699         staticAccessors$25.CAP_FLAT.get = function () { return 2 };
104700         staticAccessors$25.CAP_SQUARE.get = function () { return 3 };
104701         staticAccessors$25.JOIN_ROUND.get = function () { return 1 };
104702         staticAccessors$25.JOIN_MITRE.get = function () { return 2 };
104703         staticAccessors$25.JOIN_BEVEL.get = function () { return 3 };
104704         staticAccessors$25.DEFAULT_QUADRANT_SEGMENTS.get = function () { return 8 };
104705         staticAccessors$25.DEFAULT_MITRE_LIMIT.get = function () { return 5.0 };
104706         staticAccessors$25.DEFAULT_SIMPLIFY_FACTOR.get = function () { return 0.01 };
104707
104708         Object.defineProperties( BufferParameters, staticAccessors$25 );
104709
104710         var BufferInputLineSimplifier = function BufferInputLineSimplifier (inputLine) {
104711           this._distanceTol = null;
104712           this._isDeleted = null;
104713           this._angleOrientation = CGAlgorithms.COUNTERCLOCKWISE;
104714           this._inputLine = inputLine || null;
104715         };
104716
104717         var staticAccessors$26 = { INIT: { configurable: true },DELETE: { configurable: true },KEEP: { configurable: true },NUM_PTS_TO_CHECK: { configurable: true } };
104718         BufferInputLineSimplifier.prototype.isDeletable = function isDeletable (i0, i1, i2, distanceTol) {
104719           var p0 = this._inputLine[i0];
104720           var p1 = this._inputLine[i1];
104721           var p2 = this._inputLine[i2];
104722           if (!this.isConcave(p0, p1, p2)) { return false }
104723           if (!this.isShallow(p0, p1, p2, distanceTol)) { return false }
104724           return this.isShallowSampled(p0, p1, i0, i2, distanceTol)
104725         };
104726         BufferInputLineSimplifier.prototype.deleteShallowConcavities = function deleteShallowConcavities () {
104727             var this$1 = this;
104728
104729           var index = 1;
104730           // const maxIndex = this._inputLine.length - 1
104731           var midIndex = this.findNextNonDeletedIndex(index);
104732           var lastIndex = this.findNextNonDeletedIndex(midIndex);
104733           var isChanged = false;
104734           while (lastIndex < this._inputLine.length) {
104735             var isMiddleVertexDeleted = false;
104736             if (this$1.isDeletable(index, midIndex, lastIndex, this$1._distanceTol)) {
104737               this$1._isDeleted[midIndex] = BufferInputLineSimplifier.DELETE;
104738               isMiddleVertexDeleted = true;
104739               isChanged = true;
104740             }
104741             if (isMiddleVertexDeleted) { index = lastIndex; } else { index = midIndex; }
104742             midIndex = this$1.findNextNonDeletedIndex(index);
104743             lastIndex = this$1.findNextNonDeletedIndex(midIndex);
104744           }
104745           return isChanged
104746         };
104747         BufferInputLineSimplifier.prototype.isShallowConcavity = function isShallowConcavity (p0, p1, p2, distanceTol) {
104748           var orientation = CGAlgorithms.computeOrientation(p0, p1, p2);
104749           var isAngleToSimplify = orientation === this._angleOrientation;
104750           if (!isAngleToSimplify) { return false }
104751           var dist = CGAlgorithms.distancePointLine(p1, p0, p2);
104752           return dist < distanceTol
104753         };
104754         BufferInputLineSimplifier.prototype.isShallowSampled = function isShallowSampled (p0, p2, i0, i2, distanceTol) {
104755             var this$1 = this;
104756
104757           var inc = Math.trunc((i2 - i0) / BufferInputLineSimplifier.NUM_PTS_TO_CHECK);
104758           if (inc <= 0) { inc = 1; }
104759           for (var i = i0; i < i2; i += inc) {
104760             if (!this$1.isShallow(p0, p2, this$1._inputLine[i], distanceTol)) { return false }
104761           }
104762           return true
104763         };
104764         BufferInputLineSimplifier.prototype.isConcave = function isConcave (p0, p1, p2) {
104765           var orientation = CGAlgorithms.computeOrientation(p0, p1, p2);
104766           var isConcave = orientation === this._angleOrientation;
104767           return isConcave
104768         };
104769         BufferInputLineSimplifier.prototype.simplify = function simplify (distanceTol) {
104770             var this$1 = this;
104771
104772           this._distanceTol = Math.abs(distanceTol);
104773           if (distanceTol < 0) { this._angleOrientation = CGAlgorithms.CLOCKWISE; }
104774           this._isDeleted = new Array(this._inputLine.length).fill(null);
104775           var isChanged = false;
104776           do {
104777             isChanged = this$1.deleteShallowConcavities();
104778           } while (isChanged)
104779           return this.collapseLine()
104780         };
104781         BufferInputLineSimplifier.prototype.findNextNonDeletedIndex = function findNextNonDeletedIndex (index) {
104782           var next = index + 1;
104783           while (next < this._inputLine.length && this._isDeleted[next] === BufferInputLineSimplifier.DELETE) { next++; }
104784           return next
104785         };
104786         BufferInputLineSimplifier.prototype.isShallow = function isShallow (p0, p1, p2, distanceTol) {
104787           var dist = CGAlgorithms.distancePointLine(p1, p0, p2);
104788           return dist < distanceTol
104789         };
104790         BufferInputLineSimplifier.prototype.collapseLine = function collapseLine () {
104791             var this$1 = this;
104792
104793           var coordList = new CoordinateList();
104794           for (var i = 0; i < this._inputLine.length; i++) {
104795             if (this$1._isDeleted[i] !== BufferInputLineSimplifier.DELETE) { coordList.add(this$1._inputLine[i]); }
104796           }
104797           return coordList.toCoordinateArray()
104798         };
104799         BufferInputLineSimplifier.prototype.interfaces_ = function interfaces_ () {
104800           return []
104801         };
104802         BufferInputLineSimplifier.prototype.getClass = function getClass () {
104803           return BufferInputLineSimplifier
104804         };
104805         BufferInputLineSimplifier.simplify = function simplify (inputLine, distanceTol) {
104806           var simp = new BufferInputLineSimplifier(inputLine);
104807           return simp.simplify(distanceTol)
104808         };
104809         staticAccessors$26.INIT.get = function () { return 0 };
104810         staticAccessors$26.DELETE.get = function () { return 1 };
104811         staticAccessors$26.KEEP.get = function () { return 1 };
104812         staticAccessors$26.NUM_PTS_TO_CHECK.get = function () { return 10 };
104813
104814         Object.defineProperties( BufferInputLineSimplifier, staticAccessors$26 );
104815
104816         var OffsetSegmentString = function OffsetSegmentString () {
104817           this._ptList = null;
104818           this._precisionModel = null;
104819           this._minimimVertexDistance = 0.0;
104820           this._ptList = new ArrayList();
104821         };
104822
104823         var staticAccessors$28 = { COORDINATE_ARRAY_TYPE: { configurable: true } };
104824         OffsetSegmentString.prototype.getCoordinates = function getCoordinates () {
104825           var coord = this._ptList.toArray(OffsetSegmentString.COORDINATE_ARRAY_TYPE);
104826           return coord
104827         };
104828         OffsetSegmentString.prototype.setPrecisionModel = function setPrecisionModel (precisionModel) {
104829           this._precisionModel = precisionModel;
104830         };
104831         OffsetSegmentString.prototype.addPt = function addPt (pt) {
104832           var bufPt = new Coordinate(pt);
104833           this._precisionModel.makePrecise(bufPt);
104834           if (this.isRedundant(bufPt)) { return null }
104835           this._ptList.add(bufPt);
104836         };
104837         OffsetSegmentString.prototype.revere = function revere () {};
104838         OffsetSegmentString.prototype.addPts = function addPts (pt, isForward) {
104839             var this$1 = this;
104840
104841           if (isForward) {
104842             for (var i = 0; i < pt.length; i++) {
104843               this$1.addPt(pt[i]);
104844             }
104845           } else {
104846             for (var i$1 = pt.length - 1; i$1 >= 0; i$1--) {
104847               this$1.addPt(pt[i$1]);
104848             }
104849           }
104850         };
104851         OffsetSegmentString.prototype.isRedundant = function isRedundant (pt) {
104852           if (this._ptList.size() < 1) { return false }
104853           var lastPt = this._ptList.get(this._ptList.size() - 1);
104854           var ptDist = pt.distance(lastPt);
104855           if (ptDist < this._minimimVertexDistance) { return true }
104856           return false
104857         };
104858         OffsetSegmentString.prototype.toString = function toString () {
104859           var fact = new GeometryFactory();
104860           var line = fact.createLineString(this.getCoordinates());
104861           return line.toString()
104862         };
104863         OffsetSegmentString.prototype.closeRing = function closeRing () {
104864           if (this._ptList.size() < 1) { return null }
104865           var startPt = new Coordinate(this._ptList.get(0));
104866           var lastPt = this._ptList.get(this._ptList.size() - 1);
104867           // const last2Pt = null
104868           // if (this._ptList.size() >= 2) last2Pt = this._ptList.get(this._ptList.size() - 2)
104869           if (startPt.equals(lastPt)) { return null }
104870           this._ptList.add(startPt);
104871         };
104872         OffsetSegmentString.prototype.setMinimumVertexDistance = function setMinimumVertexDistance (minimimVertexDistance) {
104873           this._minimimVertexDistance = minimimVertexDistance;
104874         };
104875         OffsetSegmentString.prototype.interfaces_ = function interfaces_ () {
104876           return []
104877         };
104878         OffsetSegmentString.prototype.getClass = function getClass () {
104879           return OffsetSegmentString
104880         };
104881         staticAccessors$28.COORDINATE_ARRAY_TYPE.get = function () { return new Array(0).fill(null) };
104882
104883         Object.defineProperties( OffsetSegmentString, staticAccessors$28 );
104884
104885         var Angle = function Angle () {};
104886
104887         var staticAccessors$29 = { PI_TIMES_2: { configurable: true },PI_OVER_2: { configurable: true },PI_OVER_4: { configurable: true },COUNTERCLOCKWISE: { configurable: true },CLOCKWISE: { configurable: true },NONE: { configurable: true } };
104888
104889         Angle.prototype.interfaces_ = function interfaces_ () {
104890           return []
104891         };
104892         Angle.prototype.getClass = function getClass () {
104893           return Angle
104894         };
104895         Angle.toDegrees = function toDegrees (radians) {
104896           return radians * 180 / Math.PI
104897         };
104898         Angle.normalize = function normalize (angle) {
104899           while (angle > Math.PI) { angle -= Angle.PI_TIMES_2; }
104900           while (angle <= -Math.PI) { angle += Angle.PI_TIMES_2; }
104901           return angle
104902         };
104903         Angle.angle = function angle () {
104904           if (arguments.length === 1) {
104905             var p = arguments[0];
104906             return Math.atan2(p.y, p.x)
104907           } else if (arguments.length === 2) {
104908             var p0 = arguments[0];
104909             var p1 = arguments[1];
104910             var dx = p1.x - p0.x;
104911             var dy = p1.y - p0.y;
104912             return Math.atan2(dy, dx)
104913           }
104914         };
104915         Angle.isAcute = function isAcute (p0, p1, p2) {
104916           var dx0 = p0.x - p1.x;
104917           var dy0 = p0.y - p1.y;
104918           var dx1 = p2.x - p1.x;
104919           var dy1 = p2.y - p1.y;
104920           var dotprod = dx0 * dx1 + dy0 * dy1;
104921           return dotprod > 0
104922         };
104923         Angle.isObtuse = function isObtuse (p0, p1, p2) {
104924           var dx0 = p0.x - p1.x;
104925           var dy0 = p0.y - p1.y;
104926           var dx1 = p2.x - p1.x;
104927           var dy1 = p2.y - p1.y;
104928           var dotprod = dx0 * dx1 + dy0 * dy1;
104929           return dotprod < 0
104930         };
104931         Angle.interiorAngle = function interiorAngle (p0, p1, p2) {
104932           var anglePrev = Angle.angle(p1, p0);
104933           var angleNext = Angle.angle(p1, p2);
104934           return Math.abs(angleNext - anglePrev)
104935         };
104936         Angle.normalizePositive = function normalizePositive (angle) {
104937           if (angle < 0.0) {
104938             while (angle < 0.0) { angle += Angle.PI_TIMES_2; }
104939             if (angle >= Angle.PI_TIMES_2) { angle = 0.0; }
104940           } else {
104941             while (angle >= Angle.PI_TIMES_2) { angle -= Angle.PI_TIMES_2; }
104942             if (angle < 0.0) { angle = 0.0; }
104943           }
104944           return angle
104945         };
104946         Angle.angleBetween = function angleBetween (tip1, tail, tip2) {
104947           var a1 = Angle.angle(tail, tip1);
104948           var a2 = Angle.angle(tail, tip2);
104949           return Angle.diff(a1, a2)
104950         };
104951         Angle.diff = function diff (ang1, ang2) {
104952           var delAngle = null;
104953           if (ang1 < ang2) {
104954             delAngle = ang2 - ang1;
104955           } else {
104956             delAngle = ang1 - ang2;
104957           }
104958           if (delAngle > Math.PI) {
104959             delAngle = 2 * Math.PI - delAngle;
104960           }
104961           return delAngle
104962         };
104963         Angle.toRadians = function toRadians (angleDegrees) {
104964           return angleDegrees * Math.PI / 180.0
104965         };
104966         Angle.getTurn = function getTurn (ang1, ang2) {
104967           var crossproduct = Math.sin(ang2 - ang1);
104968           if (crossproduct > 0) {
104969             return Angle.COUNTERCLOCKWISE
104970           }
104971           if (crossproduct < 0) {
104972             return Angle.CLOCKWISE
104973           }
104974           return Angle.NONE
104975         };
104976         Angle.angleBetweenOriented = function angleBetweenOriented (tip1, tail, tip2) {
104977           var a1 = Angle.angle(tail, tip1);
104978           var a2 = Angle.angle(tail, tip2);
104979           var angDel = a2 - a1;
104980           if (angDel <= -Math.PI) { return angDel + Angle.PI_TIMES_2 }
104981           if (angDel > Math.PI) { return angDel - Angle.PI_TIMES_2 }
104982           return angDel
104983         };
104984         staticAccessors$29.PI_TIMES_2.get = function () { return 2.0 * Math.PI };
104985         staticAccessors$29.PI_OVER_2.get = function () { return Math.PI / 2.0 };
104986         staticAccessors$29.PI_OVER_4.get = function () { return Math.PI / 4.0 };
104987         staticAccessors$29.COUNTERCLOCKWISE.get = function () { return CGAlgorithms.COUNTERCLOCKWISE };
104988         staticAccessors$29.CLOCKWISE.get = function () { return CGAlgorithms.CLOCKWISE };
104989         staticAccessors$29.NONE.get = function () { return CGAlgorithms.COLLINEAR };
104990
104991         Object.defineProperties( Angle, staticAccessors$29 );
104992
104993         var OffsetSegmentGenerator = function OffsetSegmentGenerator () {
104994           this._maxCurveSegmentError = 0.0;
104995           this._filletAngleQuantum = null;
104996           this._closingSegLengthFactor = 1;
104997           this._segList = null;
104998           this._distance = 0.0;
104999           this._precisionModel = null;
105000           this._bufParams = null;
105001           this._li = null;
105002           this._s0 = null;
105003           this._s1 = null;
105004           this._s2 = null;
105005           this._seg0 = new LineSegment();
105006           this._seg1 = new LineSegment();
105007           this._offset0 = new LineSegment();
105008           this._offset1 = new LineSegment();
105009           this._side = 0;
105010           this._hasNarrowConcaveAngle = false;
105011           var precisionModel = arguments[0];
105012           var bufParams = arguments[1];
105013           var distance = arguments[2];
105014           this._precisionModel = precisionModel;
105015           this._bufParams = bufParams;
105016           this._li = new RobustLineIntersector();
105017           this._filletAngleQuantum = Math.PI / 2.0 / bufParams.getQuadrantSegments();
105018           if (bufParams.getQuadrantSegments() >= 8 && bufParams.getJoinStyle() === BufferParameters.JOIN_ROUND) { this._closingSegLengthFactor = OffsetSegmentGenerator.MAX_CLOSING_SEG_LEN_FACTOR; }
105019           this.init(distance);
105020         };
105021
105022         var staticAccessors$27 = { OFFSET_SEGMENT_SEPARATION_FACTOR: { configurable: true },INSIDE_TURN_VERTEX_SNAP_DISTANCE_FACTOR: { configurable: true },CURVE_VERTEX_SNAP_DISTANCE_FACTOR: { configurable: true },MAX_CLOSING_SEG_LEN_FACTOR: { configurable: true } };
105023         OffsetSegmentGenerator.prototype.addNextSegment = function addNextSegment (p, addStartPoint) {
105024           this._s0 = this._s1;
105025           this._s1 = this._s2;
105026           this._s2 = p;
105027           this._seg0.setCoordinates(this._s0, this._s1);
105028           this.computeOffsetSegment(this._seg0, this._side, this._distance, this._offset0);
105029           this._seg1.setCoordinates(this._s1, this._s2);
105030           this.computeOffsetSegment(this._seg1, this._side, this._distance, this._offset1);
105031           if (this._s1.equals(this._s2)) { return null }
105032           var orientation = CGAlgorithms.computeOrientation(this._s0, this._s1, this._s2);
105033           var outsideTurn = (orientation === CGAlgorithms.CLOCKWISE && this._side === Position.LEFT) || (orientation === CGAlgorithms.COUNTERCLOCKWISE && this._side === Position.RIGHT);
105034           if (orientation === 0) {
105035             this.addCollinear(addStartPoint);
105036           } else if (outsideTurn) {
105037             this.addOutsideTurn(orientation, addStartPoint);
105038           } else {
105039             this.addInsideTurn(orientation, addStartPoint);
105040           }
105041         };
105042         OffsetSegmentGenerator.prototype.addLineEndCap = function addLineEndCap (p0, p1) {
105043           var seg = new LineSegment(p0, p1);
105044           var offsetL = new LineSegment();
105045           this.computeOffsetSegment(seg, Position.LEFT, this._distance, offsetL);
105046           var offsetR = new LineSegment();
105047           this.computeOffsetSegment(seg, Position.RIGHT, this._distance, offsetR);
105048           var dx = p1.x - p0.x;
105049           var dy = p1.y - p0.y;
105050           var angle = Math.atan2(dy, dx);
105051           switch (this._bufParams.getEndCapStyle()) {
105052             case BufferParameters.CAP_ROUND:
105053               this._segList.addPt(offsetL.p1);
105054               this.addFilletArc(p1, angle + Math.PI / 2, angle - Math.PI / 2, CGAlgorithms.CLOCKWISE, this._distance);
105055               this._segList.addPt(offsetR.p1);
105056               break
105057             case BufferParameters.CAP_FLAT:
105058               this._segList.addPt(offsetL.p1);
105059               this._segList.addPt(offsetR.p1);
105060               break
105061             case BufferParameters.CAP_SQUARE:
105062               var squareCapSideOffset = new Coordinate();
105063               squareCapSideOffset.x = Math.abs(this._distance) * Math.cos(angle);
105064               squareCapSideOffset.y = Math.abs(this._distance) * Math.sin(angle);
105065               var squareCapLOffset = new Coordinate(offsetL.p1.x + squareCapSideOffset.x, offsetL.p1.y + squareCapSideOffset.y);
105066               var squareCapROffset = new Coordinate(offsetR.p1.x + squareCapSideOffset.x, offsetR.p1.y + squareCapSideOffset.y);
105067               this._segList.addPt(squareCapLOffset);
105068               this._segList.addPt(squareCapROffset);
105069               break
105070           }
105071         };
105072         OffsetSegmentGenerator.prototype.getCoordinates = function getCoordinates () {
105073           var pts = this._segList.getCoordinates();
105074           return pts
105075         };
105076         OffsetSegmentGenerator.prototype.addMitreJoin = function addMitreJoin (p, offset0, offset1, distance) {
105077           var isMitreWithinLimit = true;
105078           var intPt = null;
105079           try {
105080             intPt = HCoordinate.intersection(offset0.p0, offset0.p1, offset1.p0, offset1.p1);
105081             var mitreRatio = distance <= 0.0 ? 1.0 : intPt.distance(p) / Math.abs(distance);
105082             if (mitreRatio > this._bufParams.getMitreLimit()) { isMitreWithinLimit = false; }
105083           } catch (ex) {
105084             if (ex instanceof NotRepresentableException) {
105085               intPt = new Coordinate(0, 0);
105086               isMitreWithinLimit = false;
105087             } else { throw ex }
105088           } finally {}
105089           if (isMitreWithinLimit) {
105090             this._segList.addPt(intPt);
105091           } else {
105092             this.addLimitedMitreJoin(offset0, offset1, distance, this._bufParams.getMitreLimit());
105093           }
105094         };
105095         OffsetSegmentGenerator.prototype.addFilletCorner = function addFilletCorner (p, p0, p1, direction, radius) {
105096           var dx0 = p0.x - p.x;
105097           var dy0 = p0.y - p.y;
105098           var startAngle = Math.atan2(dy0, dx0);
105099           var dx1 = p1.x - p.x;
105100           var dy1 = p1.y - p.y;
105101           var endAngle = Math.atan2(dy1, dx1);
105102           if (direction === CGAlgorithms.CLOCKWISE) {
105103             if (startAngle <= endAngle) { startAngle += 2.0 * Math.PI; }
105104           } else {
105105             if (startAngle >= endAngle) { startAngle -= 2.0 * Math.PI; }
105106           }
105107           this._segList.addPt(p0);
105108           this.addFilletArc(p, startAngle, endAngle, direction, radius);
105109           this._segList.addPt(p1);
105110         };
105111         OffsetSegmentGenerator.prototype.addOutsideTurn = function addOutsideTurn (orientation, addStartPoint) {
105112           if (this._offset0.p1.distance(this._offset1.p0) < this._distance * OffsetSegmentGenerator.OFFSET_SEGMENT_SEPARATION_FACTOR) {
105113             this._segList.addPt(this._offset0.p1);
105114             return null
105115           }
105116           if (this._bufParams.getJoinStyle() === BufferParameters.JOIN_MITRE) {
105117             this.addMitreJoin(this._s1, this._offset0, this._offset1, this._distance);
105118           } else if (this._bufParams.getJoinStyle() === BufferParameters.JOIN_BEVEL) {
105119             this.addBevelJoin(this._offset0, this._offset1);
105120           } else {
105121             if (addStartPoint) { this._segList.addPt(this._offset0.p1); }
105122             this.addFilletCorner(this._s1, this._offset0.p1, this._offset1.p0, orientation, this._distance);
105123             this._segList.addPt(this._offset1.p0);
105124           }
105125         };
105126         OffsetSegmentGenerator.prototype.createSquare = function createSquare (p) {
105127           this._segList.addPt(new Coordinate(p.x + this._distance, p.y + this._distance));
105128           this._segList.addPt(new Coordinate(p.x + this._distance, p.y - this._distance));
105129           this._segList.addPt(new Coordinate(p.x - this._distance, p.y - this._distance));
105130           this._segList.addPt(new Coordinate(p.x - this._distance, p.y + this._distance));
105131           this._segList.closeRing();
105132         };
105133         OffsetSegmentGenerator.prototype.addSegments = function addSegments (pt, isForward) {
105134           this._segList.addPts(pt, isForward);
105135         };
105136         OffsetSegmentGenerator.prototype.addFirstSegment = function addFirstSegment () {
105137           this._segList.addPt(this._offset1.p0);
105138         };
105139         OffsetSegmentGenerator.prototype.addLastSegment = function addLastSegment () {
105140           this._segList.addPt(this._offset1.p1);
105141         };
105142         OffsetSegmentGenerator.prototype.initSideSegments = function initSideSegments (s1, s2, side) {
105143           this._s1 = s1;
105144           this._s2 = s2;
105145           this._side = side;
105146           this._seg1.setCoordinates(s1, s2);
105147           this.computeOffsetSegment(this._seg1, side, this._distance, this._offset1);
105148         };
105149         OffsetSegmentGenerator.prototype.addLimitedMitreJoin = function addLimitedMitreJoin (offset0, offset1, distance, mitreLimit) {
105150           var basePt = this._seg0.p1;
105151           var ang0 = Angle.angle(basePt, this._seg0.p0);
105152           // const ang1 = Angle.angle(basePt, this._seg1.p1)
105153           var angDiff = Angle.angleBetweenOriented(this._seg0.p0, basePt, this._seg1.p1);
105154           var angDiffHalf = angDiff / 2;
105155           var midAng = Angle.normalize(ang0 + angDiffHalf);
105156           var mitreMidAng = Angle.normalize(midAng + Math.PI);
105157           var mitreDist = mitreLimit * distance;
105158           var bevelDelta = mitreDist * Math.abs(Math.sin(angDiffHalf));
105159           var bevelHalfLen = distance - bevelDelta;
105160           var bevelMidX = basePt.x + mitreDist * Math.cos(mitreMidAng);
105161           var bevelMidY = basePt.y + mitreDist * Math.sin(mitreMidAng);
105162           var bevelMidPt = new Coordinate(bevelMidX, bevelMidY);
105163           var mitreMidLine = new LineSegment(basePt, bevelMidPt);
105164           var bevelEndLeft = mitreMidLine.pointAlongOffset(1.0, bevelHalfLen);
105165           var bevelEndRight = mitreMidLine.pointAlongOffset(1.0, -bevelHalfLen);
105166           if (this._side === Position.LEFT) {
105167             this._segList.addPt(bevelEndLeft);
105168             this._segList.addPt(bevelEndRight);
105169           } else {
105170             this._segList.addPt(bevelEndRight);
105171             this._segList.addPt(bevelEndLeft);
105172           }
105173         };
105174         OffsetSegmentGenerator.prototype.computeOffsetSegment = function computeOffsetSegment (seg, side, distance, offset) {
105175           var sideSign = side === Position.LEFT ? 1 : -1;
105176           var dx = seg.p1.x - seg.p0.x;
105177           var dy = seg.p1.y - seg.p0.y;
105178           var len = Math.sqrt(dx * dx + dy * dy);
105179           var ux = sideSign * distance * dx / len;
105180           var uy = sideSign * distance * dy / len;
105181           offset.p0.x = seg.p0.x - uy;
105182           offset.p0.y = seg.p0.y + ux;
105183           offset.p1.x = seg.p1.x - uy;
105184           offset.p1.y = seg.p1.y + ux;
105185         };
105186         OffsetSegmentGenerator.prototype.addFilletArc = function addFilletArc (p, startAngle, endAngle, direction, radius) {
105187             var this$1 = this;
105188
105189           var directionFactor = direction === CGAlgorithms.CLOCKWISE ? -1 : 1;
105190           var totalAngle = Math.abs(startAngle - endAngle);
105191           var nSegs = Math.trunc(totalAngle / this._filletAngleQuantum + 0.5);
105192           if (nSegs < 1) { return null }
105193           var initAngle = 0.0;
105194           var currAngleInc = totalAngle / nSegs;
105195           var currAngle = initAngle;
105196           var pt = new Coordinate();
105197           while (currAngle < totalAngle) {
105198             var angle = startAngle + directionFactor * currAngle;
105199             pt.x = p.x + radius * Math.cos(angle);
105200             pt.y = p.y + radius * Math.sin(angle);
105201             this$1._segList.addPt(pt);
105202             currAngle += currAngleInc;
105203           }
105204         };
105205         OffsetSegmentGenerator.prototype.addInsideTurn = function addInsideTurn (orientation, addStartPoint) {
105206           this._li.computeIntersection(this._offset0.p0, this._offset0.p1, this._offset1.p0, this._offset1.p1);
105207           if (this._li.hasIntersection()) {
105208             this._segList.addPt(this._li.getIntersection(0));
105209           } else {
105210             this._hasNarrowConcaveAngle = true;
105211             if (this._offset0.p1.distance(this._offset1.p0) < this._distance * OffsetSegmentGenerator.INSIDE_TURN_VERTEX_SNAP_DISTANCE_FACTOR) {
105212               this._segList.addPt(this._offset0.p1);
105213             } else {
105214               this._segList.addPt(this._offset0.p1);
105215               if (this._closingSegLengthFactor > 0) {
105216                 var mid0 = new Coordinate((this._closingSegLengthFactor * this._offset0.p1.x + this._s1.x) / (this._closingSegLengthFactor + 1), (this._closingSegLengthFactor * this._offset0.p1.y + this._s1.y) / (this._closingSegLengthFactor + 1));
105217                 this._segList.addPt(mid0);
105218                 var mid1 = new Coordinate((this._closingSegLengthFactor * this._offset1.p0.x + this._s1.x) / (this._closingSegLengthFactor + 1), (this._closingSegLengthFactor * this._offset1.p0.y + this._s1.y) / (this._closingSegLengthFactor + 1));
105219                 this._segList.addPt(mid1);
105220               } else {
105221                 this._segList.addPt(this._s1);
105222               }
105223               this._segList.addPt(this._offset1.p0);
105224             }
105225           }
105226         };
105227         OffsetSegmentGenerator.prototype.createCircle = function createCircle (p) {
105228           var pt = new Coordinate(p.x + this._distance, p.y);
105229           this._segList.addPt(pt);
105230           this.addFilletArc(p, 0.0, 2.0 * Math.PI, -1, this._distance);
105231           this._segList.closeRing();
105232         };
105233         OffsetSegmentGenerator.prototype.addBevelJoin = function addBevelJoin (offset0, offset1) {
105234           this._segList.addPt(offset0.p1);
105235           this._segList.addPt(offset1.p0);
105236         };
105237         OffsetSegmentGenerator.prototype.init = function init (distance) {
105238           this._distance = distance;
105239           this._maxCurveSegmentError = distance * (1 - Math.cos(this._filletAngleQuantum / 2.0));
105240           this._segList = new OffsetSegmentString();
105241           this._segList.setPrecisionModel(this._precisionModel);
105242           this._segList.setMinimumVertexDistance(distance * OffsetSegmentGenerator.CURVE_VERTEX_SNAP_DISTANCE_FACTOR);
105243         };
105244         OffsetSegmentGenerator.prototype.addCollinear = function addCollinear (addStartPoint) {
105245           this._li.computeIntersection(this._s0, this._s1, this._s1, this._s2);
105246           var numInt = this._li.getIntersectionNum();
105247           if (numInt >= 2) {
105248             if (this._bufParams.getJoinStyle() === BufferParameters.JOIN_BEVEL || this._bufParams.getJoinStyle() === BufferParameters.JOIN_MITRE) {
105249               if (addStartPoint) { this._segList.addPt(this._offset0.p1); }
105250               this._segList.addPt(this._offset1.p0);
105251             } else {
105252               this.addFilletCorner(this._s1, this._offset0.p1, this._offset1.p0, CGAlgorithms.CLOCKWISE, this._distance);
105253             }
105254           }
105255         };
105256         OffsetSegmentGenerator.prototype.closeRing = function closeRing () {
105257           this._segList.closeRing();
105258         };
105259         OffsetSegmentGenerator.prototype.hasNarrowConcaveAngle = function hasNarrowConcaveAngle () {
105260           return this._hasNarrowConcaveAngle
105261         };
105262         OffsetSegmentGenerator.prototype.interfaces_ = function interfaces_ () {
105263           return []
105264         };
105265         OffsetSegmentGenerator.prototype.getClass = function getClass () {
105266           return OffsetSegmentGenerator
105267         };
105268         staticAccessors$27.OFFSET_SEGMENT_SEPARATION_FACTOR.get = function () { return 1.0E-3 };
105269         staticAccessors$27.INSIDE_TURN_VERTEX_SNAP_DISTANCE_FACTOR.get = function () { return 1.0E-3 };
105270         staticAccessors$27.CURVE_VERTEX_SNAP_DISTANCE_FACTOR.get = function () { return 1.0E-6 };
105271         staticAccessors$27.MAX_CLOSING_SEG_LEN_FACTOR.get = function () { return 80 };
105272
105273         Object.defineProperties( OffsetSegmentGenerator, staticAccessors$27 );
105274
105275         var OffsetCurveBuilder = function OffsetCurveBuilder () {
105276           this._distance = 0.0;
105277           this._precisionModel = null;
105278           this._bufParams = null;
105279           var precisionModel = arguments[0];
105280           var bufParams = arguments[1];
105281           this._precisionModel = precisionModel;
105282           this._bufParams = bufParams;
105283         };
105284         OffsetCurveBuilder.prototype.getOffsetCurve = function getOffsetCurve (inputPts, distance) {
105285           this._distance = distance;
105286           if (distance === 0.0) { return null }
105287           var isRightSide = distance < 0.0;
105288           var posDistance = Math.abs(distance);
105289           var segGen = this.getSegGen(posDistance);
105290           if (inputPts.length <= 1) {
105291             this.computePointCurve(inputPts[0], segGen);
105292           } else {
105293             this.computeOffsetCurve(inputPts, isRightSide, segGen);
105294           }
105295           var curvePts = segGen.getCoordinates();
105296           if (isRightSide) { CoordinateArrays.reverse(curvePts); }
105297           return curvePts
105298         };
105299         OffsetCurveBuilder.prototype.computeSingleSidedBufferCurve = function computeSingleSidedBufferCurve (inputPts, isRightSide, segGen) {
105300           var distTol = this.simplifyTolerance(this._distance);
105301           if (isRightSide) {
105302             segGen.addSegments(inputPts, true);
105303             var simp2 = BufferInputLineSimplifier.simplify(inputPts, -distTol);
105304             var n2 = simp2.length - 1;
105305             segGen.initSideSegments(simp2[n2], simp2[n2 - 1], Position.LEFT);
105306             segGen.addFirstSegment();
105307             for (var i = n2 - 2; i >= 0; i--) {
105308               segGen.addNextSegment(simp2[i], true);
105309             }
105310           } else {
105311             segGen.addSegments(inputPts, false);
105312             var simp1 = BufferInputLineSimplifier.simplify(inputPts, distTol);
105313             var n1 = simp1.length - 1;
105314             segGen.initSideSegments(simp1[0], simp1[1], Position.LEFT);
105315             segGen.addFirstSegment();
105316             for (var i$1 = 2; i$1 <= n1; i$1++) {
105317               segGen.addNextSegment(simp1[i$1], true);
105318             }
105319           }
105320           segGen.addLastSegment();
105321           segGen.closeRing();
105322         };
105323         OffsetCurveBuilder.prototype.computeRingBufferCurve = function computeRingBufferCurve (inputPts, side, segGen) {
105324           var distTol = this.simplifyTolerance(this._distance);
105325           if (side === Position.RIGHT) { distTol = -distTol; }
105326           var simp = BufferInputLineSimplifier.simplify(inputPts, distTol);
105327           var n = simp.length - 1;
105328           segGen.initSideSegments(simp[n - 1], simp[0], side);
105329           for (var i = 1; i <= n; i++) {
105330             var addStartPoint = i !== 1;
105331             segGen.addNextSegment(simp[i], addStartPoint);
105332           }
105333           segGen.closeRing();
105334         };
105335         OffsetCurveBuilder.prototype.computeLineBufferCurve = function computeLineBufferCurve (inputPts, segGen) {
105336           var distTol = this.simplifyTolerance(this._distance);
105337           var simp1 = BufferInputLineSimplifier.simplify(inputPts, distTol);
105338           var n1 = simp1.length - 1;
105339           segGen.initSideSegments(simp1[0], simp1[1], Position.LEFT);
105340           for (var i = 2; i <= n1; i++) {
105341             segGen.addNextSegment(simp1[i], true);
105342           }
105343           segGen.addLastSegment();
105344           segGen.addLineEndCap(simp1[n1 - 1], simp1[n1]);
105345           var simp2 = BufferInputLineSimplifier.simplify(inputPts, -distTol);
105346           var n2 = simp2.length - 1;
105347           segGen.initSideSegments(simp2[n2], simp2[n2 - 1], Position.LEFT);
105348           for (var i$1 = n2 - 2; i$1 >= 0; i$1--) {
105349             segGen.addNextSegment(simp2[i$1], true);
105350           }
105351           segGen.addLastSegment();
105352           segGen.addLineEndCap(simp2[1], simp2[0]);
105353           segGen.closeRing();
105354         };
105355         OffsetCurveBuilder.prototype.computePointCurve = function computePointCurve (pt, segGen) {
105356           switch (this._bufParams.getEndCapStyle()) {
105357             case BufferParameters.CAP_ROUND:
105358               segGen.createCircle(pt);
105359               break
105360             case BufferParameters.CAP_SQUARE:
105361               segGen.createSquare(pt);
105362               break
105363           }
105364         };
105365         OffsetCurveBuilder.prototype.getLineCurve = function getLineCurve (inputPts, distance) {
105366           this._distance = distance;
105367           if (distance < 0.0 && !this._bufParams.isSingleSided()) { return null }
105368           if (distance === 0.0) { return null }
105369           var posDistance = Math.abs(distance);
105370           var segGen = this.getSegGen(posDistance);
105371           if (inputPts.length <= 1) {
105372             this.computePointCurve(inputPts[0], segGen);
105373           } else {
105374             if (this._bufParams.isSingleSided()) {
105375               var isRightSide = distance < 0.0;
105376               this.computeSingleSidedBufferCurve(inputPts, isRightSide, segGen);
105377             } else { this.computeLineBufferCurve(inputPts, segGen); }
105378           }
105379           var lineCoord = segGen.getCoordinates();
105380           return lineCoord
105381         };
105382         OffsetCurveBuilder.prototype.getBufferParameters = function getBufferParameters () {
105383           return this._bufParams
105384         };
105385         OffsetCurveBuilder.prototype.simplifyTolerance = function simplifyTolerance (bufDistance) {
105386           return bufDistance * this._bufParams.getSimplifyFactor()
105387         };
105388         OffsetCurveBuilder.prototype.getRingCurve = function getRingCurve (inputPts, side, distance) {
105389           this._distance = distance;
105390           if (inputPts.length <= 2) { return this.getLineCurve(inputPts, distance) }
105391           if (distance === 0.0) {
105392             return OffsetCurveBuilder.copyCoordinates(inputPts)
105393           }
105394           var segGen = this.getSegGen(distance);
105395           this.computeRingBufferCurve(inputPts, side, segGen);
105396           return segGen.getCoordinates()
105397         };
105398         OffsetCurveBuilder.prototype.computeOffsetCurve = function computeOffsetCurve (inputPts, isRightSide, segGen) {
105399           var distTol = this.simplifyTolerance(this._distance);
105400           if (isRightSide) {
105401             var simp2 = BufferInputLineSimplifier.simplify(inputPts, -distTol);
105402             var n2 = simp2.length - 1;
105403             segGen.initSideSegments(simp2[n2], simp2[n2 - 1], Position.LEFT);
105404             segGen.addFirstSegment();
105405             for (var i = n2 - 2; i >= 0; i--) {
105406               segGen.addNextSegment(simp2[i], true);
105407             }
105408           } else {
105409             var simp1 = BufferInputLineSimplifier.simplify(inputPts, distTol);
105410             var n1 = simp1.length - 1;
105411             segGen.initSideSegments(simp1[0], simp1[1], Position.LEFT);
105412             segGen.addFirstSegment();
105413             for (var i$1 = 2; i$1 <= n1; i$1++) {
105414               segGen.addNextSegment(simp1[i$1], true);
105415             }
105416           }
105417           segGen.addLastSegment();
105418         };
105419         OffsetCurveBuilder.prototype.getSegGen = function getSegGen (distance) {
105420           return new OffsetSegmentGenerator(this._precisionModel, this._bufParams, distance)
105421         };
105422         OffsetCurveBuilder.prototype.interfaces_ = function interfaces_ () {
105423           return []
105424         };
105425         OffsetCurveBuilder.prototype.getClass = function getClass () {
105426           return OffsetCurveBuilder
105427         };
105428         OffsetCurveBuilder.copyCoordinates = function copyCoordinates (pts) {
105429           var copy = new Array(pts.length).fill(null);
105430           for (var i = 0; i < copy.length; i++) {
105431             copy[i] = new Coordinate(pts[i]);
105432           }
105433           return copy
105434         };
105435
105436         var SubgraphDepthLocater = function SubgraphDepthLocater () {
105437           this._subgraphs = null;
105438           this._seg = new LineSegment();
105439           this._cga = new CGAlgorithms();
105440           var subgraphs = arguments[0];
105441           this._subgraphs = subgraphs;
105442         };
105443
105444         var staticAccessors$30 = { DepthSegment: { configurable: true } };
105445         SubgraphDepthLocater.prototype.findStabbedSegments = function findStabbedSegments () {
105446             var this$1 = this;
105447
105448           if (arguments.length === 1) {
105449             var stabbingRayLeftPt = arguments[0];
105450             var stabbedSegments = new ArrayList();
105451             for (var i = this._subgraphs.iterator(); i.hasNext();) {
105452               var bsg = i.next();
105453               var env = bsg.getEnvelope();
105454               if (stabbingRayLeftPt.y < env.getMinY() || stabbingRayLeftPt.y > env.getMaxY()) { continue }
105455               this$1.findStabbedSegments(stabbingRayLeftPt, bsg.getDirectedEdges(), stabbedSegments);
105456             }
105457             return stabbedSegments
105458           } else if (arguments.length === 3) {
105459             if (hasInterface(arguments[2], List) && (arguments[0] instanceof Coordinate && arguments[1] instanceof DirectedEdge)) {
105460               var stabbingRayLeftPt$1 = arguments[0];
105461               var dirEdge = arguments[1];
105462               var stabbedSegments$1 = arguments[2];
105463               var pts = dirEdge.getEdge().getCoordinates();
105464               for (var i$1 = 0; i$1 < pts.length - 1; i$1++) {
105465                 this$1._seg.p0 = pts[i$1];
105466                 this$1._seg.p1 = pts[i$1 + 1];
105467                 if (this$1._seg.p0.y > this$1._seg.p1.y) { this$1._seg.reverse(); }
105468                 var maxx = Math.max(this$1._seg.p0.x, this$1._seg.p1.x);
105469                 if (maxx < stabbingRayLeftPt$1.x) { continue }
105470                 if (this$1._seg.isHorizontal()) { continue }
105471                 if (stabbingRayLeftPt$1.y < this$1._seg.p0.y || stabbingRayLeftPt$1.y > this$1._seg.p1.y) { continue }
105472                 if (CGAlgorithms.computeOrientation(this$1._seg.p0, this$1._seg.p1, stabbingRayLeftPt$1) === CGAlgorithms.RIGHT) { continue }
105473                 var depth = dirEdge.getDepth(Position.LEFT);
105474                 if (!this$1._seg.p0.equals(pts[i$1])) { depth = dirEdge.getDepth(Position.RIGHT); }
105475                 var ds = new DepthSegment(this$1._seg, depth);
105476                 stabbedSegments$1.add(ds);
105477               }
105478             } else if (hasInterface(arguments[2], List) && (arguments[0] instanceof Coordinate && hasInterface(arguments[1], List))) {
105479               var stabbingRayLeftPt$2 = arguments[0];
105480               var dirEdges = arguments[1];
105481               var stabbedSegments$2 = arguments[2];
105482               for (var i$2 = dirEdges.iterator(); i$2.hasNext();) {
105483                 var de = i$2.next();
105484                 if (!de.isForward()) { continue }
105485                 this$1.findStabbedSegments(stabbingRayLeftPt$2, de, stabbedSegments$2);
105486               }
105487             }
105488           }
105489         };
105490         SubgraphDepthLocater.prototype.getDepth = function getDepth (p) {
105491           var stabbedSegments = this.findStabbedSegments(p);
105492           if (stabbedSegments.size() === 0) { return 0 }
105493           var ds = Collections.min(stabbedSegments);
105494           return ds._leftDepth
105495         };
105496         SubgraphDepthLocater.prototype.interfaces_ = function interfaces_ () {
105497           return []
105498         };
105499         SubgraphDepthLocater.prototype.getClass = function getClass () {
105500           return SubgraphDepthLocater
105501         };
105502         staticAccessors$30.DepthSegment.get = function () { return DepthSegment };
105503
105504         Object.defineProperties( SubgraphDepthLocater, staticAccessors$30 );
105505
105506         var DepthSegment = function DepthSegment () {
105507           this._upwardSeg = null;
105508           this._leftDepth = null;
105509           var seg = arguments[0];
105510           var depth = arguments[1];
105511           this._upwardSeg = new LineSegment(seg);
105512           this._leftDepth = depth;
105513         };
105514         DepthSegment.prototype.compareTo = function compareTo (obj) {
105515           var other = obj;
105516           if (this._upwardSeg.minX() >= other._upwardSeg.maxX()) { return 1 }
105517           if (this._upwardSeg.maxX() <= other._upwardSeg.minX()) { return -1 }
105518           var orientIndex = this._upwardSeg.orientationIndex(other._upwardSeg);
105519           if (orientIndex !== 0) { return orientIndex }
105520           orientIndex = -1 * other._upwardSeg.orientationIndex(this._upwardSeg);
105521           if (orientIndex !== 0) { return orientIndex }
105522           return this._upwardSeg.compareTo(other._upwardSeg)
105523         };
105524         DepthSegment.prototype.compareX = function compareX (seg0, seg1) {
105525           var compare0 = seg0.p0.compareTo(seg1.p0);
105526           if (compare0 !== 0) { return compare0 }
105527           return seg0.p1.compareTo(seg1.p1)
105528         };
105529         DepthSegment.prototype.toString = function toString () {
105530           return this._upwardSeg.toString()
105531         };
105532         DepthSegment.prototype.interfaces_ = function interfaces_ () {
105533           return [Comparable]
105534         };
105535         DepthSegment.prototype.getClass = function getClass () {
105536           return DepthSegment
105537         };
105538
105539         var Triangle = function Triangle (p0, p1, p2) {
105540           this.p0 = p0 || null;
105541           this.p1 = p1 || null;
105542           this.p2 = p2 || null;
105543         };
105544         Triangle.prototype.area = function area () {
105545           return Triangle.area(this.p0, this.p1, this.p2)
105546         };
105547         Triangle.prototype.signedArea = function signedArea () {
105548           return Triangle.signedArea(this.p0, this.p1, this.p2)
105549         };
105550         Triangle.prototype.interpolateZ = function interpolateZ (p) {
105551           if (p === null) { throw new IllegalArgumentException('Supplied point is null.') }
105552           return Triangle.interpolateZ(p, this.p0, this.p1, this.p2)
105553         };
105554         Triangle.prototype.longestSideLength = function longestSideLength () {
105555           return Triangle.longestSideLength(this.p0, this.p1, this.p2)
105556         };
105557         Triangle.prototype.isAcute = function isAcute () {
105558           return Triangle.isAcute(this.p0, this.p1, this.p2)
105559         };
105560         Triangle.prototype.circumcentre = function circumcentre () {
105561           return Triangle.circumcentre(this.p0, this.p1, this.p2)
105562         };
105563         Triangle.prototype.area3D = function area3D () {
105564           return Triangle.area3D(this.p0, this.p1, this.p2)
105565         };
105566         Triangle.prototype.centroid = function centroid () {
105567           return Triangle.centroid(this.p0, this.p1, this.p2)
105568         };
105569         Triangle.prototype.inCentre = function inCentre () {
105570           return Triangle.inCentre(this.p0, this.p1, this.p2)
105571         };
105572         Triangle.prototype.interfaces_ = function interfaces_ () {
105573           return []
105574         };
105575         Triangle.prototype.getClass = function getClass () {
105576           return Triangle
105577         };
105578         Triangle.area = function area (a, b, c) {
105579           return Math.abs(((c.x - a.x) * (b.y - a.y) - (b.x - a.x) * (c.y - a.y)) / 2)
105580         };
105581         Triangle.signedArea = function signedArea (a, b, c) {
105582           return ((c.x - a.x) * (b.y - a.y) - (b.x - a.x) * (c.y - a.y)) / 2
105583         };
105584         Triangle.det = function det (m00, m01, m10, m11) {
105585           return m00 * m11 - m01 * m10
105586         };
105587         Triangle.interpolateZ = function interpolateZ (p, v0, v1, v2) {
105588           var x0 = v0.x;
105589           var y0 = v0.y;
105590           var a = v1.x - x0;
105591           var b = v2.x - x0;
105592           var c = v1.y - y0;
105593           var d = v2.y - y0;
105594           var det = a * d - b * c;
105595           var dx = p.x - x0;
105596           var dy = p.y - y0;
105597           var t = (d * dx - b * dy) / det;
105598           var u = (-c * dx + a * dy) / det;
105599           var z = v0.z + t * (v1.z - v0.z) + u * (v2.z - v0.z);
105600           return z
105601         };
105602         Triangle.longestSideLength = function longestSideLength (a, b, c) {
105603           var lenAB = a.distance(b);
105604           var lenBC = b.distance(c);
105605           var lenCA = c.distance(a);
105606           var maxLen = lenAB;
105607           if (lenBC > maxLen) { maxLen = lenBC; }
105608           if (lenCA > maxLen) { maxLen = lenCA; }
105609           return maxLen
105610         };
105611         Triangle.isAcute = function isAcute (a, b, c) {
105612           if (!Angle.isAcute(a, b, c)) { return false }
105613           if (!Angle.isAcute(b, c, a)) { return false }
105614           if (!Angle.isAcute(c, a, b)) { return false }
105615           return true
105616         };
105617         Triangle.circumcentre = function circumcentre (a, b, c) {
105618           var cx = c.x;
105619           var cy = c.y;
105620           var ax = a.x - cx;
105621           var ay = a.y - cy;
105622           var bx = b.x - cx;
105623           var by = b.y - cy;
105624           var denom = 2 * Triangle.det(ax, ay, bx, by);
105625           var numx = Triangle.det(ay, ax * ax + ay * ay, by, bx * bx + by * by);
105626           var numy = Triangle.det(ax, ax * ax + ay * ay, bx, bx * bx + by * by);
105627           var ccx = cx - numx / denom;
105628           var ccy = cy + numy / denom;
105629           return new Coordinate(ccx, ccy)
105630         };
105631         Triangle.perpendicularBisector = function perpendicularBisector (a, b) {
105632           var dx = b.x - a.x;
105633           var dy = b.y - a.y;
105634           var l1 = new HCoordinate(a.x + dx / 2.0, a.y + dy / 2.0, 1.0);
105635           var l2 = new HCoordinate(a.x - dy + dx / 2.0, a.y + dx + dy / 2.0, 1.0);
105636           return new HCoordinate(l1, l2)
105637         };
105638         Triangle.angleBisector = function angleBisector (a, b, c) {
105639           var len0 = b.distance(a);
105640           var len2 = b.distance(c);
105641           var frac = len0 / (len0 + len2);
105642           var dx = c.x - a.x;
105643           var dy = c.y - a.y;
105644           var splitPt = new Coordinate(a.x + frac * dx, a.y + frac * dy);
105645           return splitPt
105646         };
105647         Triangle.area3D = function area3D (a, b, c) {
105648           var ux = b.x - a.x;
105649           var uy = b.y - a.y;
105650           var uz = b.z - a.z;
105651           var vx = c.x - a.x;
105652           var vy = c.y - a.y;
105653           var vz = c.z - a.z;
105654           var crossx = uy * vz - uz * vy;
105655           var crossy = uz * vx - ux * vz;
105656           var crossz = ux * vy - uy * vx;
105657           var absSq = crossx * crossx + crossy * crossy + crossz * crossz;
105658           var area3D = Math.sqrt(absSq) / 2;
105659           return area3D
105660         };
105661         Triangle.centroid = function centroid (a, b, c) {
105662           var x = (a.x + b.x + c.x) / 3;
105663           var y = (a.y + b.y + c.y) / 3;
105664           return new Coordinate(x, y)
105665         };
105666         Triangle.inCentre = function inCentre (a, b, c) {
105667           var len0 = b.distance(c);
105668           var len1 = a.distance(c);
105669           var len2 = a.distance(b);
105670           var circum = len0 + len1 + len2;
105671           var inCentreX = (len0 * a.x + len1 * b.x + len2 * c.x) / circum;
105672           var inCentreY = (len0 * a.y + len1 * b.y + len2 * c.y) / circum;
105673           return new Coordinate(inCentreX, inCentreY)
105674         };
105675
105676         var OffsetCurveSetBuilder = function OffsetCurveSetBuilder () {
105677           this._inputGeom = null;
105678           this._distance = null;
105679           this._curveBuilder = null;
105680           this._curveList = new ArrayList();
105681           var inputGeom = arguments[0];
105682           var distance = arguments[1];
105683           var curveBuilder = arguments[2];
105684           this._inputGeom = inputGeom;
105685           this._distance = distance;
105686           this._curveBuilder = curveBuilder;
105687         };
105688         OffsetCurveSetBuilder.prototype.addPoint = function addPoint (p) {
105689           if (this._distance <= 0.0) { return null }
105690           var coord = p.getCoordinates();
105691           var curve = this._curveBuilder.getLineCurve(coord, this._distance);
105692           this.addCurve(curve, Location.EXTERIOR, Location.INTERIOR);
105693         };
105694         OffsetCurveSetBuilder.prototype.addPolygon = function addPolygon (p) {
105695             var this$1 = this;
105696
105697           var offsetDistance = this._distance;
105698           var offsetSide = Position.LEFT;
105699           if (this._distance < 0.0) {
105700             offsetDistance = -this._distance;
105701             offsetSide = Position.RIGHT;
105702           }
105703           var shell = p.getExteriorRing();
105704           var shellCoord = CoordinateArrays.removeRepeatedPoints(shell.getCoordinates());
105705           if (this._distance < 0.0 && this.isErodedCompletely(shell, this._distance)) { return null }
105706           if (this._distance <= 0.0 && shellCoord.length < 3) { return null }
105707           this.addPolygonRing(shellCoord, offsetDistance, offsetSide, Location.EXTERIOR, Location.INTERIOR);
105708           for (var i = 0; i < p.getNumInteriorRing(); i++) {
105709             var hole = p.getInteriorRingN(i);
105710             var holeCoord = CoordinateArrays.removeRepeatedPoints(hole.getCoordinates());
105711             if (this$1._distance > 0.0 && this$1.isErodedCompletely(hole, -this$1._distance)) { continue }
105712             this$1.addPolygonRing(holeCoord, offsetDistance, Position.opposite(offsetSide), Location.INTERIOR, Location.EXTERIOR);
105713           }
105714         };
105715         OffsetCurveSetBuilder.prototype.isTriangleErodedCompletely = function isTriangleErodedCompletely (triangleCoord, bufferDistance) {
105716           var tri = new Triangle(triangleCoord[0], triangleCoord[1], triangleCoord[2]);
105717           var inCentre = tri.inCentre();
105718           var distToCentre = CGAlgorithms.distancePointLine(inCentre, tri.p0, tri.p1);
105719           return distToCentre < Math.abs(bufferDistance)
105720         };
105721         OffsetCurveSetBuilder.prototype.addLineString = function addLineString (line) {
105722           if (this._distance <= 0.0 && !this._curveBuilder.getBufferParameters().isSingleSided()) { return null }
105723           var coord = CoordinateArrays.removeRepeatedPoints(line.getCoordinates());
105724           var curve = this._curveBuilder.getLineCurve(coord, this._distance);
105725           this.addCurve(curve, Location.EXTERIOR, Location.INTERIOR);
105726         };
105727         OffsetCurveSetBuilder.prototype.addCurve = function addCurve (coord, leftLoc, rightLoc) {
105728           if (coord === null || coord.length < 2) { return null }
105729           var e = new NodedSegmentString(coord, new Label(0, Location.BOUNDARY, leftLoc, rightLoc));
105730           this._curveList.add(e);
105731         };
105732         OffsetCurveSetBuilder.prototype.getCurves = function getCurves () {
105733           this.add(this._inputGeom);
105734           return this._curveList
105735         };
105736         OffsetCurveSetBuilder.prototype.addPolygonRing = function addPolygonRing (coord, offsetDistance, side, cwLeftLoc, cwRightLoc) {
105737           if (offsetDistance === 0.0 && coord.length < LinearRing.MINIMUM_VALID_SIZE) { return null }
105738           var leftLoc = cwLeftLoc;
105739           var rightLoc = cwRightLoc;
105740           if (coord.length >= LinearRing.MINIMUM_VALID_SIZE && CGAlgorithms.isCCW(coord)) {
105741             leftLoc = cwRightLoc;
105742             rightLoc = cwLeftLoc;
105743             side = Position.opposite(side);
105744           }
105745           var curve = this._curveBuilder.getRingCurve(coord, side, offsetDistance);
105746           this.addCurve(curve, leftLoc, rightLoc);
105747         };
105748         OffsetCurveSetBuilder.prototype.add = function add (g) {
105749           if (g.isEmpty()) { return null }
105750           if (g instanceof Polygon) { this.addPolygon(g); }
105751           else if (g instanceof LineString) { this.addLineString(g); }
105752           else if (g instanceof Point$1) { this.addPoint(g); }
105753           else if (g instanceof MultiPoint) { this.addCollection(g); }
105754           else if (g instanceof MultiLineString) { this.addCollection(g); }
105755           else if (g instanceof MultiPolygon) { this.addCollection(g); }
105756           else if (g instanceof GeometryCollection) { this.addCollection(g); }
105757           // else throw new UnsupportedOperationException(g.getClass().getName())
105758         };
105759         OffsetCurveSetBuilder.prototype.isErodedCompletely = function isErodedCompletely (ring, bufferDistance) {
105760           var ringCoord = ring.getCoordinates();
105761           // const minDiam = 0.0
105762           if (ringCoord.length < 4) { return bufferDistance < 0 }
105763           if (ringCoord.length === 4) { return this.isTriangleErodedCompletely(ringCoord, bufferDistance) }
105764           var env = ring.getEnvelopeInternal();
105765           var envMinDimension = Math.min(env.getHeight(), env.getWidth());
105766           if (bufferDistance < 0.0 && 2 * Math.abs(bufferDistance) > envMinDimension) { return true }
105767           return false
105768         };
105769         OffsetCurveSetBuilder.prototype.addCollection = function addCollection (gc) {
105770             var this$1 = this;
105771
105772           for (var i = 0; i < gc.getNumGeometries(); i++) {
105773             var g = gc.getGeometryN(i);
105774             this$1.add(g);
105775           }
105776         };
105777         OffsetCurveSetBuilder.prototype.interfaces_ = function interfaces_ () {
105778           return []
105779         };
105780         OffsetCurveSetBuilder.prototype.getClass = function getClass () {
105781           return OffsetCurveSetBuilder
105782         };
105783
105784         var PointOnGeometryLocator = function PointOnGeometryLocator () {};
105785
105786         PointOnGeometryLocator.prototype.locate = function locate (p) {};
105787         PointOnGeometryLocator.prototype.interfaces_ = function interfaces_ () {
105788           return []
105789         };
105790         PointOnGeometryLocator.prototype.getClass = function getClass () {
105791           return PointOnGeometryLocator
105792         };
105793
105794         var GeometryCollectionIterator = function GeometryCollectionIterator () {
105795           this._parent = null;
105796           this._atStart = null;
105797           this._max = null;
105798           this._index = null;
105799           this._subcollectionIterator = null;
105800           var parent = arguments[0];
105801           this._parent = parent;
105802           this._atStart = true;
105803           this._index = 0;
105804           this._max = parent.getNumGeometries();
105805         };
105806         GeometryCollectionIterator.prototype.next = function next () {
105807           if (this._atStart) {
105808             this._atStart = false;
105809             if (GeometryCollectionIterator.isAtomic(this._parent)) { this._index++; }
105810             return this._parent
105811           }
105812           if (this._subcollectionIterator !== null) {
105813             if (this._subcollectionIterator.hasNext()) {
105814               return this._subcollectionIterator.next()
105815             } else {
105816               this._subcollectionIterator = null;
105817             }
105818           }
105819           if (this._index >= this._max) {
105820             throw new NoSuchElementException()
105821           }
105822           var obj = this._parent.getGeometryN(this._index++);
105823           if (obj instanceof GeometryCollection) {
105824             this._subcollectionIterator = new GeometryCollectionIterator(obj);
105825             return this._subcollectionIterator.next()
105826           }
105827           return obj
105828         };
105829         GeometryCollectionIterator.prototype.remove = function remove () {
105830           throw new Error(this.getClass().getName())
105831         };
105832         GeometryCollectionIterator.prototype.hasNext = function hasNext () {
105833           if (this._atStart) {
105834             return true
105835           }
105836           if (this._subcollectionIterator !== null) {
105837             if (this._subcollectionIterator.hasNext()) {
105838               return true
105839             }
105840             this._subcollectionIterator = null;
105841           }
105842           if (this._index >= this._max) {
105843             return false
105844           }
105845           return true
105846         };
105847         GeometryCollectionIterator.prototype.interfaces_ = function interfaces_ () {
105848           return [Iterator$1]
105849         };
105850         GeometryCollectionIterator.prototype.getClass = function getClass () {
105851           return GeometryCollectionIterator
105852         };
105853         GeometryCollectionIterator.isAtomic = function isAtomic (geom) {
105854           return !(geom instanceof GeometryCollection)
105855         };
105856
105857         var SimplePointInAreaLocator = function SimplePointInAreaLocator () {
105858           this._geom = null;
105859           var geom = arguments[0];
105860           this._geom = geom;
105861         };
105862         SimplePointInAreaLocator.prototype.locate = function locate (p) {
105863           return SimplePointInAreaLocator.locate(p, this._geom)
105864         };
105865         SimplePointInAreaLocator.prototype.interfaces_ = function interfaces_ () {
105866           return [PointOnGeometryLocator]
105867         };
105868         SimplePointInAreaLocator.prototype.getClass = function getClass () {
105869           return SimplePointInAreaLocator
105870         };
105871         SimplePointInAreaLocator.isPointInRing = function isPointInRing (p, ring) {
105872           if (!ring.getEnvelopeInternal().intersects(p)) { return false }
105873           return CGAlgorithms.isPointInRing(p, ring.getCoordinates())
105874         };
105875         SimplePointInAreaLocator.containsPointInPolygon = function containsPointInPolygon (p, poly) {
105876           if (poly.isEmpty()) { return false }
105877           var shell = poly.getExteriorRing();
105878           if (!SimplePointInAreaLocator.isPointInRing(p, shell)) { return false }
105879           for (var i = 0; i < poly.getNumInteriorRing(); i++) {
105880             var hole = poly.getInteriorRingN(i);
105881             if (SimplePointInAreaLocator.isPointInRing(p, hole)) { return false }
105882           }
105883           return true
105884         };
105885         SimplePointInAreaLocator.containsPoint = function containsPoint (p, geom) {
105886           if (geom instanceof Polygon) {
105887             return SimplePointInAreaLocator.containsPointInPolygon(p, geom)
105888           } else if (geom instanceof GeometryCollection) {
105889             var geomi = new GeometryCollectionIterator(geom);
105890             while (geomi.hasNext()) {
105891               var g2 = geomi.next();
105892               if (g2 !== geom) { if (SimplePointInAreaLocator.containsPoint(p, g2)) { return true } }
105893             }
105894           }
105895           return false
105896         };
105897         SimplePointInAreaLocator.locate = function locate (p, geom) {
105898           if (geom.isEmpty()) { return Location.EXTERIOR }
105899           if (SimplePointInAreaLocator.containsPoint(p, geom)) { return Location.INTERIOR }
105900           return Location.EXTERIOR
105901         };
105902
105903         var EdgeEndStar = function EdgeEndStar () {
105904           this._edgeMap = new TreeMap();
105905           this._edgeList = null;
105906           this._ptInAreaLocation = [Location.NONE, Location.NONE];
105907         };
105908         EdgeEndStar.prototype.getNextCW = function getNextCW (ee) {
105909           this.getEdges();
105910           var i = this._edgeList.indexOf(ee);
105911           var iNextCW = i - 1;
105912           if (i === 0) { iNextCW = this._edgeList.size() - 1; }
105913           return this._edgeList.get(iNextCW)
105914         };
105915         EdgeEndStar.prototype.propagateSideLabels = function propagateSideLabels (geomIndex) {
105916           var startLoc = Location.NONE;
105917           for (var it = this.iterator(); it.hasNext();) {
105918             var e = it.next();
105919             var label = e.getLabel();
105920             if (label.isArea(geomIndex) && label.getLocation(geomIndex, Position.LEFT) !== Location.NONE) { startLoc = label.getLocation(geomIndex, Position.LEFT); }
105921           }
105922           if (startLoc === Location.NONE) { return null }
105923           var currLoc = startLoc;
105924           for (var it$1 = this.iterator(); it$1.hasNext();) {
105925             var e$1 = it$1.next();
105926             var label$1 = e$1.getLabel();
105927             if (label$1.getLocation(geomIndex, Position.ON) === Location.NONE) { label$1.setLocation(geomIndex, Position.ON, currLoc); }
105928             if (label$1.isArea(geomIndex)) {
105929               var leftLoc = label$1.getLocation(geomIndex, Position.LEFT);
105930               var rightLoc = label$1.getLocation(geomIndex, Position.RIGHT);
105931               if (rightLoc !== Location.NONE) {
105932                 if (rightLoc !== currLoc) { throw new TopologyException('side location conflict', e$1.getCoordinate()) }
105933                 if (leftLoc === Location.NONE) {
105934                   Assert.shouldNeverReachHere('found single null side (at ' + e$1.getCoordinate() + ')');
105935                 }
105936                 currLoc = leftLoc;
105937               } else {
105938                 Assert.isTrue(label$1.getLocation(geomIndex, Position.LEFT) === Location.NONE, 'found single null side');
105939                 label$1.setLocation(geomIndex, Position.RIGHT, currLoc);
105940                 label$1.setLocation(geomIndex, Position.LEFT, currLoc);
105941               }
105942             }
105943           }
105944         };
105945         EdgeEndStar.prototype.getCoordinate = function getCoordinate () {
105946           var it = this.iterator();
105947           if (!it.hasNext()) { return null }
105948           var e = it.next();
105949           return e.getCoordinate()
105950         };
105951         EdgeEndStar.prototype.print = function print (out) {
105952           System.out.println('EdgeEndStar:   ' + this.getCoordinate());
105953           for (var it = this.iterator(); it.hasNext();) {
105954             var e = it.next();
105955             e.print(out);
105956           }
105957         };
105958         EdgeEndStar.prototype.isAreaLabelsConsistent = function isAreaLabelsConsistent (geomGraph) {
105959           this.computeEdgeEndLabels(geomGraph.getBoundaryNodeRule());
105960           return this.checkAreaLabelsConsistent(0)
105961         };
105962         EdgeEndStar.prototype.checkAreaLabelsConsistent = function checkAreaLabelsConsistent (geomIndex) {
105963           var edges = this.getEdges();
105964           if (edges.size() <= 0) { return true }
105965           var lastEdgeIndex = edges.size() - 1;
105966           var startLabel = edges.get(lastEdgeIndex).getLabel();
105967           var startLoc = startLabel.getLocation(geomIndex, Position.LEFT);
105968           Assert.isTrue(startLoc !== Location.NONE, 'Found unlabelled area edge');
105969           var currLoc = startLoc;
105970           for (var it = this.iterator(); it.hasNext();) {
105971             var e = it.next();
105972             var label = e.getLabel();
105973             Assert.isTrue(label.isArea(geomIndex), 'Found non-area edge');
105974             var leftLoc = label.getLocation(geomIndex, Position.LEFT);
105975             var rightLoc = label.getLocation(geomIndex, Position.RIGHT);
105976             if (leftLoc === rightLoc) {
105977               return false
105978             }
105979             if (rightLoc !== currLoc) {
105980               return false
105981             }
105982             currLoc = leftLoc;
105983           }
105984           return true
105985         };
105986         EdgeEndStar.prototype.findIndex = function findIndex (eSearch) {
105987             var this$1 = this;
105988
105989           this.iterator();
105990           for (var i = 0; i < this._edgeList.size(); i++) {
105991             var e = this$1._edgeList.get(i);
105992             if (e === eSearch) { return i }
105993           }
105994           return -1
105995         };
105996         EdgeEndStar.prototype.iterator = function iterator () {
105997           return this.getEdges().iterator()
105998         };
105999         EdgeEndStar.prototype.getEdges = function getEdges () {
106000           if (this._edgeList === null) {
106001             this._edgeList = new ArrayList(this._edgeMap.values());
106002           }
106003           return this._edgeList
106004         };
106005         EdgeEndStar.prototype.getLocation = function getLocation (geomIndex, p, geom) {
106006           if (this._ptInAreaLocation[geomIndex] === Location.NONE) {
106007             this._ptInAreaLocation[geomIndex] = SimplePointInAreaLocator.locate(p, geom[geomIndex].getGeometry());
106008           }
106009           return this._ptInAreaLocation[geomIndex]
106010         };
106011         EdgeEndStar.prototype.toString = function toString () {
106012           var buf = new StringBuffer();
106013           buf.append('EdgeEndStar:   ' + this.getCoordinate());
106014           buf.append('\n');
106015           for (var it = this.iterator(); it.hasNext();) {
106016             var e = it.next();
106017             buf.append(e);
106018             buf.append('\n');
106019           }
106020           return buf.toString()
106021         };
106022         EdgeEndStar.prototype.computeEdgeEndLabels = function computeEdgeEndLabels (boundaryNodeRule) {
106023           for (var it = this.iterator(); it.hasNext();) {
106024             var ee = it.next();
106025             ee.computeLabel(boundaryNodeRule);
106026           }
106027         };
106028         EdgeEndStar.prototype.computeLabelling = function computeLabelling (geomGraph) {
106029             var this$1 = this;
106030
106031           this.computeEdgeEndLabels(geomGraph[0].getBoundaryNodeRule());
106032           this.propagateSideLabels(0);
106033           this.propagateSideLabels(1);
106034           var hasDimensionalCollapseEdge = [false, false];
106035           for (var it = this.iterator(); it.hasNext();) {
106036             var e = it.next();
106037             var label = e.getLabel();
106038             for (var geomi = 0; geomi < 2; geomi++) {
106039               if (label.isLine(geomi) && label.getLocation(geomi) === Location.BOUNDARY) { hasDimensionalCollapseEdge[geomi] = true; }
106040             }
106041           }
106042           for (var it$1 = this.iterator(); it$1.hasNext();) {
106043             var e$1 = it$1.next();
106044             var label$1 = e$1.getLabel();
106045             for (var geomi$1 = 0; geomi$1 < 2; geomi$1++) {
106046               if (label$1.isAnyNull(geomi$1)) {
106047                 var loc = Location.NONE;
106048                 if (hasDimensionalCollapseEdge[geomi$1]) {
106049                   loc = Location.EXTERIOR;
106050                 } else {
106051                   var p = e$1.getCoordinate();
106052                   loc = this$1.getLocation(geomi$1, p, geomGraph);
106053                 }
106054                 label$1.setAllLocationsIfNull(geomi$1, loc);
106055               }
106056             }
106057           }
106058         };
106059         EdgeEndStar.prototype.getDegree = function getDegree () {
106060           return this._edgeMap.size()
106061         };
106062         EdgeEndStar.prototype.insertEdgeEnd = function insertEdgeEnd (e, obj) {
106063           this._edgeMap.put(e, obj);
106064           this._edgeList = null;
106065         };
106066         EdgeEndStar.prototype.interfaces_ = function interfaces_ () {
106067           return []
106068         };
106069         EdgeEndStar.prototype.getClass = function getClass () {
106070           return EdgeEndStar
106071         };
106072
106073         var DirectedEdgeStar = (function (EdgeEndStar$$1) {
106074           function DirectedEdgeStar () {
106075             EdgeEndStar$$1.call(this);
106076             this._resultAreaEdgeList = null;
106077             this._label = null;
106078             this._SCANNING_FOR_INCOMING = 1;
106079             this._LINKING_TO_OUTGOING = 2;
106080           }
106081
106082           if ( EdgeEndStar$$1 ) DirectedEdgeStar.__proto__ = EdgeEndStar$$1;
106083           DirectedEdgeStar.prototype = Object.create( EdgeEndStar$$1 && EdgeEndStar$$1.prototype );
106084           DirectedEdgeStar.prototype.constructor = DirectedEdgeStar;
106085           DirectedEdgeStar.prototype.linkResultDirectedEdges = function linkResultDirectedEdges () {
106086             var this$1 = this;
106087
106088             this.getResultAreaEdges();
106089             var firstOut = null;
106090             var incoming = null;
106091             var state = this._SCANNING_FOR_INCOMING;
106092             for (var i = 0; i < this._resultAreaEdgeList.size(); i++) {
106093               var nextOut = this$1._resultAreaEdgeList.get(i);
106094               var nextIn = nextOut.getSym();
106095               if (!nextOut.getLabel().isArea()) { continue }
106096               if (firstOut === null && nextOut.isInResult()) { firstOut = nextOut; }
106097               switch (state) {
106098                 case this$1._SCANNING_FOR_INCOMING:
106099                   if (!nextIn.isInResult()) { continue }
106100                   incoming = nextIn;
106101                   state = this$1._LINKING_TO_OUTGOING;
106102                   break
106103                 case this$1._LINKING_TO_OUTGOING:
106104                   if (!nextOut.isInResult()) { continue }
106105                   incoming.setNext(nextOut);
106106                   state = this$1._SCANNING_FOR_INCOMING;
106107                   break
106108               }
106109             }
106110             if (state === this._LINKING_TO_OUTGOING) {
106111               if (firstOut === null) { throw new TopologyException('no outgoing dirEdge found', this.getCoordinate()) }
106112               Assert.isTrue(firstOut.isInResult(), 'unable to link last incoming dirEdge');
106113               incoming.setNext(firstOut);
106114             }
106115           };
106116           DirectedEdgeStar.prototype.insert = function insert (ee) {
106117             var de = ee;
106118             this.insertEdgeEnd(de, de);
106119           };
106120           DirectedEdgeStar.prototype.getRightmostEdge = function getRightmostEdge () {
106121             var edges = this.getEdges();
106122             var size = edges.size();
106123             if (size < 1) { return null }
106124             var de0 = edges.get(0);
106125             if (size === 1) { return de0 }
106126             var deLast = edges.get(size - 1);
106127             var quad0 = de0.getQuadrant();
106128             var quad1 = deLast.getQuadrant();
106129             if (Quadrant.isNorthern(quad0) && Quadrant.isNorthern(quad1)) { return de0; } else if (!Quadrant.isNorthern(quad0) && !Quadrant.isNorthern(quad1)) { return deLast; } else {
106130               // const nonHorizontalEdge = null
106131               if (de0.getDy() !== 0) { return de0; } else if (deLast.getDy() !== 0) { return deLast }
106132             }
106133             Assert.shouldNeverReachHere('found two horizontal edges incident on node');
106134             return null
106135           };
106136           DirectedEdgeStar.prototype.print = function print (out) {
106137             System.out.println('DirectedEdgeStar: ' + this.getCoordinate());
106138             for (var it = this.iterator(); it.hasNext();) {
106139               var de = it.next();
106140               out.print('out ');
106141               de.print(out);
106142               out.println();
106143               out.print('in ');
106144               de.getSym().print(out);
106145               out.println();
106146             }
106147           };
106148           DirectedEdgeStar.prototype.getResultAreaEdges = function getResultAreaEdges () {
106149             var this$1 = this;
106150
106151             if (this._resultAreaEdgeList !== null) { return this._resultAreaEdgeList }
106152             this._resultAreaEdgeList = new ArrayList();
106153             for (var it = this.iterator(); it.hasNext();) {
106154               var de = it.next();
106155               if (de.isInResult() || de.getSym().isInResult()) { this$1._resultAreaEdgeList.add(de); }
106156             }
106157             return this._resultAreaEdgeList
106158           };
106159           DirectedEdgeStar.prototype.updateLabelling = function updateLabelling (nodeLabel) {
106160             for (var it = this.iterator(); it.hasNext();) {
106161               var de = it.next();
106162               var label = de.getLabel();
106163               label.setAllLocationsIfNull(0, nodeLabel.getLocation(0));
106164               label.setAllLocationsIfNull(1, nodeLabel.getLocation(1));
106165             }
106166           };
106167           DirectedEdgeStar.prototype.linkAllDirectedEdges = function linkAllDirectedEdges () {
106168             var this$1 = this;
106169
106170             this.getEdges();
106171             var prevOut = null;
106172             var firstIn = null;
106173             for (var i = this._edgeList.size() - 1; i >= 0; i--) {
106174               var nextOut = this$1._edgeList.get(i);
106175               var nextIn = nextOut.getSym();
106176               if (firstIn === null) { firstIn = nextIn; }
106177               if (prevOut !== null) { nextIn.setNext(prevOut); }
106178               prevOut = nextOut;
106179             }
106180             firstIn.setNext(prevOut);
106181           };
106182           DirectedEdgeStar.prototype.computeDepths = function computeDepths () {
106183             var this$1 = this;
106184
106185             if (arguments.length === 1) {
106186               var de = arguments[0];
106187               var edgeIndex = this.findIndex(de);
106188               // const label = de.getLabel()
106189               var startDepth = de.getDepth(Position.LEFT);
106190               var targetLastDepth = de.getDepth(Position.RIGHT);
106191               var nextDepth = this.computeDepths(edgeIndex + 1, this._edgeList.size(), startDepth);
106192               var lastDepth = this.computeDepths(0, edgeIndex, nextDepth);
106193               if (lastDepth !== targetLastDepth) { throw new TopologyException('depth mismatch at ' + de.getCoordinate()) }
106194             } else if (arguments.length === 3) {
106195               var startIndex = arguments[0];
106196               var endIndex = arguments[1];
106197               var startDepth$1 = arguments[2];
106198               var currDepth = startDepth$1;
106199               for (var i = startIndex; i < endIndex; i++) {
106200                 var nextDe = this$1._edgeList.get(i);
106201                 // const label = nextDe.getLabel()
106202                 nextDe.setEdgeDepths(Position.RIGHT, currDepth);
106203                 currDepth = nextDe.getDepth(Position.LEFT);
106204               }
106205               return currDepth
106206             }
106207           };
106208           DirectedEdgeStar.prototype.mergeSymLabels = function mergeSymLabels () {
106209             for (var it = this.iterator(); it.hasNext();) {
106210               var de = it.next();
106211               var label = de.getLabel();
106212               label.merge(de.getSym().getLabel());
106213             }
106214           };
106215           DirectedEdgeStar.prototype.linkMinimalDirectedEdges = function linkMinimalDirectedEdges (er) {
106216             var this$1 = this;
106217
106218             var firstOut = null;
106219             var incoming = null;
106220             var state = this._SCANNING_FOR_INCOMING;
106221             for (var i = this._resultAreaEdgeList.size() - 1; i >= 0; i--) {
106222               var nextOut = this$1._resultAreaEdgeList.get(i);
106223               var nextIn = nextOut.getSym();
106224               if (firstOut === null && nextOut.getEdgeRing() === er) { firstOut = nextOut; }
106225               switch (state) {
106226                 case this$1._SCANNING_FOR_INCOMING:
106227                   if (nextIn.getEdgeRing() !== er) { continue }
106228                   incoming = nextIn;
106229                   state = this$1._LINKING_TO_OUTGOING;
106230                   break
106231                 case this$1._LINKING_TO_OUTGOING:
106232                   if (nextOut.getEdgeRing() !== er) { continue }
106233                   incoming.setNextMin(nextOut);
106234                   state = this$1._SCANNING_FOR_INCOMING;
106235                   break
106236               }
106237             }
106238             if (state === this._LINKING_TO_OUTGOING) {
106239               Assert.isTrue(firstOut !== null, 'found null for first outgoing dirEdge');
106240               Assert.isTrue(firstOut.getEdgeRing() === er, 'unable to link last incoming dirEdge');
106241               incoming.setNextMin(firstOut);
106242             }
106243           };
106244           DirectedEdgeStar.prototype.getOutgoingDegree = function getOutgoingDegree () {
106245             if (arguments.length === 0) {
106246               var degree = 0;
106247               for (var it = this.iterator(); it.hasNext();) {
106248                 var de = it.next();
106249                 if (de.isInResult()) { degree++; }
106250               }
106251               return degree
106252             } else if (arguments.length === 1) {
106253               var er = arguments[0];
106254               var degree$1 = 0;
106255               for (var it$1 = this.iterator(); it$1.hasNext();) {
106256                 var de$1 = it$1.next();
106257                 if (de$1.getEdgeRing() === er) { degree$1++; }
106258               }
106259               return degree$1
106260             }
106261           };
106262           DirectedEdgeStar.prototype.getLabel = function getLabel () {
106263             return this._label
106264           };
106265           DirectedEdgeStar.prototype.findCoveredLineEdges = function findCoveredLineEdges () {
106266             var startLoc = Location.NONE;
106267             for (var it = this.iterator(); it.hasNext();) {
106268               var nextOut = it.next();
106269               var nextIn = nextOut.getSym();
106270               if (!nextOut.isLineEdge()) {
106271                 if (nextOut.isInResult()) {
106272                   startLoc = Location.INTERIOR;
106273                   break
106274                 }
106275                 if (nextIn.isInResult()) {
106276                   startLoc = Location.EXTERIOR;
106277                   break
106278                 }
106279               }
106280             }
106281             if (startLoc === Location.NONE) { return null }
106282             var currLoc = startLoc;
106283             for (var it$1 = this.iterator(); it$1.hasNext();) {
106284               var nextOut$1 = it$1.next();
106285               var nextIn$1 = nextOut$1.getSym();
106286               if (nextOut$1.isLineEdge()) {
106287                 nextOut$1.getEdge().setCovered(currLoc === Location.INTERIOR);
106288               } else {
106289                 if (nextOut$1.isInResult()) { currLoc = Location.EXTERIOR; }
106290                 if (nextIn$1.isInResult()) { currLoc = Location.INTERIOR; }
106291               }
106292             }
106293           };
106294           DirectedEdgeStar.prototype.computeLabelling = function computeLabelling (geom) {
106295             var this$1 = this;
106296
106297             EdgeEndStar$$1.prototype.computeLabelling.call(this, geom);
106298             this._label = new Label(Location.NONE);
106299             for (var it = this.iterator(); it.hasNext();) {
106300               var ee = it.next();
106301               var e = ee.getEdge();
106302               var eLabel = e.getLabel();
106303               for (var i = 0; i < 2; i++) {
106304                 var eLoc = eLabel.getLocation(i);
106305                 if (eLoc === Location.INTERIOR || eLoc === Location.BOUNDARY) { this$1._label.setLocation(i, Location.INTERIOR); }
106306               }
106307             }
106308           };
106309           DirectedEdgeStar.prototype.interfaces_ = function interfaces_ () {
106310             return []
106311           };
106312           DirectedEdgeStar.prototype.getClass = function getClass () {
106313             return DirectedEdgeStar
106314           };
106315
106316           return DirectedEdgeStar;
106317         }(EdgeEndStar));
106318
106319         var OverlayNodeFactory = (function (NodeFactory$$1) {
106320           function OverlayNodeFactory () {
106321             NodeFactory$$1.apply(this, arguments);
106322           }
106323
106324           if ( NodeFactory$$1 ) OverlayNodeFactory.__proto__ = NodeFactory$$1;
106325           OverlayNodeFactory.prototype = Object.create( NodeFactory$$1 && NodeFactory$$1.prototype );
106326           OverlayNodeFactory.prototype.constructor = OverlayNodeFactory;
106327
106328           OverlayNodeFactory.prototype.createNode = function createNode (coord) {
106329             return new Node$1(coord, new DirectedEdgeStar())
106330           };
106331           OverlayNodeFactory.prototype.interfaces_ = function interfaces_ () {
106332             return []
106333           };
106334           OverlayNodeFactory.prototype.getClass = function getClass () {
106335             return OverlayNodeFactory
106336           };
106337
106338           return OverlayNodeFactory;
106339         }(NodeFactory));
106340
106341         var OrientedCoordinateArray = function OrientedCoordinateArray () {
106342           this._pts = null;
106343           this._orientation = null;
106344           var pts = arguments[0];
106345           this._pts = pts;
106346           this._orientation = OrientedCoordinateArray.orientation(pts);
106347         };
106348         OrientedCoordinateArray.prototype.compareTo = function compareTo (o1) {
106349           var oca = o1;
106350           var comp = OrientedCoordinateArray.compareOriented(this._pts, this._orientation, oca._pts, oca._orientation);
106351           return comp
106352         };
106353         OrientedCoordinateArray.prototype.interfaces_ = function interfaces_ () {
106354           return [Comparable]
106355         };
106356         OrientedCoordinateArray.prototype.getClass = function getClass () {
106357           return OrientedCoordinateArray
106358         };
106359         OrientedCoordinateArray.orientation = function orientation (pts) {
106360           return CoordinateArrays.increasingDirection(pts) === 1
106361         };
106362         OrientedCoordinateArray.compareOriented = function compareOriented (pts1, orientation1, pts2, orientation2) {
106363           var dir1 = orientation1 ? 1 : -1;
106364           var dir2 = orientation2 ? 1 : -1;
106365           var limit1 = orientation1 ? pts1.length : -1;
106366           var limit2 = orientation2 ? pts2.length : -1;
106367           var i1 = orientation1 ? 0 : pts1.length - 1;
106368           var i2 = orientation2 ? 0 : pts2.length - 1;
106369           // const comp = 0
106370           while (true) {
106371             var compPt = pts1[i1].compareTo(pts2[i2]);
106372             if (compPt !== 0) { return compPt }
106373             i1 += dir1;
106374             i2 += dir2;
106375             var done1 = i1 === limit1;
106376             var done2 = i2 === limit2;
106377             if (done1 && !done2) { return -1 }
106378             if (!done1 && done2) { return 1 }
106379             if (done1 && done2) { return 0 }
106380           }
106381         };
106382
106383         var EdgeList = function EdgeList () {
106384           this._edges = new ArrayList();
106385           this._ocaMap = new TreeMap();
106386         };
106387         EdgeList.prototype.print = function print (out) {
106388             var this$1 = this;
106389
106390           out.print('MULTILINESTRING ( ');
106391           for (var j = 0; j < this._edges.size(); j++) {
106392             var e = this$1._edges.get(j);
106393             if (j > 0) { out.print(','); }
106394             out.print('(');
106395             var pts = e.getCoordinates();
106396             for (var i = 0; i < pts.length; i++) {
106397               if (i > 0) { out.print(','); }
106398               out.print(pts[i].x + ' ' + pts[i].y);
106399             }
106400             out.println(')');
106401           }
106402           out.print(')  ');
106403         };
106404         EdgeList.prototype.addAll = function addAll (edgeColl) {
106405             var this$1 = this;
106406
106407           for (var i = edgeColl.iterator(); i.hasNext();) {
106408             this$1.add(i.next());
106409           }
106410         };
106411         EdgeList.prototype.findEdgeIndex = function findEdgeIndex (e) {
106412             var this$1 = this;
106413
106414           for (var i = 0; i < this._edges.size(); i++) {
106415             if (this$1._edges.get(i).equals(e)) { return i }
106416           }
106417           return -1
106418         };
106419         EdgeList.prototype.iterator = function iterator () {
106420           return this._edges.iterator()
106421         };
106422         EdgeList.prototype.getEdges = function getEdges () {
106423           return this._edges
106424         };
106425         EdgeList.prototype.get = function get (i) {
106426           return this._edges.get(i)
106427         };
106428         EdgeList.prototype.findEqualEdge = function findEqualEdge (e) {
106429           var oca = new OrientedCoordinateArray(e.getCoordinates());
106430           var matchEdge = this._ocaMap.get(oca);
106431           return matchEdge
106432         };
106433         EdgeList.prototype.add = function add (e) {
106434           this._edges.add(e);
106435           var oca = new OrientedCoordinateArray(e.getCoordinates());
106436           this._ocaMap.put(oca, e);
106437         };
106438         EdgeList.prototype.interfaces_ = function interfaces_ () {
106439           return []
106440         };
106441         EdgeList.prototype.getClass = function getClass () {
106442           return EdgeList
106443         };
106444
106445         var SegmentIntersector = function SegmentIntersector () {};
106446
106447         SegmentIntersector.prototype.processIntersections = function processIntersections (e0, segIndex0, e1, segIndex1) {};
106448         SegmentIntersector.prototype.isDone = function isDone () {};
106449         SegmentIntersector.prototype.interfaces_ = function interfaces_ () {
106450           return []
106451         };
106452         SegmentIntersector.prototype.getClass = function getClass () {
106453           return SegmentIntersector
106454         };
106455
106456         var IntersectionAdder = function IntersectionAdder () {
106457           this._hasIntersection = false;
106458           this._hasProper = false;
106459           this._hasProperInterior = false;
106460           this._hasInterior = false;
106461           this._properIntersectionPoint = null;
106462           this._li = null;
106463           this._isSelfIntersection = null;
106464           this.numIntersections = 0;
106465           this.numInteriorIntersections = 0;
106466           this.numProperIntersections = 0;
106467           this.numTests = 0;
106468           var li = arguments[0];
106469           this._li = li;
106470         };
106471         IntersectionAdder.prototype.isTrivialIntersection = function isTrivialIntersection (e0, segIndex0, e1, segIndex1) {
106472           if (e0 === e1) {
106473             if (this._li.getIntersectionNum() === 1) {
106474               if (IntersectionAdder.isAdjacentSegments(segIndex0, segIndex1)) { return true }
106475               if (e0.isClosed()) {
106476                 var maxSegIndex = e0.size() - 1;
106477                 if ((segIndex0 === 0 && segIndex1 === maxSegIndex) ||
106478                     (segIndex1 === 0 && segIndex0 === maxSegIndex)) {
106479                   return true
106480                 }
106481               }
106482             }
106483           }
106484           return false
106485         };
106486         IntersectionAdder.prototype.getProperIntersectionPoint = function getProperIntersectionPoint () {
106487           return this._properIntersectionPoint
106488         };
106489         IntersectionAdder.prototype.hasProperInteriorIntersection = function hasProperInteriorIntersection () {
106490           return this._hasProperInterior
106491         };
106492         IntersectionAdder.prototype.getLineIntersector = function getLineIntersector () {
106493           return this._li
106494         };
106495         IntersectionAdder.prototype.hasProperIntersection = function hasProperIntersection () {
106496           return this._hasProper
106497         };
106498         IntersectionAdder.prototype.processIntersections = function processIntersections (e0, segIndex0, e1, segIndex1) {
106499           if (e0 === e1 && segIndex0 === segIndex1) { return null }
106500           this.numTests++;
106501           var p00 = e0.getCoordinates()[segIndex0];
106502           var p01 = e0.getCoordinates()[segIndex0 + 1];
106503           var p10 = e1.getCoordinates()[segIndex1];
106504           var p11 = e1.getCoordinates()[segIndex1 + 1];
106505           this._li.computeIntersection(p00, p01, p10, p11);
106506           if (this._li.hasIntersection()) {
106507             this.numIntersections++;
106508             if (this._li.isInteriorIntersection()) {
106509               this.numInteriorIntersections++;
106510               this._hasInterior = true;
106511             }
106512             if (!this.isTrivialIntersection(e0, segIndex0, e1, segIndex1)) {
106513               this._hasIntersection = true;
106514               e0.addIntersections(this._li, segIndex0, 0);
106515               e1.addIntersections(this._li, segIndex1, 1);
106516               if (this._li.isProper()) {
106517                 this.numProperIntersections++;
106518                 this._hasProper = true;
106519                 this._hasProperInterior = true;
106520               }
106521             }
106522           }
106523         };
106524         IntersectionAdder.prototype.hasIntersection = function hasIntersection () {
106525           return this._hasIntersection
106526         };
106527         IntersectionAdder.prototype.isDone = function isDone () {
106528           return false
106529         };
106530         IntersectionAdder.prototype.hasInteriorIntersection = function hasInteriorIntersection () {
106531           return this._hasInterior
106532         };
106533         IntersectionAdder.prototype.interfaces_ = function interfaces_ () {
106534           return [SegmentIntersector]
106535         };
106536         IntersectionAdder.prototype.getClass = function getClass () {
106537           return IntersectionAdder
106538         };
106539         IntersectionAdder.isAdjacentSegments = function isAdjacentSegments (i1, i2) {
106540           return Math.abs(i1 - i2) === 1
106541         };
106542
106543         var EdgeIntersection = function EdgeIntersection () {
106544           this.coord = null;
106545           this.segmentIndex = null;
106546           this.dist = null;
106547           var coord = arguments[0];
106548           var segmentIndex = arguments[1];
106549           var dist = arguments[2];
106550           this.coord = new Coordinate(coord);
106551           this.segmentIndex = segmentIndex;
106552           this.dist = dist;
106553         };
106554         EdgeIntersection.prototype.getSegmentIndex = function getSegmentIndex () {
106555           return this.segmentIndex
106556         };
106557         EdgeIntersection.prototype.getCoordinate = function getCoordinate () {
106558           return this.coord
106559         };
106560         EdgeIntersection.prototype.print = function print (out) {
106561           out.print(this.coord);
106562           out.print(' seg # = ' + this.segmentIndex);
106563           out.println(' dist = ' + this.dist);
106564         };
106565         EdgeIntersection.prototype.compareTo = function compareTo (obj) {
106566           var other = obj;
106567           return this.compare(other.segmentIndex, other.dist)
106568         };
106569         EdgeIntersection.prototype.isEndPoint = function isEndPoint (maxSegmentIndex) {
106570           if (this.segmentIndex === 0 && this.dist === 0.0) { return true }
106571           if (this.segmentIndex === maxSegmentIndex) { return true }
106572           return false
106573         };
106574         EdgeIntersection.prototype.toString = function toString () {
106575           return this.coord + ' seg # = ' + this.segmentIndex + ' dist = ' + this.dist
106576         };
106577         EdgeIntersection.prototype.getDistance = function getDistance () {
106578           return this.dist
106579         };
106580         EdgeIntersection.prototype.compare = function compare (segmentIndex, dist) {
106581           if (this.segmentIndex < segmentIndex) { return -1 }
106582           if (this.segmentIndex > segmentIndex) { return 1 }
106583           if (this.dist < dist) { return -1 }
106584           if (this.dist > dist) { return 1 }
106585           return 0
106586         };
106587         EdgeIntersection.prototype.interfaces_ = function interfaces_ () {
106588           return [Comparable]
106589         };
106590         EdgeIntersection.prototype.getClass = function getClass () {
106591           return EdgeIntersection
106592         };
106593
106594         var EdgeIntersectionList = function EdgeIntersectionList () {
106595           this._nodeMap = new TreeMap();
106596           this.edge = null;
106597           var edge = arguments[0];
106598           this.edge = edge;
106599         };
106600         EdgeIntersectionList.prototype.print = function print (out) {
106601           out.println('Intersections:');
106602           for (var it = this.iterator(); it.hasNext();) {
106603             var ei = it.next();
106604             ei.print(out);
106605           }
106606         };
106607         EdgeIntersectionList.prototype.iterator = function iterator () {
106608           return this._nodeMap.values().iterator()
106609         };
106610         EdgeIntersectionList.prototype.addSplitEdges = function addSplitEdges (edgeList) {
106611             var this$1 = this;
106612
106613           this.addEndpoints();
106614           var it = this.iterator();
106615           var eiPrev = it.next();
106616           while (it.hasNext()) {
106617             var ei = it.next();
106618             var newEdge = this$1.createSplitEdge(eiPrev, ei);
106619             edgeList.add(newEdge);
106620             eiPrev = ei;
106621           }
106622         };
106623         EdgeIntersectionList.prototype.addEndpoints = function addEndpoints () {
106624           var maxSegIndex = this.edge.pts.length - 1;
106625           this.add(this.edge.pts[0], 0, 0.0);
106626           this.add(this.edge.pts[maxSegIndex], maxSegIndex, 0.0);
106627         };
106628         EdgeIntersectionList.prototype.createSplitEdge = function createSplitEdge (ei0, ei1) {
106629             var this$1 = this;
106630
106631           var npts = ei1.segmentIndex - ei0.segmentIndex + 2;
106632           var lastSegStartPt = this.edge.pts[ei1.segmentIndex];
106633           var useIntPt1 = ei1.dist > 0.0 || !ei1.coord.equals2D(lastSegStartPt);
106634           if (!useIntPt1) {
106635             npts--;
106636           }
106637           var pts = new Array(npts).fill(null);
106638           var ipt = 0;
106639           pts[ipt++] = new Coordinate(ei0.coord);
106640           for (var i = ei0.segmentIndex + 1; i <= ei1.segmentIndex; i++) {
106641             pts[ipt++] = this$1.edge.pts[i];
106642           }
106643           if (useIntPt1) { pts[ipt] = ei1.coord; }
106644           return new Edge(pts, new Label(this.edge._label))
106645         };
106646         EdgeIntersectionList.prototype.add = function add (intPt, segmentIndex, dist) {
106647           var eiNew = new EdgeIntersection(intPt, segmentIndex, dist);
106648           var ei = this._nodeMap.get(eiNew);
106649           if (ei !== null) {
106650             return ei
106651           }
106652           this._nodeMap.put(eiNew, eiNew);
106653           return eiNew
106654         };
106655         EdgeIntersectionList.prototype.isIntersection = function isIntersection (pt) {
106656           for (var it = this.iterator(); it.hasNext();) {
106657             var ei = it.next();
106658             if (ei.coord.equals(pt)) { return true }
106659           }
106660           return false
106661         };
106662         EdgeIntersectionList.prototype.interfaces_ = function interfaces_ () {
106663           return []
106664         };
106665         EdgeIntersectionList.prototype.getClass = function getClass () {
106666           return EdgeIntersectionList
106667         };
106668
106669         var MonotoneChainIndexer = function MonotoneChainIndexer () {};
106670
106671         MonotoneChainIndexer.prototype.getChainStartIndices = function getChainStartIndices (pts) {
106672             var this$1 = this;
106673
106674           var start = 0;
106675           var startIndexList = new ArrayList();
106676           startIndexList.add(new Integer(start));
106677           do {
106678             var last = this$1.findChainEnd(pts, start);
106679             startIndexList.add(new Integer(last));
106680             start = last;
106681           } while (start < pts.length - 1)
106682           var startIndex = MonotoneChainIndexer.toIntArray(startIndexList);
106683           return startIndex
106684         };
106685         MonotoneChainIndexer.prototype.findChainEnd = function findChainEnd (pts, start) {
106686           var chainQuad = Quadrant.quadrant(pts[start], pts[start + 1]);
106687           var last = start + 1;
106688           while (last < pts.length) {
106689             var quad = Quadrant.quadrant(pts[last - 1], pts[last]);
106690             if (quad !== chainQuad) { break }
106691             last++;
106692           }
106693           return last - 1
106694         };
106695         MonotoneChainIndexer.prototype.interfaces_ = function interfaces_ () {
106696           return []
106697         };
106698         MonotoneChainIndexer.prototype.getClass = function getClass () {
106699           return MonotoneChainIndexer
106700         };
106701         MonotoneChainIndexer.toIntArray = function toIntArray (list) {
106702           var array = new Array(list.size()).fill(null);
106703           for (var i = 0; i < array.length; i++) {
106704             array[i] = list.get(i).intValue();
106705           }
106706           return array
106707         };
106708
106709         var MonotoneChainEdge = function MonotoneChainEdge () {
106710           this.e = null;
106711           this.pts = null;
106712           this.startIndex = null;
106713           this.env1 = new Envelope();
106714           this.env2 = new Envelope();
106715           var e = arguments[0];
106716           this.e = e;
106717           this.pts = e.getCoordinates();
106718           var mcb = new MonotoneChainIndexer();
106719           this.startIndex = mcb.getChainStartIndices(this.pts);
106720         };
106721         MonotoneChainEdge.prototype.getCoordinates = function getCoordinates () {
106722           return this.pts
106723         };
106724         MonotoneChainEdge.prototype.getMaxX = function getMaxX (chainIndex) {
106725           var x1 = this.pts[this.startIndex[chainIndex]].x;
106726           var x2 = this.pts[this.startIndex[chainIndex + 1]].x;
106727           return x1 > x2 ? x1 : x2
106728         };
106729         MonotoneChainEdge.prototype.getMinX = function getMinX (chainIndex) {
106730           var x1 = this.pts[this.startIndex[chainIndex]].x;
106731           var x2 = this.pts[this.startIndex[chainIndex + 1]].x;
106732           return x1 < x2 ? x1 : x2
106733         };
106734         MonotoneChainEdge.prototype.computeIntersectsForChain = function computeIntersectsForChain () {
106735           if (arguments.length === 4) {
106736             var chainIndex0 = arguments[0];
106737             var mce = arguments[1];
106738             var chainIndex1 = arguments[2];
106739             var si = arguments[3];
106740             this.computeIntersectsForChain(this.startIndex[chainIndex0], this.startIndex[chainIndex0 + 1], mce, mce.startIndex[chainIndex1], mce.startIndex[chainIndex1 + 1], si);
106741           } else if (arguments.length === 6) {
106742             var start0 = arguments[0];
106743             var end0 = arguments[1];
106744             var mce$1 = arguments[2];
106745             var start1 = arguments[3];
106746             var end1 = arguments[4];
106747             var ei = arguments[5];
106748             var p00 = this.pts[start0];
106749             var p01 = this.pts[end0];
106750             var p10 = mce$1.pts[start1];
106751             var p11 = mce$1.pts[end1];
106752             if (end0 - start0 === 1 && end1 - start1 === 1) {
106753               ei.addIntersections(this.e, start0, mce$1.e, start1);
106754               return null
106755             }
106756             this.env1.init(p00, p01);
106757             this.env2.init(p10, p11);
106758             if (!this.env1.intersects(this.env2)) { return null }
106759             var mid0 = Math.trunc((start0 + end0) / 2);
106760             var mid1 = Math.trunc((start1 + end1) / 2);
106761             if (start0 < mid0) {
106762               if (start1 < mid1) { this.computeIntersectsForChain(start0, mid0, mce$1, start1, mid1, ei); }
106763               if (mid1 < end1) { this.computeIntersectsForChain(start0, mid0, mce$1, mid1, end1, ei); }
106764             }
106765             if (mid0 < end0) {
106766               if (start1 < mid1) { this.computeIntersectsForChain(mid0, end0, mce$1, start1, mid1, ei); }
106767               if (mid1 < end1) { this.computeIntersectsForChain(mid0, end0, mce$1, mid1, end1, ei); }
106768             }
106769           }
106770         };
106771         MonotoneChainEdge.prototype.getStartIndexes = function getStartIndexes () {
106772           return this.startIndex
106773         };
106774         MonotoneChainEdge.prototype.computeIntersects = function computeIntersects (mce, si) {
106775             var this$1 = this;
106776
106777           for (var i = 0; i < this.startIndex.length - 1; i++) {
106778             for (var j = 0; j < mce.startIndex.length - 1; j++) {
106779               this$1.computeIntersectsForChain(i, mce, j, si);
106780             }
106781           }
106782         };
106783         MonotoneChainEdge.prototype.interfaces_ = function interfaces_ () {
106784           return []
106785         };
106786         MonotoneChainEdge.prototype.getClass = function getClass () {
106787           return MonotoneChainEdge
106788         };
106789
106790         var Depth = function Depth () {
106791           var this$1 = this;
106792
106793           this._depth = Array(2).fill().map(function () { return Array(3); });
106794           for (var i = 0; i < 2; i++) {
106795             for (var j = 0; j < 3; j++) {
106796               this$1._depth[i][j] = Depth.NULL_VALUE;
106797             }
106798           }
106799         };
106800
106801         var staticAccessors$31 = { NULL_VALUE: { configurable: true } };
106802         Depth.prototype.getDepth = function getDepth (geomIndex, posIndex) {
106803           return this._depth[geomIndex][posIndex]
106804         };
106805         Depth.prototype.setDepth = function setDepth (geomIndex, posIndex, depthValue) {
106806           this._depth[geomIndex][posIndex] = depthValue;
106807         };
106808         Depth.prototype.isNull = function isNull () {
106809             var this$1 = this;
106810
106811           if (arguments.length === 0) {
106812             for (var i = 0; i < 2; i++) {
106813               for (var j = 0; j < 3; j++) {
106814                 if (this$1._depth[i][j] !== Depth.NULL_VALUE) { return false }
106815               }
106816             }
106817             return true
106818           } else if (arguments.length === 1) {
106819             var geomIndex = arguments[0];
106820             return this._depth[geomIndex][1] === Depth.NULL_VALUE
106821           } else if (arguments.length === 2) {
106822             var geomIndex$1 = arguments[0];
106823             var posIndex = arguments[1];
106824             return this._depth[geomIndex$1][posIndex] === Depth.NULL_VALUE
106825           }
106826         };
106827         Depth.prototype.normalize = function normalize () {
106828             var this$1 = this;
106829
106830           for (var i = 0; i < 2; i++) {
106831             if (!this$1.isNull(i)) {
106832               var minDepth = this$1._depth[i][1];
106833               if (this$1._depth[i][2] < minDepth) { minDepth = this$1._depth[i][2]; }
106834               if (minDepth < 0) { minDepth = 0; }
106835               for (var j = 1; j < 3; j++) {
106836                 var newValue = 0;
106837                 if (this$1._depth[i][j] > minDepth) { newValue = 1; }
106838                 this$1._depth[i][j] = newValue;
106839               }
106840             }
106841           }
106842         };
106843         Depth.prototype.getDelta = function getDelta (geomIndex) {
106844           return this._depth[geomIndex][Position.RIGHT] - this._depth[geomIndex][Position.LEFT]
106845         };
106846         Depth.prototype.getLocation = function getLocation (geomIndex, posIndex) {
106847           if (this._depth[geomIndex][posIndex] <= 0) { return Location.EXTERIOR }
106848           return Location.INTERIOR
106849         };
106850         Depth.prototype.toString = function toString () {
106851           return 'A: ' + this._depth[0][1] + ',' + this._depth[0][2] + ' B: ' + this._depth[1][1] + ',' + this._depth[1][2]
106852         };
106853         Depth.prototype.add = function add () {
106854             var this$1 = this;
106855
106856           if (arguments.length === 1) {
106857             var lbl = arguments[0];
106858             for (var i = 0; i < 2; i++) {
106859               for (var j = 1; j < 3; j++) {
106860                 var loc = lbl.getLocation(i, j);
106861                 if (loc === Location.EXTERIOR || loc === Location.INTERIOR) {
106862                   if (this$1.isNull(i, j)) {
106863                     this$1._depth[i][j] = Depth.depthAtLocation(loc);
106864                   } else { this$1._depth[i][j] += Depth.depthAtLocation(loc); }
106865                 }
106866               }
106867             }
106868           } else if (arguments.length === 3) {
106869             var geomIndex = arguments[0];
106870             var posIndex = arguments[1];
106871             var location = arguments[2];
106872             if (location === Location.INTERIOR) { this._depth[geomIndex][posIndex]++; }
106873           }
106874         };
106875         Depth.prototype.interfaces_ = function interfaces_ () {
106876           return []
106877         };
106878         Depth.prototype.getClass = function getClass () {
106879           return Depth
106880         };
106881         Depth.depthAtLocation = function depthAtLocation (location) {
106882           if (location === Location.EXTERIOR) { return 0 }
106883           if (location === Location.INTERIOR) { return 1 }
106884           return Depth.NULL_VALUE
106885         };
106886         staticAccessors$31.NULL_VALUE.get = function () { return -1 };
106887
106888         Object.defineProperties( Depth, staticAccessors$31 );
106889
106890         var Edge = (function (GraphComponent$$1) {
106891           function Edge () {
106892             GraphComponent$$1.call(this);
106893             this.pts = null;
106894             this._env = null;
106895             this.eiList = new EdgeIntersectionList(this);
106896             this._name = null;
106897             this._mce = null;
106898             this._isIsolated = true;
106899             this._depth = new Depth();
106900             this._depthDelta = 0;
106901             if (arguments.length === 1) {
106902               var pts = arguments[0];
106903               Edge.call(this, pts, null);
106904             } else if (arguments.length === 2) {
106905               var pts$1 = arguments[0];
106906               var label = arguments[1];
106907               this.pts = pts$1;
106908               this._label = label;
106909             }
106910           }
106911
106912           if ( GraphComponent$$1 ) Edge.__proto__ = GraphComponent$$1;
106913           Edge.prototype = Object.create( GraphComponent$$1 && GraphComponent$$1.prototype );
106914           Edge.prototype.constructor = Edge;
106915           Edge.prototype.getDepth = function getDepth () {
106916             return this._depth
106917           };
106918           Edge.prototype.getCollapsedEdge = function getCollapsedEdge () {
106919             var newPts = new Array(2).fill(null);
106920             newPts[0] = this.pts[0];
106921             newPts[1] = this.pts[1];
106922             var newe = new Edge(newPts, Label.toLineLabel(this._label));
106923             return newe
106924           };
106925           Edge.prototype.isIsolated = function isIsolated () {
106926             return this._isIsolated
106927           };
106928           Edge.prototype.getCoordinates = function getCoordinates () {
106929             return this.pts
106930           };
106931           Edge.prototype.setIsolated = function setIsolated (isIsolated) {
106932             this._isIsolated = isIsolated;
106933           };
106934           Edge.prototype.setName = function setName (name) {
106935             this._name = name;
106936           };
106937           Edge.prototype.equals = function equals (o) {
106938             var this$1 = this;
106939
106940             if (!(o instanceof Edge)) { return false }
106941             var e = o;
106942             if (this.pts.length !== e.pts.length) { return false }
106943             var isEqualForward = true;
106944             var isEqualReverse = true;
106945             var iRev = this.pts.length;
106946             for (var i = 0; i < this.pts.length; i++) {
106947               if (!this$1.pts[i].equals2D(e.pts[i])) {
106948                 isEqualForward = false;
106949               }
106950               if (!this$1.pts[i].equals2D(e.pts[--iRev])) {
106951                 isEqualReverse = false;
106952               }
106953               if (!isEqualForward && !isEqualReverse) { return false }
106954             }
106955             return true
106956           };
106957           Edge.prototype.getCoordinate = function getCoordinate () {
106958             if (arguments.length === 0) {
106959               if (this.pts.length > 0) { return this.pts[0] }
106960               return null
106961             } else if (arguments.length === 1) {
106962               var i = arguments[0];
106963               return this.pts[i]
106964             }
106965           };
106966           Edge.prototype.print = function print (out) {
106967             var this$1 = this;
106968
106969             out.print('edge ' + this._name + ': ');
106970             out.print('LINESTRING (');
106971             for (var i = 0; i < this.pts.length; i++) {
106972               if (i > 0) { out.print(','); }
106973               out.print(this$1.pts[i].x + ' ' + this$1.pts[i].y);
106974             }
106975             out.print(')  ' + this._label + ' ' + this._depthDelta);
106976           };
106977           Edge.prototype.computeIM = function computeIM (im) {
106978             Edge.updateIM(this._label, im);
106979           };
106980           Edge.prototype.isCollapsed = function isCollapsed () {
106981             if (!this._label.isArea()) { return false }
106982             if (this.pts.length !== 3) { return false }
106983             if (this.pts[0].equals(this.pts[2])) { return true }
106984             return false
106985           };
106986           Edge.prototype.isClosed = function isClosed () {
106987             return this.pts[0].equals(this.pts[this.pts.length - 1])
106988           };
106989           Edge.prototype.getMaximumSegmentIndex = function getMaximumSegmentIndex () {
106990             return this.pts.length - 1
106991           };
106992           Edge.prototype.getDepthDelta = function getDepthDelta () {
106993             return this._depthDelta
106994           };
106995           Edge.prototype.getNumPoints = function getNumPoints () {
106996             return this.pts.length
106997           };
106998           Edge.prototype.printReverse = function printReverse (out) {
106999             var this$1 = this;
107000
107001             out.print('edge ' + this._name + ': ');
107002             for (var i = this.pts.length - 1; i >= 0; i--) {
107003               out.print(this$1.pts[i] + ' ');
107004             }
107005             out.println('');
107006           };
107007           Edge.prototype.getMonotoneChainEdge = function getMonotoneChainEdge () {
107008             if (this._mce === null) { this._mce = new MonotoneChainEdge(this); }
107009             return this._mce
107010           };
107011           Edge.prototype.getEnvelope = function getEnvelope () {
107012             var this$1 = this;
107013
107014             if (this._env === null) {
107015               this._env = new Envelope();
107016               for (var i = 0; i < this.pts.length; i++) {
107017                 this$1._env.expandToInclude(this$1.pts[i]);
107018               }
107019             }
107020             return this._env
107021           };
107022           Edge.prototype.addIntersection = function addIntersection (li, segmentIndex, geomIndex, intIndex) {
107023             var intPt = new Coordinate(li.getIntersection(intIndex));
107024             var normalizedSegmentIndex = segmentIndex;
107025             var dist = li.getEdgeDistance(geomIndex, intIndex);
107026             var nextSegIndex = normalizedSegmentIndex + 1;
107027             if (nextSegIndex < this.pts.length) {
107028               var nextPt = this.pts[nextSegIndex];
107029               if (intPt.equals2D(nextPt)) {
107030                 normalizedSegmentIndex = nextSegIndex;
107031                 dist = 0.0;
107032               }
107033             }
107034             this.eiList.add(intPt, normalizedSegmentIndex, dist);
107035           };
107036           Edge.prototype.toString = function toString () {
107037             var this$1 = this;
107038
107039             var buf = new StringBuffer();
107040             buf.append('edge ' + this._name + ': ');
107041             buf.append('LINESTRING (');
107042             for (var i = 0; i < this.pts.length; i++) {
107043               if (i > 0) { buf.append(','); }
107044               buf.append(this$1.pts[i].x + ' ' + this$1.pts[i].y);
107045             }
107046             buf.append(')  ' + this._label + ' ' + this._depthDelta);
107047             return buf.toString()
107048           };
107049           Edge.prototype.isPointwiseEqual = function isPointwiseEqual (e) {
107050             var this$1 = this;
107051
107052             if (this.pts.length !== e.pts.length) { return false }
107053             for (var i = 0; i < this.pts.length; i++) {
107054               if (!this$1.pts[i].equals2D(e.pts[i])) {
107055                 return false
107056               }
107057             }
107058             return true
107059           };
107060           Edge.prototype.setDepthDelta = function setDepthDelta (depthDelta) {
107061             this._depthDelta = depthDelta;
107062           };
107063           Edge.prototype.getEdgeIntersectionList = function getEdgeIntersectionList () {
107064             return this.eiList
107065           };
107066           Edge.prototype.addIntersections = function addIntersections (li, segmentIndex, geomIndex) {
107067             var this$1 = this;
107068
107069             for (var i = 0; i < li.getIntersectionNum(); i++) {
107070               this$1.addIntersection(li, segmentIndex, geomIndex, i);
107071             }
107072           };
107073           Edge.prototype.interfaces_ = function interfaces_ () {
107074             return []
107075           };
107076           Edge.prototype.getClass = function getClass () {
107077             return Edge
107078           };
107079           Edge.updateIM = function updateIM () {
107080             if (arguments.length === 2) {
107081               var label = arguments[0];
107082               var im = arguments[1];
107083               im.setAtLeastIfValid(label.getLocation(0, Position.ON), label.getLocation(1, Position.ON), 1);
107084               if (label.isArea()) {
107085                 im.setAtLeastIfValid(label.getLocation(0, Position.LEFT), label.getLocation(1, Position.LEFT), 2);
107086                 im.setAtLeastIfValid(label.getLocation(0, Position.RIGHT), label.getLocation(1, Position.RIGHT), 2);
107087               }
107088             } else { return GraphComponent$$1.prototype.updateIM.apply(this, arguments) }
107089           };
107090
107091           return Edge;
107092         }(GraphComponent));
107093
107094         var BufferBuilder = function BufferBuilder (bufParams) {
107095           this._workingPrecisionModel = null;
107096           this._workingNoder = null;
107097           this._geomFact = null;
107098           this._graph = null;
107099           this._edgeList = new EdgeList();
107100           this._bufParams = bufParams || null;
107101         };
107102         BufferBuilder.prototype.setWorkingPrecisionModel = function setWorkingPrecisionModel (pm) {
107103           this._workingPrecisionModel = pm;
107104         };
107105         BufferBuilder.prototype.insertUniqueEdge = function insertUniqueEdge (e) {
107106           var existingEdge = this._edgeList.findEqualEdge(e);
107107           if (existingEdge !== null) {
107108             var existingLabel = existingEdge.getLabel();
107109             var labelToMerge = e.getLabel();
107110             if (!existingEdge.isPointwiseEqual(e)) {
107111               labelToMerge = new Label(e.getLabel());
107112               labelToMerge.flip();
107113             }
107114             existingLabel.merge(labelToMerge);
107115             var mergeDelta = BufferBuilder.depthDelta(labelToMerge);
107116             var existingDelta = existingEdge.getDepthDelta();
107117             var newDelta = existingDelta + mergeDelta;
107118             existingEdge.setDepthDelta(newDelta);
107119           } else {
107120             this._edgeList.add(e);
107121             e.setDepthDelta(BufferBuilder.depthDelta(e.getLabel()));
107122           }
107123         };
107124         BufferBuilder.prototype.buildSubgraphs = function buildSubgraphs (subgraphList, polyBuilder) {
107125           var processedGraphs = new ArrayList();
107126           for (var i = subgraphList.iterator(); i.hasNext();) {
107127             var subgraph = i.next();
107128             var p = subgraph.getRightmostCoordinate();
107129             var locater = new SubgraphDepthLocater(processedGraphs);
107130             var outsideDepth = locater.getDepth(p);
107131             subgraph.computeDepth(outsideDepth);
107132             subgraph.findResultEdges();
107133             processedGraphs.add(subgraph);
107134             polyBuilder.add(subgraph.getDirectedEdges(), subgraph.getNodes());
107135           }
107136         };
107137         BufferBuilder.prototype.createSubgraphs = function createSubgraphs (graph) {
107138           var subgraphList = new ArrayList();
107139           for (var i = graph.getNodes().iterator(); i.hasNext();) {
107140             var node = i.next();
107141             if (!node.isVisited()) {
107142               var subgraph = new BufferSubgraph();
107143               subgraph.create(node);
107144               subgraphList.add(subgraph);
107145             }
107146           }
107147           Collections.sort(subgraphList, Collections.reverseOrder());
107148           return subgraphList
107149         };
107150         BufferBuilder.prototype.createEmptyResultGeometry = function createEmptyResultGeometry () {
107151           var emptyGeom = this._geomFact.createPolygon();
107152           return emptyGeom
107153         };
107154         BufferBuilder.prototype.getNoder = function getNoder (precisionModel) {
107155           if (this._workingNoder !== null) { return this._workingNoder }
107156           var noder = new MCIndexNoder();
107157           var li = new RobustLineIntersector();
107158           li.setPrecisionModel(precisionModel);
107159           noder.setSegmentIntersector(new IntersectionAdder(li));
107160           return noder
107161         };
107162         BufferBuilder.prototype.buffer = function buffer (g, distance) {
107163           var precisionModel = this._workingPrecisionModel;
107164           if (precisionModel === null) { precisionModel = g.getPrecisionModel(); }
107165           this._geomFact = g.getFactory();
107166           var curveBuilder = new OffsetCurveBuilder(precisionModel, this._bufParams);
107167           var curveSetBuilder = new OffsetCurveSetBuilder(g, distance, curveBuilder);
107168           var bufferSegStrList = curveSetBuilder.getCurves();
107169           if (bufferSegStrList.size() <= 0) {
107170             return this.createEmptyResultGeometry()
107171           }
107172           this.computeNodedEdges(bufferSegStrList, precisionModel);
107173           this._graph = new PlanarGraph(new OverlayNodeFactory());
107174           this._graph.addEdges(this._edgeList.getEdges());
107175           var subgraphList = this.createSubgraphs(this._graph);
107176           var polyBuilder = new PolygonBuilder(this._geomFact);
107177           this.buildSubgraphs(subgraphList, polyBuilder);
107178           var resultPolyList = polyBuilder.getPolygons();
107179           if (resultPolyList.size() <= 0) {
107180             return this.createEmptyResultGeometry()
107181           }
107182           var resultGeom = this._geomFact.buildGeometry(resultPolyList);
107183           return resultGeom
107184         };
107185         BufferBuilder.prototype.computeNodedEdges = function computeNodedEdges (bufferSegStrList, precisionModel) {
107186             var this$1 = this;
107187
107188           var noder = this.getNoder(precisionModel);
107189           noder.computeNodes(bufferSegStrList);
107190           var nodedSegStrings = noder.getNodedSubstrings();
107191           for (var i = nodedSegStrings.iterator(); i.hasNext();) {
107192             var segStr = i.next();
107193             var pts = segStr.getCoordinates();
107194             if (pts.length === 2 && pts[0].equals2D(pts[1])) { continue }
107195             var oldLabel = segStr.getData();
107196             var edge = new Edge(segStr.getCoordinates(), new Label(oldLabel));
107197             this$1.insertUniqueEdge(edge);
107198           }
107199         };
107200         BufferBuilder.prototype.setNoder = function setNoder (noder) {
107201           this._workingNoder = noder;
107202         };
107203         BufferBuilder.prototype.interfaces_ = function interfaces_ () {
107204           return []
107205         };
107206         BufferBuilder.prototype.getClass = function getClass () {
107207           return BufferBuilder
107208         };
107209         BufferBuilder.depthDelta = function depthDelta (label) {
107210           var lLoc = label.getLocation(0, Position.LEFT);
107211           var rLoc = label.getLocation(0, Position.RIGHT);
107212           if (lLoc === Location.INTERIOR && rLoc === Location.EXTERIOR) { return 1; } else if (lLoc === Location.EXTERIOR && rLoc === Location.INTERIOR) { return -1 }
107213           return 0
107214         };
107215         BufferBuilder.convertSegStrings = function convertSegStrings (it) {
107216           var fact = new GeometryFactory();
107217           var lines = new ArrayList();
107218           while (it.hasNext()) {
107219             var ss = it.next();
107220             var line = fact.createLineString(ss.getCoordinates());
107221             lines.add(line);
107222           }
107223           return fact.buildGeometry(lines)
107224         };
107225
107226         var ScaledNoder = function ScaledNoder () {
107227           this._noder = null;
107228           this._scaleFactor = null;
107229           this._offsetX = null;
107230           this._offsetY = null;
107231           this._isScaled = false;
107232           if (arguments.length === 2) {
107233             var noder = arguments[0];
107234             var scaleFactor = arguments[1];
107235             this._noder = noder;
107236             this._scaleFactor = scaleFactor;
107237             this._offsetX = 0.0;
107238             this._offsetY = 0.0;
107239             this._isScaled = !this.isIntegerPrecision();
107240           } else if (arguments.length === 4) {
107241             var noder$1 = arguments[0];
107242             var scaleFactor$1 = arguments[1];
107243             var offsetX = arguments[2];
107244             var offsetY = arguments[3];
107245             this._noder = noder$1;
107246             this._scaleFactor = scaleFactor$1;
107247             this._offsetX = offsetX;
107248             this._offsetY = offsetY;
107249             this._isScaled = !this.isIntegerPrecision();
107250           }
107251         };
107252         ScaledNoder.prototype.rescale = function rescale () {
107253             var this$1 = this;
107254
107255           if (hasInterface(arguments[0], Collection)) {
107256             var segStrings = arguments[0];
107257             for (var i = segStrings.iterator(); i.hasNext();) {
107258               var ss = i.next();
107259               this$1.rescale(ss.getCoordinates());
107260             }
107261           } else if (arguments[0] instanceof Array) {
107262             var pts = arguments[0];
107263             // let p0 = null
107264             // let p1 = null
107265             // if (pts.length === 2) {
107266             // p0 = new Coordinate(pts[0])
107267             // p1 = new Coordinate(pts[1])
107268             // }
107269             for (var i$1 = 0; i$1 < pts.length; i$1++) {
107270               pts[i$1].x = pts[i$1].x / this$1._scaleFactor + this$1._offsetX;
107271               pts[i$1].y = pts[i$1].y / this$1._scaleFactor + this$1._offsetY;
107272             }
107273             if (pts.length === 2 && pts[0].equals2D(pts[1])) {
107274               System.out.println(pts);
107275             }
107276           }
107277         };
107278         ScaledNoder.prototype.scale = function scale () {
107279             var this$1 = this;
107280
107281           if (hasInterface(arguments[0], Collection)) {
107282             var segStrings = arguments[0];
107283             var nodedSegmentStrings = new ArrayList();
107284             for (var i = segStrings.iterator(); i.hasNext();) {
107285               var ss = i.next();
107286               nodedSegmentStrings.add(new NodedSegmentString(this$1.scale(ss.getCoordinates()), ss.getData()));
107287             }
107288             return nodedSegmentStrings
107289           } else if (arguments[0] instanceof Array) {
107290             var pts = arguments[0];
107291             var roundPts = new Array(pts.length).fill(null);
107292             for (var i$1 = 0; i$1 < pts.length; i$1++) {
107293               roundPts[i$1] = new Coordinate(Math.round((pts[i$1].x - this$1._offsetX) * this$1._scaleFactor), Math.round((pts[i$1].y - this$1._offsetY) * this$1._scaleFactor), pts[i$1].z);
107294             }
107295             var roundPtsNoDup = CoordinateArrays.removeRepeatedPoints(roundPts);
107296             return roundPtsNoDup
107297           }
107298         };
107299         ScaledNoder.prototype.isIntegerPrecision = function isIntegerPrecision () {
107300           return this._scaleFactor === 1.0
107301         };
107302         ScaledNoder.prototype.getNodedSubstrings = function getNodedSubstrings () {
107303           var splitSS = this._noder.getNodedSubstrings();
107304           if (this._isScaled) { this.rescale(splitSS); }
107305           return splitSS
107306         };
107307         ScaledNoder.prototype.computeNodes = function computeNodes (inputSegStrings) {
107308           var intSegStrings = inputSegStrings;
107309           if (this._isScaled) { intSegStrings = this.scale(inputSegStrings); }
107310           this._noder.computeNodes(intSegStrings);
107311         };
107312         ScaledNoder.prototype.interfaces_ = function interfaces_ () {
107313           return [Noder]
107314         };
107315         ScaledNoder.prototype.getClass = function getClass () {
107316           return ScaledNoder
107317         };
107318
107319         var NodingValidator = function NodingValidator () {
107320           this._li = new RobustLineIntersector();
107321           this._segStrings = null;
107322           var segStrings = arguments[0];
107323           this._segStrings = segStrings;
107324         };
107325
107326         var staticAccessors$33 = { fact: { configurable: true } };
107327         NodingValidator.prototype.checkEndPtVertexIntersections = function checkEndPtVertexIntersections () {
107328             var this$1 = this;
107329
107330           if (arguments.length === 0) {
107331             for (var i = this._segStrings.iterator(); i.hasNext();) {
107332               var ss = i.next();
107333               var pts = ss.getCoordinates();
107334               this$1.checkEndPtVertexIntersections(pts[0], this$1._segStrings);
107335               this$1.checkEndPtVertexIntersections(pts[pts.length - 1], this$1._segStrings);
107336             }
107337           } else if (arguments.length === 2) {
107338             var testPt = arguments[0];
107339             var segStrings = arguments[1];
107340             for (var i$1 = segStrings.iterator(); i$1.hasNext();) {
107341               var ss$1 = i$1.next();
107342               var pts$1 = ss$1.getCoordinates();
107343               for (var j = 1; j < pts$1.length - 1; j++) {
107344                 if (pts$1[j].equals(testPt)) { throw new RuntimeException('found endpt/interior pt intersection at index ' + j + ' :pt ' + testPt) }
107345               }
107346             }
107347           }
107348         };
107349         NodingValidator.prototype.checkInteriorIntersections = function checkInteriorIntersections () {
107350             var this$1 = this;
107351
107352           if (arguments.length === 0) {
107353             for (var i = this._segStrings.iterator(); i.hasNext();) {
107354               var ss0 = i.next();
107355               for (var j = this._segStrings.iterator(); j.hasNext();) {
107356                 var ss1 = j.next();
107357                 this$1.checkInteriorIntersections(ss0, ss1);
107358               }
107359             }
107360           } else if (arguments.length === 2) {
107361             var ss0$1 = arguments[0];
107362             var ss1$1 = arguments[1];
107363             var pts0 = ss0$1.getCoordinates();
107364             var pts1 = ss1$1.getCoordinates();
107365             for (var i0 = 0; i0 < pts0.length - 1; i0++) {
107366               for (var i1 = 0; i1 < pts1.length - 1; i1++) {
107367                 this$1.checkInteriorIntersections(ss0$1, i0, ss1$1, i1);
107368               }
107369             }
107370           } else if (arguments.length === 4) {
107371             var e0 = arguments[0];
107372             var segIndex0 = arguments[1];
107373             var e1 = arguments[2];
107374             var segIndex1 = arguments[3];
107375             if (e0 === e1 && segIndex0 === segIndex1) { return null }
107376             var p00 = e0.getCoordinates()[segIndex0];
107377             var p01 = e0.getCoordinates()[segIndex0 + 1];
107378             var p10 = e1.getCoordinates()[segIndex1];
107379             var p11 = e1.getCoordinates()[segIndex1 + 1];
107380             this._li.computeIntersection(p00, p01, p10, p11);
107381             if (this._li.hasIntersection()) {
107382               if (this._li.isProper() || this.hasInteriorIntersection(this._li, p00, p01) || this.hasInteriorIntersection(this._li, p10, p11)) {
107383                 throw new RuntimeException('found non-noded intersection at ' + p00 + '-' + p01 + ' and ' + p10 + '-' + p11)
107384               }
107385             }
107386           }
107387         };
107388         NodingValidator.prototype.checkValid = function checkValid () {
107389           this.checkEndPtVertexIntersections();
107390           this.checkInteriorIntersections();
107391           this.checkCollapses();
107392         };
107393         NodingValidator.prototype.checkCollapses = function checkCollapses () {
107394             var this$1 = this;
107395
107396           if (arguments.length === 0) {
107397             for (var i = this._segStrings.iterator(); i.hasNext();) {
107398               var ss = i.next();
107399               this$1.checkCollapses(ss);
107400             }
107401           } else if (arguments.length === 1) {
107402             var ss$1 = arguments[0];
107403             var pts = ss$1.getCoordinates();
107404             for (var i$1 = 0; i$1 < pts.length - 2; i$1++) {
107405               this$1.checkCollapse(pts[i$1], pts[i$1 + 1], pts[i$1 + 2]);
107406             }
107407           }
107408         };
107409         NodingValidator.prototype.hasInteriorIntersection = function hasInteriorIntersection (li, p0, p1) {
107410           for (var i = 0; i < li.getIntersectionNum(); i++) {
107411             var intPt = li.getIntersection(i);
107412             if (!(intPt.equals(p0) || intPt.equals(p1))) { return true }
107413           }
107414           return false
107415         };
107416         NodingValidator.prototype.checkCollapse = function checkCollapse (p0, p1, p2) {
107417           if (p0.equals(p2)) { throw new RuntimeException('found non-noded collapse at ' + NodingValidator.fact.createLineString([p0, p1, p2])) }
107418         };
107419         NodingValidator.prototype.interfaces_ = function interfaces_ () {
107420           return []
107421         };
107422         NodingValidator.prototype.getClass = function getClass () {
107423           return NodingValidator
107424         };
107425         staticAccessors$33.fact.get = function () { return new GeometryFactory() };
107426
107427         Object.defineProperties( NodingValidator, staticAccessors$33 );
107428
107429         var HotPixel = function HotPixel () {
107430           this._li = null;
107431           this._pt = null;
107432           this._originalPt = null;
107433           this._ptScaled = null;
107434           this._p0Scaled = null;
107435           this._p1Scaled = null;
107436           this._scaleFactor = null;
107437           this._minx = null;
107438           this._maxx = null;
107439           this._miny = null;
107440           this._maxy = null;
107441           this._corner = new Array(4).fill(null);
107442           this._safeEnv = null;
107443           var pt = arguments[0];
107444           var scaleFactor = arguments[1];
107445           var li = arguments[2];
107446           this._originalPt = pt;
107447           this._pt = pt;
107448           this._scaleFactor = scaleFactor;
107449           this._li = li;
107450           if (scaleFactor <= 0) { throw new IllegalArgumentException('Scale factor must be non-zero') }
107451           if (scaleFactor !== 1.0) {
107452             this._pt = new Coordinate(this.scale(pt.x), this.scale(pt.y));
107453             this._p0Scaled = new Coordinate();
107454             this._p1Scaled = new Coordinate();
107455           }
107456           this.initCorners(this._pt);
107457         };
107458
107459         var staticAccessors$34 = { SAFE_ENV_EXPANSION_FACTOR: { configurable: true } };
107460         HotPixel.prototype.intersectsScaled = function intersectsScaled (p0, p1) {
107461           var segMinx = Math.min(p0.x, p1.x);
107462           var segMaxx = Math.max(p0.x, p1.x);
107463           var segMiny = Math.min(p0.y, p1.y);
107464           var segMaxy = Math.max(p0.y, p1.y);
107465           var isOutsidePixelEnv = this._maxx < segMinx || this._minx > segMaxx || this._maxy < segMiny || this._miny > segMaxy;
107466           if (isOutsidePixelEnv) { return false }
107467           var intersects = this.intersectsToleranceSquare(p0, p1);
107468           Assert.isTrue(!(isOutsidePixelEnv && intersects), 'Found bad envelope test');
107469           return intersects
107470         };
107471         HotPixel.prototype.initCorners = function initCorners (pt) {
107472           var tolerance = 0.5;
107473           this._minx = pt.x - tolerance;
107474           this._maxx = pt.x + tolerance;
107475           this._miny = pt.y - tolerance;
107476           this._maxy = pt.y + tolerance;
107477           this._corner[0] = new Coordinate(this._maxx, this._maxy);
107478           this._corner[1] = new Coordinate(this._minx, this._maxy);
107479           this._corner[2] = new Coordinate(this._minx, this._miny);
107480           this._corner[3] = new Coordinate(this._maxx, this._miny);
107481         };
107482         HotPixel.prototype.intersects = function intersects (p0, p1) {
107483           if (this._scaleFactor === 1.0) { return this.intersectsScaled(p0, p1) }
107484           this.copyScaled(p0, this._p0Scaled);
107485           this.copyScaled(p1, this._p1Scaled);
107486           return this.intersectsScaled(this._p0Scaled, this._p1Scaled)
107487         };
107488         HotPixel.prototype.scale = function scale (val) {
107489           return Math.round(val * this._scaleFactor)
107490         };
107491         HotPixel.prototype.getCoordinate = function getCoordinate () {
107492           return this._originalPt
107493         };
107494         HotPixel.prototype.copyScaled = function copyScaled (p, pScaled) {
107495           pScaled.x = this.scale(p.x);
107496           pScaled.y = this.scale(p.y);
107497         };
107498         HotPixel.prototype.getSafeEnvelope = function getSafeEnvelope () {
107499           if (this._safeEnv === null) {
107500             var safeTolerance = HotPixel.SAFE_ENV_EXPANSION_FACTOR / this._scaleFactor;
107501             this._safeEnv = new Envelope(this._originalPt.x - safeTolerance, this._originalPt.x + safeTolerance, this._originalPt.y - safeTolerance, this._originalPt.y + safeTolerance);
107502           }
107503           return this._safeEnv
107504         };
107505         HotPixel.prototype.intersectsPixelClosure = function intersectsPixelClosure (p0, p1) {
107506           this._li.computeIntersection(p0, p1, this._corner[0], this._corner[1]);
107507           if (this._li.hasIntersection()) { return true }
107508           this._li.computeIntersection(p0, p1, this._corner[1], this._corner[2]);
107509           if (this._li.hasIntersection()) { return true }
107510           this._li.computeIntersection(p0, p1, this._corner[2], this._corner[3]);
107511           if (this._li.hasIntersection()) { return true }
107512           this._li.computeIntersection(p0, p1, this._corner[3], this._corner[0]);
107513           if (this._li.hasIntersection()) { return true }
107514           return false
107515         };
107516         HotPixel.prototype.intersectsToleranceSquare = function intersectsToleranceSquare (p0, p1) {
107517           var intersectsLeft = false;
107518           var intersectsBottom = false;
107519           this._li.computeIntersection(p0, p1, this._corner[0], this._corner[1]);
107520           if (this._li.isProper()) { return true }
107521           this._li.computeIntersection(p0, p1, this._corner[1], this._corner[2]);
107522           if (this._li.isProper()) { return true }
107523           if (this._li.hasIntersection()) { intersectsLeft = true; }
107524           this._li.computeIntersection(p0, p1, this._corner[2], this._corner[3]);
107525           if (this._li.isProper()) { return true }
107526           if (this._li.hasIntersection()) { intersectsBottom = true; }
107527           this._li.computeIntersection(p0, p1, this._corner[3], this._corner[0]);
107528           if (this._li.isProper()) { return true }
107529           if (intersectsLeft && intersectsBottom) { return true }
107530           if (p0.equals(this._pt)) { return true }
107531           if (p1.equals(this._pt)) { return true }
107532           return false
107533         };
107534         HotPixel.prototype.addSnappedNode = function addSnappedNode (segStr, segIndex) {
107535           var p0 = segStr.getCoordinate(segIndex);
107536           var p1 = segStr.getCoordinate(segIndex + 1);
107537           if (this.intersects(p0, p1)) {
107538             segStr.addIntersection(this.getCoordinate(), segIndex);
107539             return true
107540           }
107541           return false
107542         };
107543         HotPixel.prototype.interfaces_ = function interfaces_ () {
107544           return []
107545         };
107546         HotPixel.prototype.getClass = function getClass () {
107547           return HotPixel
107548         };
107549         staticAccessors$34.SAFE_ENV_EXPANSION_FACTOR.get = function () { return 0.75 };
107550
107551         Object.defineProperties( HotPixel, staticAccessors$34 );
107552
107553         var MonotoneChainSelectAction = function MonotoneChainSelectAction () {
107554           this.tempEnv1 = new Envelope();
107555           this.selectedSegment = new LineSegment();
107556         };
107557         MonotoneChainSelectAction.prototype.select = function select () {
107558           if (arguments.length === 1) ; else if (arguments.length === 2) {
107559             var mc = arguments[0];
107560             var startIndex = arguments[1];
107561             mc.getLineSegment(startIndex, this.selectedSegment);
107562             this.select(this.selectedSegment);
107563           }
107564         };
107565         MonotoneChainSelectAction.prototype.interfaces_ = function interfaces_ () {
107566           return []
107567         };
107568         MonotoneChainSelectAction.prototype.getClass = function getClass () {
107569           return MonotoneChainSelectAction
107570         };
107571
107572         var MCIndexPointSnapper = function MCIndexPointSnapper () {
107573           this._index = null;
107574           var index = arguments[0];
107575           this._index = index;
107576         };
107577
107578         var staticAccessors$35 = { HotPixelSnapAction: { configurable: true } };
107579         MCIndexPointSnapper.prototype.snap = function snap () {
107580           if (arguments.length === 1) {
107581             var hotPixel = arguments[0];
107582             return this.snap(hotPixel, null, -1)
107583           } else if (arguments.length === 3) {
107584             var hotPixel$1 = arguments[0];
107585             var parentEdge = arguments[1];
107586             var hotPixelVertexIndex = arguments[2];
107587             var pixelEnv = hotPixel$1.getSafeEnvelope();
107588             var hotPixelSnapAction = new HotPixelSnapAction(hotPixel$1, parentEdge, hotPixelVertexIndex);
107589             this._index.query(pixelEnv, {
107590               interfaces_: function () {
107591                 return [ItemVisitor]
107592               },
107593               visitItem: function (item) {
107594                 var testChain = item;
107595                 testChain.select(pixelEnv, hotPixelSnapAction);
107596               }
107597             });
107598             return hotPixelSnapAction.isNodeAdded()
107599           }
107600         };
107601         MCIndexPointSnapper.prototype.interfaces_ = function interfaces_ () {
107602           return []
107603         };
107604         MCIndexPointSnapper.prototype.getClass = function getClass () {
107605           return MCIndexPointSnapper
107606         };
107607         staticAccessors$35.HotPixelSnapAction.get = function () { return HotPixelSnapAction };
107608
107609         Object.defineProperties( MCIndexPointSnapper, staticAccessors$35 );
107610
107611         var HotPixelSnapAction = (function (MonotoneChainSelectAction$$1) {
107612           function HotPixelSnapAction () {
107613             MonotoneChainSelectAction$$1.call(this);
107614             this._hotPixel = null;
107615             this._parentEdge = null;
107616             this._hotPixelVertexIndex = null;
107617             this._isNodeAdded = false;
107618             var hotPixel = arguments[0];
107619             var parentEdge = arguments[1];
107620             var hotPixelVertexIndex = arguments[2];
107621             this._hotPixel = hotPixel;
107622             this._parentEdge = parentEdge;
107623             this._hotPixelVertexIndex = hotPixelVertexIndex;
107624           }
107625
107626           if ( MonotoneChainSelectAction$$1 ) HotPixelSnapAction.__proto__ = MonotoneChainSelectAction$$1;
107627           HotPixelSnapAction.prototype = Object.create( MonotoneChainSelectAction$$1 && MonotoneChainSelectAction$$1.prototype );
107628           HotPixelSnapAction.prototype.constructor = HotPixelSnapAction;
107629           HotPixelSnapAction.prototype.isNodeAdded = function isNodeAdded () {
107630             return this._isNodeAdded
107631           };
107632           HotPixelSnapAction.prototype.select = function select () {
107633             if (arguments.length === 2) {
107634               var mc = arguments[0];
107635               var startIndex = arguments[1];
107636               var ss = mc.getContext();
107637               if (this._parentEdge !== null) {
107638                 if (ss === this._parentEdge && startIndex === this._hotPixelVertexIndex) { return null }
107639               }
107640               this._isNodeAdded = this._hotPixel.addSnappedNode(ss, startIndex);
107641             } else { return MonotoneChainSelectAction$$1.prototype.select.apply(this, arguments) }
107642           };
107643           HotPixelSnapAction.prototype.interfaces_ = function interfaces_ () {
107644             return []
107645           };
107646           HotPixelSnapAction.prototype.getClass = function getClass () {
107647             return HotPixelSnapAction
107648           };
107649
107650           return HotPixelSnapAction;
107651         }(MonotoneChainSelectAction));
107652
107653         var InteriorIntersectionFinderAdder = function InteriorIntersectionFinderAdder () {
107654           this._li = null;
107655           this._interiorIntersections = null;
107656           var li = arguments[0];
107657           this._li = li;
107658           this._interiorIntersections = new ArrayList();
107659         };
107660         InteriorIntersectionFinderAdder.prototype.processIntersections = function processIntersections (e0, segIndex0, e1, segIndex1) {
107661             var this$1 = this;
107662
107663           if (e0 === e1 && segIndex0 === segIndex1) { return null }
107664           var p00 = e0.getCoordinates()[segIndex0];
107665           var p01 = e0.getCoordinates()[segIndex0 + 1];
107666           var p10 = e1.getCoordinates()[segIndex1];
107667           var p11 = e1.getCoordinates()[segIndex1 + 1];
107668           this._li.computeIntersection(p00, p01, p10, p11);
107669           if (this._li.hasIntersection()) {
107670             if (this._li.isInteriorIntersection()) {
107671               for (var intIndex = 0; intIndex < this._li.getIntersectionNum(); intIndex++) {
107672                 this$1._interiorIntersections.add(this$1._li.getIntersection(intIndex));
107673               }
107674               e0.addIntersections(this._li, segIndex0, 0);
107675               e1.addIntersections(this._li, segIndex1, 1);
107676             }
107677           }
107678         };
107679         InteriorIntersectionFinderAdder.prototype.isDone = function isDone () {
107680           return false
107681         };
107682         InteriorIntersectionFinderAdder.prototype.getInteriorIntersections = function getInteriorIntersections () {
107683           return this._interiorIntersections
107684         };
107685         InteriorIntersectionFinderAdder.prototype.interfaces_ = function interfaces_ () {
107686           return [SegmentIntersector]
107687         };
107688         InteriorIntersectionFinderAdder.prototype.getClass = function getClass () {
107689           return InteriorIntersectionFinderAdder
107690         };
107691
107692         var MCIndexSnapRounder = function MCIndexSnapRounder () {
107693           this._pm = null;
107694           this._li = null;
107695           this._scaleFactor = null;
107696           this._noder = null;
107697           this._pointSnapper = null;
107698           this._nodedSegStrings = null;
107699           var pm = arguments[0];
107700           this._pm = pm;
107701           this._li = new RobustLineIntersector();
107702           this._li.setPrecisionModel(pm);
107703           this._scaleFactor = pm.getScale();
107704         };
107705         MCIndexSnapRounder.prototype.checkCorrectness = function checkCorrectness (inputSegmentStrings) {
107706           var resultSegStrings = NodedSegmentString.getNodedSubstrings(inputSegmentStrings);
107707           var nv = new NodingValidator(resultSegStrings);
107708           try {
107709             nv.checkValid();
107710           } catch (ex) {
107711             if (ex instanceof Exception) {
107712               ex.printStackTrace();
107713             } else { throw ex }
107714           } finally {}
107715         };
107716         MCIndexSnapRounder.prototype.getNodedSubstrings = function getNodedSubstrings () {
107717           return NodedSegmentString.getNodedSubstrings(this._nodedSegStrings)
107718         };
107719         MCIndexSnapRounder.prototype.snapRound = function snapRound (segStrings, li) {
107720           var intersections = this.findInteriorIntersections(segStrings, li);
107721           this.computeIntersectionSnaps(intersections);
107722           this.computeVertexSnaps(segStrings);
107723         };
107724         MCIndexSnapRounder.prototype.findInteriorIntersections = function findInteriorIntersections (segStrings, li) {
107725           var intFinderAdder = new InteriorIntersectionFinderAdder(li);
107726           this._noder.setSegmentIntersector(intFinderAdder);
107727           this._noder.computeNodes(segStrings);
107728           return intFinderAdder.getInteriorIntersections()
107729         };
107730         MCIndexSnapRounder.prototype.computeVertexSnaps = function computeVertexSnaps () {
107731             var this$1 = this;
107732
107733           if (hasInterface(arguments[0], Collection)) {
107734             var edges = arguments[0];
107735             for (var i0 = edges.iterator(); i0.hasNext();) {
107736               var edge0 = i0.next();
107737               this$1.computeVertexSnaps(edge0);
107738             }
107739           } else if (arguments[0] instanceof NodedSegmentString) {
107740             var e = arguments[0];
107741             var pts0 = e.getCoordinates();
107742             for (var i = 0; i < pts0.length; i++) {
107743               var hotPixel = new HotPixel(pts0[i], this$1._scaleFactor, this$1._li);
107744               var isNodeAdded = this$1._pointSnapper.snap(hotPixel, e, i);
107745               if (isNodeAdded) {
107746                 e.addIntersection(pts0[i], i);
107747               }
107748             }
107749           }
107750         };
107751         MCIndexSnapRounder.prototype.computeNodes = function computeNodes (inputSegmentStrings) {
107752           this._nodedSegStrings = inputSegmentStrings;
107753           this._noder = new MCIndexNoder();
107754           this._pointSnapper = new MCIndexPointSnapper(this._noder.getIndex());
107755           this.snapRound(inputSegmentStrings, this._li);
107756         };
107757         MCIndexSnapRounder.prototype.computeIntersectionSnaps = function computeIntersectionSnaps (snapPts) {
107758             var this$1 = this;
107759
107760           for (var it = snapPts.iterator(); it.hasNext();) {
107761             var snapPt = it.next();
107762             var hotPixel = new HotPixel(snapPt, this$1._scaleFactor, this$1._li);
107763             this$1._pointSnapper.snap(hotPixel);
107764           }
107765         };
107766         MCIndexSnapRounder.prototype.interfaces_ = function interfaces_ () {
107767           return [Noder]
107768         };
107769         MCIndexSnapRounder.prototype.getClass = function getClass () {
107770           return MCIndexSnapRounder
107771         };
107772
107773         var BufferOp = function BufferOp () {
107774           this._argGeom = null;
107775           this._distance = null;
107776           this._bufParams = new BufferParameters();
107777           this._resultGeometry = null;
107778           this._saveException = null;
107779           if (arguments.length === 1) {
107780             var g = arguments[0];
107781             this._argGeom = g;
107782           } else if (arguments.length === 2) {
107783             var g$1 = arguments[0];
107784             var bufParams = arguments[1];
107785             this._argGeom = g$1;
107786             this._bufParams = bufParams;
107787           }
107788         };
107789
107790         var staticAccessors$32 = { CAP_ROUND: { configurable: true },CAP_BUTT: { configurable: true },CAP_FLAT: { configurable: true },CAP_SQUARE: { configurable: true },MAX_PRECISION_DIGITS: { configurable: true } };
107791         BufferOp.prototype.bufferFixedPrecision = function bufferFixedPrecision (fixedPM) {
107792           var noder = new ScaledNoder(new MCIndexSnapRounder(new PrecisionModel(1.0)), fixedPM.getScale());
107793           var bufBuilder = new BufferBuilder(this._bufParams);
107794           bufBuilder.setWorkingPrecisionModel(fixedPM);
107795           bufBuilder.setNoder(noder);
107796           this._resultGeometry = bufBuilder.buffer(this._argGeom, this._distance);
107797         };
107798         BufferOp.prototype.bufferReducedPrecision = function bufferReducedPrecision () {
107799             var this$1 = this;
107800
107801           if (arguments.length === 0) {
107802             for (var precDigits = BufferOp.MAX_PRECISION_DIGITS; precDigits >= 0; precDigits--) {
107803               try {
107804                 this$1.bufferReducedPrecision(precDigits);
107805               } catch (ex) {
107806                 if (ex instanceof TopologyException) {
107807                   this$1._saveException = ex;
107808                 } else { throw ex }
107809               } finally {}
107810               if (this$1._resultGeometry !== null) { return null }
107811             }
107812             throw this._saveException
107813           } else if (arguments.length === 1) {
107814             var precisionDigits = arguments[0];
107815             var sizeBasedScaleFactor = BufferOp.precisionScaleFactor(this._argGeom, this._distance, precisionDigits);
107816             var fixedPM = new PrecisionModel(sizeBasedScaleFactor);
107817             this.bufferFixedPrecision(fixedPM);
107818           }
107819         };
107820         BufferOp.prototype.computeGeometry = function computeGeometry () {
107821           this.bufferOriginalPrecision();
107822           if (this._resultGeometry !== null) { return null }
107823           var argPM = this._argGeom.getFactory().getPrecisionModel();
107824           if (argPM.getType() === PrecisionModel.FIXED) { this.bufferFixedPrecision(argPM); } else { this.bufferReducedPrecision(); }
107825         };
107826         BufferOp.prototype.setQuadrantSegments = function setQuadrantSegments (quadrantSegments) {
107827           this._bufParams.setQuadrantSegments(quadrantSegments);
107828         };
107829         BufferOp.prototype.bufferOriginalPrecision = function bufferOriginalPrecision () {
107830           try {
107831             var bufBuilder = new BufferBuilder(this._bufParams);
107832             this._resultGeometry = bufBuilder.buffer(this._argGeom, this._distance);
107833           } catch (ex) {
107834             if (ex instanceof RuntimeException) {
107835               this._saveException = ex;
107836             } else { throw ex }
107837           } finally {}
107838         };
107839         BufferOp.prototype.getResultGeometry = function getResultGeometry (distance) {
107840           this._distance = distance;
107841           this.computeGeometry();
107842           return this._resultGeometry
107843         };
107844         BufferOp.prototype.setEndCapStyle = function setEndCapStyle (endCapStyle) {
107845           this._bufParams.setEndCapStyle(endCapStyle);
107846         };
107847         BufferOp.prototype.interfaces_ = function interfaces_ () {
107848           return []
107849         };
107850         BufferOp.prototype.getClass = function getClass () {
107851           return BufferOp
107852         };
107853         BufferOp.bufferOp = function bufferOp () {
107854           if (arguments.length === 2) {
107855             var g = arguments[0];
107856             var distance = arguments[1];
107857             var gBuf = new BufferOp(g);
107858             var geomBuf = gBuf.getResultGeometry(distance);
107859             return geomBuf
107860           } else if (arguments.length === 3) {
107861             if (Number.isInteger(arguments[2]) && (arguments[0] instanceof Geometry && typeof arguments[1] === 'number')) {
107862               var g$1 = arguments[0];
107863               var distance$1 = arguments[1];
107864               var quadrantSegments = arguments[2];
107865               var bufOp = new BufferOp(g$1);
107866               bufOp.setQuadrantSegments(quadrantSegments);
107867               var geomBuf$1 = bufOp.getResultGeometry(distance$1);
107868               return geomBuf$1
107869             } else if (arguments[2] instanceof BufferParameters && (arguments[0] instanceof Geometry && typeof arguments[1] === 'number')) {
107870               var g$2 = arguments[0];
107871               var distance$2 = arguments[1];
107872               var params = arguments[2];
107873               var bufOp$1 = new BufferOp(g$2, params);
107874               var geomBuf$2 = bufOp$1.getResultGeometry(distance$2);
107875               return geomBuf$2
107876             }
107877           } else if (arguments.length === 4) {
107878             var g$3 = arguments[0];
107879             var distance$3 = arguments[1];
107880             var quadrantSegments$1 = arguments[2];
107881             var endCapStyle = arguments[3];
107882             var bufOp$2 = new BufferOp(g$3);
107883             bufOp$2.setQuadrantSegments(quadrantSegments$1);
107884             bufOp$2.setEndCapStyle(endCapStyle);
107885             var geomBuf$3 = bufOp$2.getResultGeometry(distance$3);
107886             return geomBuf$3
107887           }
107888         };
107889         BufferOp.precisionScaleFactor = function precisionScaleFactor (g, distance, maxPrecisionDigits) {
107890           var env = g.getEnvelopeInternal();
107891           var envMax = MathUtil.max(Math.abs(env.getMaxX()), Math.abs(env.getMaxY()), Math.abs(env.getMinX()), Math.abs(env.getMinY()));
107892           var expandByDistance = distance > 0.0 ? distance : 0.0;
107893           var bufEnvMax = envMax + 2 * expandByDistance;
107894           var bufEnvPrecisionDigits = Math.trunc(Math.log(bufEnvMax) / Math.log(10) + 1.0);
107895           var minUnitLog10 = maxPrecisionDigits - bufEnvPrecisionDigits;
107896           var scaleFactor = Math.pow(10.0, minUnitLog10);
107897           return scaleFactor
107898         };
107899         staticAccessors$32.CAP_ROUND.get = function () { return BufferParameters.CAP_ROUND };
107900         staticAccessors$32.CAP_BUTT.get = function () { return BufferParameters.CAP_FLAT };
107901         staticAccessors$32.CAP_FLAT.get = function () { return BufferParameters.CAP_FLAT };
107902         staticAccessors$32.CAP_SQUARE.get = function () { return BufferParameters.CAP_SQUARE };
107903         staticAccessors$32.MAX_PRECISION_DIGITS.get = function () { return 12 };
107904
107905         Object.defineProperties( BufferOp, staticAccessors$32 );
107906
107907         var PointPairDistance = function PointPairDistance () {
107908           this._pt = [new Coordinate(), new Coordinate()];
107909           this._distance = Double.NaN;
107910           this._isNull = true;
107911         };
107912         PointPairDistance.prototype.getCoordinates = function getCoordinates () {
107913           return this._pt
107914         };
107915         PointPairDistance.prototype.getCoordinate = function getCoordinate (i) {
107916           return this._pt[i]
107917         };
107918         PointPairDistance.prototype.setMinimum = function setMinimum () {
107919           if (arguments.length === 1) {
107920             var ptDist = arguments[0];
107921             this.setMinimum(ptDist._pt[0], ptDist._pt[1]);
107922           } else if (arguments.length === 2) {
107923             var p0 = arguments[0];
107924             var p1 = arguments[1];
107925             if (this._isNull) {
107926               this.initialize(p0, p1);
107927               return null
107928             }
107929             var dist = p0.distance(p1);
107930             if (dist < this._distance) { this.initialize(p0, p1, dist); }
107931           }
107932         };
107933         PointPairDistance.prototype.initialize = function initialize () {
107934           if (arguments.length === 0) {
107935             this._isNull = true;
107936           } else if (arguments.length === 2) {
107937             var p0 = arguments[0];
107938             var p1 = arguments[1];
107939             this._pt[0].setCoordinate(p0);
107940             this._pt[1].setCoordinate(p1);
107941             this._distance = p0.distance(p1);
107942             this._isNull = false;
107943           } else if (arguments.length === 3) {
107944             var p0$1 = arguments[0];
107945             var p1$1 = arguments[1];
107946             var distance = arguments[2];
107947             this._pt[0].setCoordinate(p0$1);
107948             this._pt[1].setCoordinate(p1$1);
107949             this._distance = distance;
107950             this._isNull = false;
107951           }
107952         };
107953         PointPairDistance.prototype.getDistance = function getDistance () {
107954           return this._distance
107955         };
107956         PointPairDistance.prototype.setMaximum = function setMaximum () {
107957           if (arguments.length === 1) {
107958             var ptDist = arguments[0];
107959             this.setMaximum(ptDist._pt[0], ptDist._pt[1]);
107960           } else if (arguments.length === 2) {
107961             var p0 = arguments[0];
107962             var p1 = arguments[1];
107963             if (this._isNull) {
107964               this.initialize(p0, p1);
107965               return null
107966             }
107967             var dist = p0.distance(p1);
107968             if (dist > this._distance) { this.initialize(p0, p1, dist); }
107969           }
107970         };
107971         PointPairDistance.prototype.interfaces_ = function interfaces_ () {
107972           return []
107973         };
107974         PointPairDistance.prototype.getClass = function getClass () {
107975           return PointPairDistance
107976         };
107977
107978         var DistanceToPointFinder = function DistanceToPointFinder () {};
107979
107980         DistanceToPointFinder.prototype.interfaces_ = function interfaces_ () {
107981           return []
107982         };
107983         DistanceToPointFinder.prototype.getClass = function getClass () {
107984           return DistanceToPointFinder
107985         };
107986         DistanceToPointFinder.computeDistance = function computeDistance () {
107987           if (arguments[2] instanceof PointPairDistance && (arguments[0] instanceof LineString && arguments[1] instanceof Coordinate)) {
107988             var line = arguments[0];
107989             var pt = arguments[1];
107990             var ptDist = arguments[2];
107991             var coords = line.getCoordinates();
107992             var tempSegment = new LineSegment();
107993             for (var i = 0; i < coords.length - 1; i++) {
107994               tempSegment.setCoordinates(coords[i], coords[i + 1]);
107995               var closestPt = tempSegment.closestPoint(pt);
107996               ptDist.setMinimum(closestPt, pt);
107997             }
107998           } else if (arguments[2] instanceof PointPairDistance && (arguments[0] instanceof Polygon && arguments[1] instanceof Coordinate)) {
107999             var poly = arguments[0];
108000             var pt$1 = arguments[1];
108001             var ptDist$1 = arguments[2];
108002             DistanceToPointFinder.computeDistance(poly.getExteriorRing(), pt$1, ptDist$1);
108003             for (var i$1 = 0; i$1 < poly.getNumInteriorRing(); i$1++) {
108004               DistanceToPointFinder.computeDistance(poly.getInteriorRingN(i$1), pt$1, ptDist$1);
108005             }
108006           } else if (arguments[2] instanceof PointPairDistance && (arguments[0] instanceof Geometry && arguments[1] instanceof Coordinate)) {
108007             var geom = arguments[0];
108008             var pt$2 = arguments[1];
108009             var ptDist$2 = arguments[2];
108010             if (geom instanceof LineString) {
108011               DistanceToPointFinder.computeDistance(geom, pt$2, ptDist$2);
108012             } else if (geom instanceof Polygon) {
108013               DistanceToPointFinder.computeDistance(geom, pt$2, ptDist$2);
108014             } else if (geom instanceof GeometryCollection) {
108015               var gc = geom;
108016               for (var i$2 = 0; i$2 < gc.getNumGeometries(); i$2++) {
108017                 var g = gc.getGeometryN(i$2);
108018                 DistanceToPointFinder.computeDistance(g, pt$2, ptDist$2);
108019               }
108020             } else {
108021               ptDist$2.setMinimum(geom.getCoordinate(), pt$2);
108022             }
108023           } else if (arguments[2] instanceof PointPairDistance && (arguments[0] instanceof LineSegment && arguments[1] instanceof Coordinate)) {
108024             var segment = arguments[0];
108025             var pt$3 = arguments[1];
108026             var ptDist$3 = arguments[2];
108027             var closestPt$1 = segment.closestPoint(pt$3);
108028             ptDist$3.setMinimum(closestPt$1, pt$3);
108029           }
108030         };
108031
108032         var BufferCurveMaximumDistanceFinder = function BufferCurveMaximumDistanceFinder (inputGeom) {
108033           this._maxPtDist = new PointPairDistance();
108034           this._inputGeom = inputGeom || null;
108035         };
108036
108037         var staticAccessors$36 = { MaxPointDistanceFilter: { configurable: true },MaxMidpointDistanceFilter: { configurable: true } };
108038         BufferCurveMaximumDistanceFinder.prototype.computeMaxMidpointDistance = function computeMaxMidpointDistance (curve) {
108039           var distFilter = new MaxMidpointDistanceFilter(this._inputGeom);
108040           curve.apply(distFilter);
108041           this._maxPtDist.setMaximum(distFilter.getMaxPointDistance());
108042         };
108043         BufferCurveMaximumDistanceFinder.prototype.computeMaxVertexDistance = function computeMaxVertexDistance (curve) {
108044           var distFilter = new MaxPointDistanceFilter(this._inputGeom);
108045           curve.apply(distFilter);
108046           this._maxPtDist.setMaximum(distFilter.getMaxPointDistance());
108047         };
108048         BufferCurveMaximumDistanceFinder.prototype.findDistance = function findDistance (bufferCurve) {
108049           this.computeMaxVertexDistance(bufferCurve);
108050           this.computeMaxMidpointDistance(bufferCurve);
108051           return this._maxPtDist.getDistance()
108052         };
108053         BufferCurveMaximumDistanceFinder.prototype.getDistancePoints = function getDistancePoints () {
108054           return this._maxPtDist
108055         };
108056         BufferCurveMaximumDistanceFinder.prototype.interfaces_ = function interfaces_ () {
108057           return []
108058         };
108059         BufferCurveMaximumDistanceFinder.prototype.getClass = function getClass () {
108060           return BufferCurveMaximumDistanceFinder
108061         };
108062         staticAccessors$36.MaxPointDistanceFilter.get = function () { return MaxPointDistanceFilter };
108063         staticAccessors$36.MaxMidpointDistanceFilter.get = function () { return MaxMidpointDistanceFilter };
108064
108065         Object.defineProperties( BufferCurveMaximumDistanceFinder, staticAccessors$36 );
108066
108067         var MaxPointDistanceFilter = function MaxPointDistanceFilter (geom) {
108068           this._maxPtDist = new PointPairDistance();
108069           this._minPtDist = new PointPairDistance();
108070           this._geom = geom || null;
108071         };
108072         MaxPointDistanceFilter.prototype.filter = function filter (pt) {
108073           this._minPtDist.initialize();
108074           DistanceToPointFinder.computeDistance(this._geom, pt, this._minPtDist);
108075           this._maxPtDist.setMaximum(this._minPtDist);
108076         };
108077         MaxPointDistanceFilter.prototype.getMaxPointDistance = function getMaxPointDistance () {
108078           return this._maxPtDist
108079         };
108080         MaxPointDistanceFilter.prototype.interfaces_ = function interfaces_ () {
108081           return [CoordinateFilter]
108082         };
108083         MaxPointDistanceFilter.prototype.getClass = function getClass () {
108084           return MaxPointDistanceFilter
108085         };
108086
108087         var MaxMidpointDistanceFilter = function MaxMidpointDistanceFilter (geom) {
108088           this._maxPtDist = new PointPairDistance();
108089           this._minPtDist = new PointPairDistance();
108090           this._geom = geom || null;
108091         };
108092         MaxMidpointDistanceFilter.prototype.filter = function filter (seq, index) {
108093           if (index === 0) { return null }
108094           var p0 = seq.getCoordinate(index - 1);
108095           var p1 = seq.getCoordinate(index);
108096           var midPt = new Coordinate((p0.x + p1.x) / 2, (p0.y + p1.y) / 2);
108097           this._minPtDist.initialize();
108098           DistanceToPointFinder.computeDistance(this._geom, midPt, this._minPtDist);
108099           this._maxPtDist.setMaximum(this._minPtDist);
108100         };
108101         MaxMidpointDistanceFilter.prototype.isDone = function isDone () {
108102           return false
108103         };
108104         MaxMidpointDistanceFilter.prototype.isGeometryChanged = function isGeometryChanged () {
108105           return false
108106         };
108107         MaxMidpointDistanceFilter.prototype.getMaxPointDistance = function getMaxPointDistance () {
108108           return this._maxPtDist
108109         };
108110         MaxMidpointDistanceFilter.prototype.interfaces_ = function interfaces_ () {
108111           return [CoordinateSequenceFilter]
108112         };
108113         MaxMidpointDistanceFilter.prototype.getClass = function getClass () {
108114           return MaxMidpointDistanceFilter
108115         };
108116
108117         var PolygonExtracter = function PolygonExtracter (comps) {
108118           this._comps = comps || null;
108119         };
108120         PolygonExtracter.prototype.filter = function filter (geom) {
108121           if (geom instanceof Polygon) { this._comps.add(geom); }
108122         };
108123         PolygonExtracter.prototype.interfaces_ = function interfaces_ () {
108124           return [GeometryFilter]
108125         };
108126         PolygonExtracter.prototype.getClass = function getClass () {
108127           return PolygonExtracter
108128         };
108129         PolygonExtracter.getPolygons = function getPolygons () {
108130           if (arguments.length === 1) {
108131             var geom = arguments[0];
108132             return PolygonExtracter.getPolygons(geom, new ArrayList())
108133           } else if (arguments.length === 2) {
108134             var geom$1 = arguments[0];
108135             var list = arguments[1];
108136             if (geom$1 instanceof Polygon) {
108137               list.add(geom$1);
108138             } else if (geom$1 instanceof GeometryCollection) {
108139               geom$1.apply(new PolygonExtracter(list));
108140             }
108141             return list
108142           }
108143         };
108144
108145         var LinearComponentExtracter = function LinearComponentExtracter () {
108146           this._lines = null;
108147           this._isForcedToLineString = false;
108148           if (arguments.length === 1) {
108149             var lines = arguments[0];
108150             this._lines = lines;
108151           } else if (arguments.length === 2) {
108152             var lines$1 = arguments[0];
108153             var isForcedToLineString = arguments[1];
108154             this._lines = lines$1;
108155             this._isForcedToLineString = isForcedToLineString;
108156           }
108157         };
108158         LinearComponentExtracter.prototype.filter = function filter (geom) {
108159           if (this._isForcedToLineString && geom instanceof LinearRing) {
108160             var line = geom.getFactory().createLineString(geom.getCoordinateSequence());
108161             this._lines.add(line);
108162             return null
108163           }
108164           if (geom instanceof LineString) { this._lines.add(geom); }
108165         };
108166         LinearComponentExtracter.prototype.setForceToLineString = function setForceToLineString (isForcedToLineString) {
108167           this._isForcedToLineString = isForcedToLineString;
108168         };
108169         LinearComponentExtracter.prototype.interfaces_ = function interfaces_ () {
108170           return [GeometryComponentFilter]
108171         };
108172         LinearComponentExtracter.prototype.getClass = function getClass () {
108173           return LinearComponentExtracter
108174         };
108175         LinearComponentExtracter.getGeometry = function getGeometry () {
108176           if (arguments.length === 1) {
108177             var geom = arguments[0];
108178             return geom.getFactory().buildGeometry(LinearComponentExtracter.getLines(geom))
108179           } else if (arguments.length === 2) {
108180             var geom$1 = arguments[0];
108181             var forceToLineString = arguments[1];
108182             return geom$1.getFactory().buildGeometry(LinearComponentExtracter.getLines(geom$1, forceToLineString))
108183           }
108184         };
108185         LinearComponentExtracter.getLines = function getLines () {
108186           if (arguments.length === 1) {
108187             var geom = arguments[0];
108188             return LinearComponentExtracter.getLines(geom, false)
108189           } else if (arguments.length === 2) {
108190             if (hasInterface(arguments[0], Collection) && hasInterface(arguments[1], Collection)) {
108191               var geoms = arguments[0];
108192               var lines$1 = arguments[1];
108193               for (var i = geoms.iterator(); i.hasNext();) {
108194                 var g = i.next();
108195                 LinearComponentExtracter.getLines(g, lines$1);
108196               }
108197               return lines$1
108198             } else if (arguments[0] instanceof Geometry && typeof arguments[1] === 'boolean') {
108199               var geom$1 = arguments[0];
108200               var forceToLineString = arguments[1];
108201               var lines = new ArrayList();
108202               geom$1.apply(new LinearComponentExtracter(lines, forceToLineString));
108203               return lines
108204             } else if (arguments[0] instanceof Geometry && hasInterface(arguments[1], Collection)) {
108205               var geom$2 = arguments[0];
108206               var lines$2 = arguments[1];
108207               if (geom$2 instanceof LineString) {
108208                 lines$2.add(geom$2);
108209               } else {
108210                 geom$2.apply(new LinearComponentExtracter(lines$2));
108211               }
108212               return lines$2
108213             }
108214           } else if (arguments.length === 3) {
108215             if (typeof arguments[2] === 'boolean' && (hasInterface(arguments[0], Collection) && hasInterface(arguments[1], Collection))) {
108216               var geoms$1 = arguments[0];
108217               var lines$3 = arguments[1];
108218               var forceToLineString$1 = arguments[2];
108219               for (var i$1 = geoms$1.iterator(); i$1.hasNext();) {
108220                 var g$1 = i$1.next();
108221                 LinearComponentExtracter.getLines(g$1, lines$3, forceToLineString$1);
108222               }
108223               return lines$3
108224             } else if (typeof arguments[2] === 'boolean' && (arguments[0] instanceof Geometry && hasInterface(arguments[1], Collection))) {
108225               var geom$3 = arguments[0];
108226               var lines$4 = arguments[1];
108227               var forceToLineString$2 = arguments[2];
108228               geom$3.apply(new LinearComponentExtracter(lines$4, forceToLineString$2));
108229               return lines$4
108230             }
108231           }
108232         };
108233
108234         var PointLocator = function PointLocator () {
108235           this._boundaryRule = BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE;
108236           this._isIn = null;
108237           this._numBoundaries = null;
108238           if (arguments.length === 0) ; else if (arguments.length === 1) {
108239             var boundaryRule = arguments[0];
108240             if (boundaryRule === null) { throw new IllegalArgumentException('Rule must be non-null') }
108241             this._boundaryRule = boundaryRule;
108242           }
108243         };
108244         PointLocator.prototype.locateInternal = function locateInternal () {
108245             var this$1 = this;
108246
108247           if (arguments[0] instanceof Coordinate && arguments[1] instanceof Polygon) {
108248             var p = arguments[0];
108249             var poly = arguments[1];
108250             if (poly.isEmpty()) { return Location.EXTERIOR }
108251             var shell = poly.getExteriorRing();
108252             var shellLoc = this.locateInPolygonRing(p, shell);
108253             if (shellLoc === Location.EXTERIOR) { return Location.EXTERIOR }
108254             if (shellLoc === Location.BOUNDARY) { return Location.BOUNDARY }
108255             for (var i = 0; i < poly.getNumInteriorRing(); i++) {
108256               var hole = poly.getInteriorRingN(i);
108257               var holeLoc = this$1.locateInPolygonRing(p, hole);
108258               if (holeLoc === Location.INTERIOR) { return Location.EXTERIOR }
108259               if (holeLoc === Location.BOUNDARY) { return Location.BOUNDARY }
108260             }
108261             return Location.INTERIOR
108262           } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof LineString) {
108263             var p$1 = arguments[0];
108264             var l = arguments[1];
108265             if (!l.getEnvelopeInternal().intersects(p$1)) { return Location.EXTERIOR }
108266             var pt = l.getCoordinates();
108267             if (!l.isClosed()) {
108268               if (p$1.equals(pt[0]) || p$1.equals(pt[pt.length - 1])) {
108269                 return Location.BOUNDARY
108270               }
108271             }
108272             if (CGAlgorithms.isOnLine(p$1, pt)) { return Location.INTERIOR }
108273             return Location.EXTERIOR
108274           } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof Point$1) {
108275             var p$2 = arguments[0];
108276             var pt$1 = arguments[1];
108277             var ptCoord = pt$1.getCoordinate();
108278             if (ptCoord.equals2D(p$2)) { return Location.INTERIOR }
108279             return Location.EXTERIOR
108280           }
108281         };
108282         PointLocator.prototype.locateInPolygonRing = function locateInPolygonRing (p, ring) {
108283           if (!ring.getEnvelopeInternal().intersects(p)) { return Location.EXTERIOR }
108284           return CGAlgorithms.locatePointInRing(p, ring.getCoordinates())
108285         };
108286         PointLocator.prototype.intersects = function intersects (p, geom) {
108287           return this.locate(p, geom) !== Location.EXTERIOR
108288         };
108289         PointLocator.prototype.updateLocationInfo = function updateLocationInfo (loc) {
108290           if (loc === Location.INTERIOR) { this._isIn = true; }
108291           if (loc === Location.BOUNDARY) { this._numBoundaries++; }
108292         };
108293         PointLocator.prototype.computeLocation = function computeLocation (p, geom) {
108294             var this$1 = this;
108295
108296           if (geom instanceof Point$1) {
108297             this.updateLocationInfo(this.locateInternal(p, geom));
108298           }
108299           if (geom instanceof LineString) {
108300             this.updateLocationInfo(this.locateInternal(p, geom));
108301           } else if (geom instanceof Polygon) {
108302             this.updateLocationInfo(this.locateInternal(p, geom));
108303           } else if (geom instanceof MultiLineString) {
108304             var ml = geom;
108305             for (var i = 0; i < ml.getNumGeometries(); i++) {
108306               var l = ml.getGeometryN(i);
108307               this$1.updateLocationInfo(this$1.locateInternal(p, l));
108308             }
108309           } else if (geom instanceof MultiPolygon) {
108310             var mpoly = geom;
108311             for (var i$1 = 0; i$1 < mpoly.getNumGeometries(); i$1++) {
108312               var poly = mpoly.getGeometryN(i$1);
108313               this$1.updateLocationInfo(this$1.locateInternal(p, poly));
108314             }
108315           } else if (geom instanceof GeometryCollection) {
108316             var geomi = new GeometryCollectionIterator(geom);
108317             while (geomi.hasNext()) {
108318               var g2 = geomi.next();
108319               if (g2 !== geom) { this$1.computeLocation(p, g2); }
108320             }
108321           }
108322         };
108323         PointLocator.prototype.locate = function locate (p, geom) {
108324           if (geom.isEmpty()) { return Location.EXTERIOR }
108325           if (geom instanceof LineString) {
108326             return this.locateInternal(p, geom)
108327           } else if (geom instanceof Polygon) {
108328             return this.locateInternal(p, geom)
108329           }
108330           this._isIn = false;
108331           this._numBoundaries = 0;
108332           this.computeLocation(p, geom);
108333           if (this._boundaryRule.isInBoundary(this._numBoundaries)) { return Location.BOUNDARY }
108334           if (this._numBoundaries > 0 || this._isIn) { return Location.INTERIOR }
108335           return Location.EXTERIOR
108336         };
108337         PointLocator.prototype.interfaces_ = function interfaces_ () {
108338           return []
108339         };
108340         PointLocator.prototype.getClass = function getClass () {
108341           return PointLocator
108342         };
108343
108344         var GeometryLocation = function GeometryLocation () {
108345           this._component = null;
108346           this._segIndex = null;
108347           this._pt = null;
108348           if (arguments.length === 2) {
108349             var component = arguments[0];
108350             var pt = arguments[1];
108351             GeometryLocation.call(this, component, GeometryLocation.INSIDE_AREA, pt);
108352           } else if (arguments.length === 3) {
108353             var component$1 = arguments[0];
108354             var segIndex = arguments[1];
108355             var pt$1 = arguments[2];
108356             this._component = component$1;
108357             this._segIndex = segIndex;
108358             this._pt = pt$1;
108359           }
108360         };
108361
108362         var staticAccessors$38 = { INSIDE_AREA: { configurable: true } };
108363         GeometryLocation.prototype.isInsideArea = function isInsideArea () {
108364           return this._segIndex === GeometryLocation.INSIDE_AREA
108365         };
108366         GeometryLocation.prototype.getCoordinate = function getCoordinate () {
108367           return this._pt
108368         };
108369         GeometryLocation.prototype.getGeometryComponent = function getGeometryComponent () {
108370           return this._component
108371         };
108372         GeometryLocation.prototype.getSegmentIndex = function getSegmentIndex () {
108373           return this._segIndex
108374         };
108375         GeometryLocation.prototype.interfaces_ = function interfaces_ () {
108376           return []
108377         };
108378         GeometryLocation.prototype.getClass = function getClass () {
108379           return GeometryLocation
108380         };
108381         staticAccessors$38.INSIDE_AREA.get = function () { return -1 };
108382
108383         Object.defineProperties( GeometryLocation, staticAccessors$38 );
108384
108385         var PointExtracter = function PointExtracter (pts) {
108386           this._pts = pts || null;
108387         };
108388         PointExtracter.prototype.filter = function filter (geom) {
108389           if (geom instanceof Point$1) { this._pts.add(geom); }
108390         };
108391         PointExtracter.prototype.interfaces_ = function interfaces_ () {
108392           return [GeometryFilter]
108393         };
108394         PointExtracter.prototype.getClass = function getClass () {
108395           return PointExtracter
108396         };
108397         PointExtracter.getPoints = function getPoints () {
108398           if (arguments.length === 1) {
108399             var geom = arguments[0];
108400             if (geom instanceof Point$1) {
108401               return Collections.singletonList(geom)
108402             }
108403             return PointExtracter.getPoints(geom, new ArrayList())
108404           } else if (arguments.length === 2) {
108405             var geom$1 = arguments[0];
108406             var list = arguments[1];
108407             if (geom$1 instanceof Point$1) {
108408               list.add(geom$1);
108409             } else if (geom$1 instanceof GeometryCollection) {
108410               geom$1.apply(new PointExtracter(list));
108411             }
108412             return list
108413           }
108414         };
108415
108416         var ConnectedElementLocationFilter = function ConnectedElementLocationFilter () {
108417           this._locations = null;
108418           var locations = arguments[0];
108419           this._locations = locations;
108420         };
108421         ConnectedElementLocationFilter.prototype.filter = function filter (geom) {
108422           if (geom instanceof Point$1 || geom instanceof LineString || geom instanceof Polygon) { this._locations.add(new GeometryLocation(geom, 0, geom.getCoordinate())); }
108423         };
108424         ConnectedElementLocationFilter.prototype.interfaces_ = function interfaces_ () {
108425           return [GeometryFilter]
108426         };
108427         ConnectedElementLocationFilter.prototype.getClass = function getClass () {
108428           return ConnectedElementLocationFilter
108429         };
108430         ConnectedElementLocationFilter.getLocations = function getLocations (geom) {
108431           var locations = new ArrayList();
108432           geom.apply(new ConnectedElementLocationFilter(locations));
108433           return locations
108434         };
108435
108436         var DistanceOp = function DistanceOp () {
108437           this._geom = null;
108438           this._terminateDistance = 0.0;
108439           this._ptLocator = new PointLocator();
108440           this._minDistanceLocation = null;
108441           this._minDistance = Double.MAX_VALUE;
108442           if (arguments.length === 2) {
108443             var g0 = arguments[0];
108444             var g1 = arguments[1];
108445             this._geom = [g0, g1];
108446             this._terminateDistance = 0.0;
108447           } else if (arguments.length === 3) {
108448             var g0$1 = arguments[0];
108449             var g1$1 = arguments[1];
108450             var terminateDistance = arguments[2];
108451             this._geom = new Array(2).fill(null);
108452             this._geom[0] = g0$1;
108453             this._geom[1] = g1$1;
108454             this._terminateDistance = terminateDistance;
108455           }
108456         };
108457         DistanceOp.prototype.computeContainmentDistance = function computeContainmentDistance () {
108458             var this$1 = this;
108459
108460           if (arguments.length === 0) {
108461             var locPtPoly = new Array(2).fill(null);
108462             this.computeContainmentDistance(0, locPtPoly);
108463             if (this._minDistance <= this._terminateDistance) { return null }
108464             this.computeContainmentDistance(1, locPtPoly);
108465           } else if (arguments.length === 2) {
108466             var polyGeomIndex = arguments[0];
108467             var locPtPoly$1 = arguments[1];
108468             var locationsIndex = 1 - polyGeomIndex;
108469             var polys = PolygonExtracter.getPolygons(this._geom[polyGeomIndex]);
108470             if (polys.size() > 0) {
108471               var insideLocs = ConnectedElementLocationFilter.getLocations(this._geom[locationsIndex]);
108472               this.computeContainmentDistance(insideLocs, polys, locPtPoly$1);
108473               if (this._minDistance <= this._terminateDistance) {
108474                 this._minDistanceLocation[locationsIndex] = locPtPoly$1[0];
108475                 this._minDistanceLocation[polyGeomIndex] = locPtPoly$1[1];
108476                 return null
108477               }
108478             }
108479           } else if (arguments.length === 3) {
108480             if (arguments[2] instanceof Array && (hasInterface(arguments[0], List) && hasInterface(arguments[1], List))) {
108481               var locs = arguments[0];
108482               var polys$1 = arguments[1];
108483               var locPtPoly$2 = arguments[2];
108484               for (var i = 0; i < locs.size(); i++) {
108485                 var loc = locs.get(i);
108486                 for (var j = 0; j < polys$1.size(); j++) {
108487                   this$1.computeContainmentDistance(loc, polys$1.get(j), locPtPoly$2);
108488                   if (this$1._minDistance <= this$1._terminateDistance) { return null }
108489                 }
108490               }
108491             } else if (arguments[2] instanceof Array && (arguments[0] instanceof GeometryLocation && arguments[1] instanceof Polygon)) {
108492               var ptLoc = arguments[0];
108493               var poly = arguments[1];
108494               var locPtPoly$3 = arguments[2];
108495               var pt = ptLoc.getCoordinate();
108496               if (Location.EXTERIOR !== this._ptLocator.locate(pt, poly)) {
108497                 this._minDistance = 0.0;
108498                 locPtPoly$3[0] = ptLoc;
108499                 locPtPoly$3[1] = new GeometryLocation(poly, pt);
108500
108501                 return null
108502               }
108503             }
108504           }
108505         };
108506         DistanceOp.prototype.computeMinDistanceLinesPoints = function computeMinDistanceLinesPoints (lines, points, locGeom) {
108507             var this$1 = this;
108508
108509           for (var i = 0; i < lines.size(); i++) {
108510             var line = lines.get(i);
108511             for (var j = 0; j < points.size(); j++) {
108512               var pt = points.get(j);
108513               this$1.computeMinDistance(line, pt, locGeom);
108514               if (this$1._minDistance <= this$1._terminateDistance) { return null }
108515             }
108516           }
108517         };
108518         DistanceOp.prototype.computeFacetDistance = function computeFacetDistance () {
108519           var locGeom = new Array(2).fill(null);
108520           var lines0 = LinearComponentExtracter.getLines(this._geom[0]);
108521           var lines1 = LinearComponentExtracter.getLines(this._geom[1]);
108522           var pts0 = PointExtracter.getPoints(this._geom[0]);
108523           var pts1 = PointExtracter.getPoints(this._geom[1]);
108524           this.computeMinDistanceLines(lines0, lines1, locGeom);
108525           this.updateMinDistance(locGeom, false);
108526           if (this._minDistance <= this._terminateDistance) { return null }
108527           locGeom[0] = null;
108528           locGeom[1] = null;
108529           this.computeMinDistanceLinesPoints(lines0, pts1, locGeom);
108530           this.updateMinDistance(locGeom, false);
108531           if (this._minDistance <= this._terminateDistance) { return null }
108532           locGeom[0] = null;
108533           locGeom[1] = null;
108534           this.computeMinDistanceLinesPoints(lines1, pts0, locGeom);
108535           this.updateMinDistance(locGeom, true);
108536           if (this._minDistance <= this._terminateDistance) { return null }
108537           locGeom[0] = null;
108538           locGeom[1] = null;
108539           this.computeMinDistancePoints(pts0, pts1, locGeom);
108540           this.updateMinDistance(locGeom, false);
108541         };
108542         DistanceOp.prototype.nearestLocations = function nearestLocations () {
108543           this.computeMinDistance();
108544           return this._minDistanceLocation
108545         };
108546         DistanceOp.prototype.updateMinDistance = function updateMinDistance (locGeom, flip) {
108547           if (locGeom[0] === null) { return null }
108548           if (flip) {
108549             this._minDistanceLocation[0] = locGeom[1];
108550             this._minDistanceLocation[1] = locGeom[0];
108551           } else {
108552             this._minDistanceLocation[0] = locGeom[0];
108553             this._minDistanceLocation[1] = locGeom[1];
108554           }
108555         };
108556         DistanceOp.prototype.nearestPoints = function nearestPoints () {
108557           this.computeMinDistance();
108558           var nearestPts = [this._minDistanceLocation[0].getCoordinate(), this._minDistanceLocation[1].getCoordinate()];
108559           return nearestPts
108560         };
108561         DistanceOp.prototype.computeMinDistance = function computeMinDistance () {
108562             var this$1 = this;
108563
108564           if (arguments.length === 0) {
108565             if (this._minDistanceLocation !== null) { return null }
108566             this._minDistanceLocation = new Array(2).fill(null);
108567             this.computeContainmentDistance();
108568             if (this._minDistance <= this._terminateDistance) { return null }
108569             this.computeFacetDistance();
108570           } else if (arguments.length === 3) {
108571             if (arguments[2] instanceof Array && (arguments[0] instanceof LineString && arguments[1] instanceof Point$1)) {
108572               var line = arguments[0];
108573               var pt = arguments[1];
108574               var locGeom = arguments[2];
108575               if (line.getEnvelopeInternal().distance(pt.getEnvelopeInternal()) > this._minDistance) { return null }
108576               var coord0 = line.getCoordinates();
108577               var coord = pt.getCoordinate();
108578               for (var i = 0; i < coord0.length - 1; i++) {
108579                 var dist = CGAlgorithms.distancePointLine(coord, coord0[i], coord0[i + 1]);
108580                 if (dist < this$1._minDistance) {
108581                   this$1._minDistance = dist;
108582                   var seg = new LineSegment(coord0[i], coord0[i + 1]);
108583                   var segClosestPoint = seg.closestPoint(coord);
108584                   locGeom[0] = new GeometryLocation(line, i, segClosestPoint);
108585                   locGeom[1] = new GeometryLocation(pt, 0, coord);
108586                 }
108587                 if (this$1._minDistance <= this$1._terminateDistance) { return null }
108588               }
108589             } else if (arguments[2] instanceof Array && (arguments[0] instanceof LineString && arguments[1] instanceof LineString)) {
108590               var line0 = arguments[0];
108591               var line1 = arguments[1];
108592               var locGeom$1 = arguments[2];
108593               if (line0.getEnvelopeInternal().distance(line1.getEnvelopeInternal()) > this._minDistance) { return null }
108594               var coord0$1 = line0.getCoordinates();
108595               var coord1 = line1.getCoordinates();
108596               for (var i$1 = 0; i$1 < coord0$1.length - 1; i$1++) {
108597                 for (var j = 0; j < coord1.length - 1; j++) {
108598                   var dist$1 = CGAlgorithms.distanceLineLine(coord0$1[i$1], coord0$1[i$1 + 1], coord1[j], coord1[j + 1]);
108599                   if (dist$1 < this$1._minDistance) {
108600                     this$1._minDistance = dist$1;
108601                     var seg0 = new LineSegment(coord0$1[i$1], coord0$1[i$1 + 1]);
108602                     var seg1 = new LineSegment(coord1[j], coord1[j + 1]);
108603                     var closestPt = seg0.closestPoints(seg1);
108604                     locGeom$1[0] = new GeometryLocation(line0, i$1, closestPt[0]);
108605                     locGeom$1[1] = new GeometryLocation(line1, j, closestPt[1]);
108606                   }
108607                   if (this$1._minDistance <= this$1._terminateDistance) { return null }
108608                 }
108609               }
108610             }
108611           }
108612         };
108613         DistanceOp.prototype.computeMinDistancePoints = function computeMinDistancePoints (points0, points1, locGeom) {
108614             var this$1 = this;
108615
108616           for (var i = 0; i < points0.size(); i++) {
108617             var pt0 = points0.get(i);
108618             for (var j = 0; j < points1.size(); j++) {
108619               var pt1 = points1.get(j);
108620               var dist = pt0.getCoordinate().distance(pt1.getCoordinate());
108621               if (dist < this$1._minDistance) {
108622                 this$1._minDistance = dist;
108623                 locGeom[0] = new GeometryLocation(pt0, 0, pt0.getCoordinate());
108624                 locGeom[1] = new GeometryLocation(pt1, 0, pt1.getCoordinate());
108625               }
108626               if (this$1._minDistance <= this$1._terminateDistance) { return null }
108627             }
108628           }
108629         };
108630         DistanceOp.prototype.distance = function distance () {
108631           if (this._geom[0] === null || this._geom[1] === null) { throw new IllegalArgumentException('null geometries are not supported') }
108632           if (this._geom[0].isEmpty() || this._geom[1].isEmpty()) { return 0.0 }
108633           this.computeMinDistance();
108634           return this._minDistance
108635         };
108636         DistanceOp.prototype.computeMinDistanceLines = function computeMinDistanceLines (lines0, lines1, locGeom) {
108637             var this$1 = this;
108638
108639           for (var i = 0; i < lines0.size(); i++) {
108640             var line0 = lines0.get(i);
108641             for (var j = 0; j < lines1.size(); j++) {
108642               var line1 = lines1.get(j);
108643               this$1.computeMinDistance(line0, line1, locGeom);
108644               if (this$1._minDistance <= this$1._terminateDistance) { return null }
108645             }
108646           }
108647         };
108648         DistanceOp.prototype.interfaces_ = function interfaces_ () {
108649           return []
108650         };
108651         DistanceOp.prototype.getClass = function getClass () {
108652           return DistanceOp
108653         };
108654         DistanceOp.distance = function distance (g0, g1) {
108655           var distOp = new DistanceOp(g0, g1);
108656           return distOp.distance()
108657         };
108658         DistanceOp.isWithinDistance = function isWithinDistance (g0, g1, distance) {
108659           var distOp = new DistanceOp(g0, g1, distance);
108660           return distOp.distance() <= distance
108661         };
108662         DistanceOp.nearestPoints = function nearestPoints (g0, g1) {
108663           var distOp = new DistanceOp(g0, g1);
108664           return distOp.nearestPoints()
108665         };
108666
108667         var PointPairDistance$2 = function PointPairDistance () {
108668           this._pt = [new Coordinate(), new Coordinate()];
108669           this._distance = Double.NaN;
108670           this._isNull = true;
108671         };
108672         PointPairDistance$2.prototype.getCoordinates = function getCoordinates () {
108673           return this._pt
108674         };
108675         PointPairDistance$2.prototype.getCoordinate = function getCoordinate (i) {
108676           return this._pt[i]
108677         };
108678         PointPairDistance$2.prototype.setMinimum = function setMinimum () {
108679           if (arguments.length === 1) {
108680             var ptDist = arguments[0];
108681             this.setMinimum(ptDist._pt[0], ptDist._pt[1]);
108682           } else if (arguments.length === 2) {
108683             var p0 = arguments[0];
108684             var p1 = arguments[1];
108685             if (this._isNull) {
108686               this.initialize(p0, p1);
108687               return null
108688             }
108689             var dist = p0.distance(p1);
108690             if (dist < this._distance) { this.initialize(p0, p1, dist); }
108691           }
108692         };
108693         PointPairDistance$2.prototype.initialize = function initialize () {
108694           if (arguments.length === 0) {
108695             this._isNull = true;
108696           } else if (arguments.length === 2) {
108697             var p0 = arguments[0];
108698             var p1 = arguments[1];
108699             this._pt[0].setCoordinate(p0);
108700             this._pt[1].setCoordinate(p1);
108701             this._distance = p0.distance(p1);
108702             this._isNull = false;
108703           } else if (arguments.length === 3) {
108704             var p0$1 = arguments[0];
108705             var p1$1 = arguments[1];
108706             var distance = arguments[2];
108707             this._pt[0].setCoordinate(p0$1);
108708             this._pt[1].setCoordinate(p1$1);
108709             this._distance = distance;
108710             this._isNull = false;
108711           }
108712         };
108713         PointPairDistance$2.prototype.toString = function toString () {
108714           return WKTWriter.toLineString(this._pt[0], this._pt[1])
108715         };
108716         PointPairDistance$2.prototype.getDistance = function getDistance () {
108717           return this._distance
108718         };
108719         PointPairDistance$2.prototype.setMaximum = function setMaximum () {
108720           if (arguments.length === 1) {
108721             var ptDist = arguments[0];
108722             this.setMaximum(ptDist._pt[0], ptDist._pt[1]);
108723           } else if (arguments.length === 2) {
108724             var p0 = arguments[0];
108725             var p1 = arguments[1];
108726             if (this._isNull) {
108727               this.initialize(p0, p1);
108728               return null
108729             }
108730             var dist = p0.distance(p1);
108731             if (dist > this._distance) { this.initialize(p0, p1, dist); }
108732           }
108733         };
108734         PointPairDistance$2.prototype.interfaces_ = function interfaces_ () {
108735           return []
108736         };
108737         PointPairDistance$2.prototype.getClass = function getClass () {
108738           return PointPairDistance$2
108739         };
108740
108741         var DistanceToPoint = function DistanceToPoint () {};
108742
108743         DistanceToPoint.prototype.interfaces_ = function interfaces_ () {
108744           return []
108745         };
108746         DistanceToPoint.prototype.getClass = function getClass () {
108747           return DistanceToPoint
108748         };
108749         DistanceToPoint.computeDistance = function computeDistance () {
108750           if (arguments[2] instanceof PointPairDistance$2 && (arguments[0] instanceof LineString && arguments[1] instanceof Coordinate)) {
108751             var line = arguments[0];
108752             var pt = arguments[1];
108753             var ptDist = arguments[2];
108754             var tempSegment = new LineSegment();
108755             var coords = line.getCoordinates();
108756             for (var i = 0; i < coords.length - 1; i++) {
108757               tempSegment.setCoordinates(coords[i], coords[i + 1]);
108758               var closestPt = tempSegment.closestPoint(pt);
108759               ptDist.setMinimum(closestPt, pt);
108760             }
108761           } else if (arguments[2] instanceof PointPairDistance$2 && (arguments[0] instanceof Polygon && arguments[1] instanceof Coordinate)) {
108762             var poly = arguments[0];
108763             var pt$1 = arguments[1];
108764             var ptDist$1 = arguments[2];
108765             DistanceToPoint.computeDistance(poly.getExteriorRing(), pt$1, ptDist$1);
108766             for (var i$1 = 0; i$1 < poly.getNumInteriorRing(); i$1++) {
108767               DistanceToPoint.computeDistance(poly.getInteriorRingN(i$1), pt$1, ptDist$1);
108768             }
108769           } else if (arguments[2] instanceof PointPairDistance$2 && (arguments[0] instanceof Geometry && arguments[1] instanceof Coordinate)) {
108770             var geom = arguments[0];
108771             var pt$2 = arguments[1];
108772             var ptDist$2 = arguments[2];
108773             if (geom instanceof LineString) {
108774               DistanceToPoint.computeDistance(geom, pt$2, ptDist$2);
108775             } else if (geom instanceof Polygon) {
108776               DistanceToPoint.computeDistance(geom, pt$2, ptDist$2);
108777             } else if (geom instanceof GeometryCollection) {
108778               var gc = geom;
108779               for (var i$2 = 0; i$2 < gc.getNumGeometries(); i$2++) {
108780                 var g = gc.getGeometryN(i$2);
108781                 DistanceToPoint.computeDistance(g, pt$2, ptDist$2);
108782               }
108783             } else {
108784               ptDist$2.setMinimum(geom.getCoordinate(), pt$2);
108785             }
108786           } else if (arguments[2] instanceof PointPairDistance$2 && (arguments[0] instanceof LineSegment && arguments[1] instanceof Coordinate)) {
108787             var segment = arguments[0];
108788             var pt$3 = arguments[1];
108789             var ptDist$3 = arguments[2];
108790             var closestPt$1 = segment.closestPoint(pt$3);
108791             ptDist$3.setMinimum(closestPt$1, pt$3);
108792           }
108793         };
108794
108795         var DiscreteHausdorffDistance = function DiscreteHausdorffDistance () {
108796           this._g0 = null;
108797           this._g1 = null;
108798           this._ptDist = new PointPairDistance$2();
108799           this._densifyFrac = 0.0;
108800           var g0 = arguments[0];
108801           var g1 = arguments[1];
108802           this._g0 = g0;
108803           this._g1 = g1;
108804         };
108805
108806         var staticAccessors$39 = { MaxPointDistanceFilter: { configurable: true },MaxDensifiedByFractionDistanceFilter: { configurable: true } };
108807         DiscreteHausdorffDistance.prototype.getCoordinates = function getCoordinates () {
108808           return this._ptDist.getCoordinates()
108809         };
108810         DiscreteHausdorffDistance.prototype.setDensifyFraction = function setDensifyFraction (densifyFrac) {
108811           if (densifyFrac > 1.0 || densifyFrac <= 0.0) { throw new IllegalArgumentException('Fraction is not in range (0.0 - 1.0]') }
108812           this._densifyFrac = densifyFrac;
108813         };
108814         DiscreteHausdorffDistance.prototype.compute = function compute (g0, g1) {
108815           this.computeOrientedDistance(g0, g1, this._ptDist);
108816           this.computeOrientedDistance(g1, g0, this._ptDist);
108817         };
108818         DiscreteHausdorffDistance.prototype.distance = function distance () {
108819           this.compute(this._g0, this._g1);
108820           return this._ptDist.getDistance()
108821         };
108822         DiscreteHausdorffDistance.prototype.computeOrientedDistance = function computeOrientedDistance (discreteGeom, geom, ptDist) {
108823           var distFilter = new MaxPointDistanceFilter$1(geom);
108824           discreteGeom.apply(distFilter);
108825           ptDist.setMaximum(distFilter.getMaxPointDistance());
108826           if (this._densifyFrac > 0) {
108827             var fracFilter = new MaxDensifiedByFractionDistanceFilter(geom, this._densifyFrac);
108828             discreteGeom.apply(fracFilter);
108829             ptDist.setMaximum(fracFilter.getMaxPointDistance());
108830           }
108831         };
108832         DiscreteHausdorffDistance.prototype.orientedDistance = function orientedDistance () {
108833           this.computeOrientedDistance(this._g0, this._g1, this._ptDist);
108834           return this._ptDist.getDistance()
108835         };
108836         DiscreteHausdorffDistance.prototype.interfaces_ = function interfaces_ () {
108837           return []
108838         };
108839         DiscreteHausdorffDistance.prototype.getClass = function getClass () {
108840           return DiscreteHausdorffDistance
108841         };
108842         DiscreteHausdorffDistance.distance = function distance () {
108843           if (arguments.length === 2) {
108844             var g0 = arguments[0];
108845             var g1 = arguments[1];
108846             var dist = new DiscreteHausdorffDistance(g0, g1);
108847             return dist.distance()
108848           } else if (arguments.length === 3) {
108849             var g0$1 = arguments[0];
108850             var g1$1 = arguments[1];
108851             var densifyFrac = arguments[2];
108852             var dist$1 = new DiscreteHausdorffDistance(g0$1, g1$1);
108853             dist$1.setDensifyFraction(densifyFrac);
108854             return dist$1.distance()
108855           }
108856         };
108857         staticAccessors$39.MaxPointDistanceFilter.get = function () { return MaxPointDistanceFilter$1 };
108858         staticAccessors$39.MaxDensifiedByFractionDistanceFilter.get = function () { return MaxDensifiedByFractionDistanceFilter };
108859
108860         Object.defineProperties( DiscreteHausdorffDistance, staticAccessors$39 );
108861
108862         var MaxPointDistanceFilter$1 = function MaxPointDistanceFilter () {
108863           this._maxPtDist = new PointPairDistance$2();
108864           this._minPtDist = new PointPairDistance$2();
108865           this._euclideanDist = new DistanceToPoint();
108866           this._geom = null;
108867           var geom = arguments[0];
108868           this._geom = geom;
108869         };
108870         MaxPointDistanceFilter$1.prototype.filter = function filter (pt) {
108871           this._minPtDist.initialize();
108872           DistanceToPoint.computeDistance(this._geom, pt, this._minPtDist);
108873           this._maxPtDist.setMaximum(this._minPtDist);
108874         };
108875         MaxPointDistanceFilter$1.prototype.getMaxPointDistance = function getMaxPointDistance () {
108876           return this._maxPtDist
108877         };
108878         MaxPointDistanceFilter$1.prototype.interfaces_ = function interfaces_ () {
108879           return [CoordinateFilter]
108880         };
108881         MaxPointDistanceFilter$1.prototype.getClass = function getClass () {
108882           return MaxPointDistanceFilter$1
108883         };
108884
108885         var MaxDensifiedByFractionDistanceFilter = function MaxDensifiedByFractionDistanceFilter () {
108886           this._maxPtDist = new PointPairDistance$2();
108887           this._minPtDist = new PointPairDistance$2();
108888           this._geom = null;
108889           this._numSubSegs = 0;
108890           var geom = arguments[0];
108891           var fraction = arguments[1];
108892           this._geom = geom;
108893           this._numSubSegs = Math.trunc(Math.round(1.0 / fraction));
108894         };
108895         MaxDensifiedByFractionDistanceFilter.prototype.filter = function filter (seq, index) {
108896             var this$1 = this;
108897
108898           if (index === 0) { return null }
108899           var p0 = seq.getCoordinate(index - 1);
108900           var p1 = seq.getCoordinate(index);
108901           var delx = (p1.x - p0.x) / this._numSubSegs;
108902           var dely = (p1.y - p0.y) / this._numSubSegs;
108903           for (var i = 0; i < this._numSubSegs; i++) {
108904             var x = p0.x + i * delx;
108905             var y = p0.y + i * dely;
108906             var pt = new Coordinate(x, y);
108907             this$1._minPtDist.initialize();
108908             DistanceToPoint.computeDistance(this$1._geom, pt, this$1._minPtDist);
108909             this$1._maxPtDist.setMaximum(this$1._minPtDist);
108910           }
108911         };
108912         MaxDensifiedByFractionDistanceFilter.prototype.isDone = function isDone () {
108913           return false
108914         };
108915         MaxDensifiedByFractionDistanceFilter.prototype.isGeometryChanged = function isGeometryChanged () {
108916           return false
108917         };
108918         MaxDensifiedByFractionDistanceFilter.prototype.getMaxPointDistance = function getMaxPointDistance () {
108919           return this._maxPtDist
108920         };
108921         MaxDensifiedByFractionDistanceFilter.prototype.interfaces_ = function interfaces_ () {
108922           return [CoordinateSequenceFilter]
108923         };
108924         MaxDensifiedByFractionDistanceFilter.prototype.getClass = function getClass () {
108925           return MaxDensifiedByFractionDistanceFilter
108926         };
108927
108928         var BufferDistanceValidator = function BufferDistanceValidator (input, bufDistance, result) {
108929           this._minValidDistance = null;
108930           this._maxValidDistance = null;
108931           this._minDistanceFound = null;
108932           this._maxDistanceFound = null;
108933           this._isValid = true;
108934           this._errMsg = null;
108935           this._errorLocation = null;
108936           this._errorIndicator = null;
108937           this._input = input || null;
108938           this._bufDistance = bufDistance || null;
108939           this._result = result || null;
108940         };
108941
108942         var staticAccessors$37 = { VERBOSE: { configurable: true },MAX_DISTANCE_DIFF_FRAC: { configurable: true } };
108943         BufferDistanceValidator.prototype.checkMaximumDistance = function checkMaximumDistance (input, bufCurve, maxDist) {
108944           var haus = new DiscreteHausdorffDistance(bufCurve, input);
108945           haus.setDensifyFraction(0.25);
108946           this._maxDistanceFound = haus.orientedDistance();
108947           if (this._maxDistanceFound > maxDist) {
108948             this._isValid = false;
108949             var pts = haus.getCoordinates();
108950             this._errorLocation = pts[1];
108951             this._errorIndicator = input.getFactory().createLineString(pts);
108952             this._errMsg = 'Distance between buffer curve and input is too large (' + this._maxDistanceFound + ' at ' + WKTWriter.toLineString(pts[0], pts[1]) + ')';
108953           }
108954         };
108955         BufferDistanceValidator.prototype.isValid = function isValid () {
108956           var posDistance = Math.abs(this._bufDistance);
108957           var distDelta = BufferDistanceValidator.MAX_DISTANCE_DIFF_FRAC * posDistance;
108958           this._minValidDistance = posDistance - distDelta;
108959           this._maxValidDistance = posDistance + distDelta;
108960           if (this._input.isEmpty() || this._result.isEmpty()) { return true }
108961           if (this._bufDistance > 0.0) {
108962             this.checkPositiveValid();
108963           } else {
108964             this.checkNegativeValid();
108965           }
108966           if (BufferDistanceValidator.VERBOSE) {
108967             System.out.println('Min Dist= ' + this._minDistanceFound + '  err= ' + (1.0 - this._minDistanceFound / this._bufDistance) + '  Max Dist= ' + this._maxDistanceFound + '  err= ' + (this._maxDistanceFound / this._bufDistance - 1.0));
108968           }
108969           return this._isValid
108970         };
108971         BufferDistanceValidator.prototype.checkNegativeValid = function checkNegativeValid () {
108972           if (!(this._input instanceof Polygon || this._input instanceof MultiPolygon || this._input instanceof GeometryCollection)) {
108973             return null
108974           }
108975           var inputCurve = this.getPolygonLines(this._input);
108976           this.checkMinimumDistance(inputCurve, this._result, this._minValidDistance);
108977           if (!this._isValid) { return null }
108978           this.checkMaximumDistance(inputCurve, this._result, this._maxValidDistance);
108979         };
108980         BufferDistanceValidator.prototype.getErrorIndicator = function getErrorIndicator () {
108981           return this._errorIndicator
108982         };
108983         BufferDistanceValidator.prototype.checkMinimumDistance = function checkMinimumDistance (g1, g2, minDist) {
108984           var distOp = new DistanceOp(g1, g2, minDist);
108985           this._minDistanceFound = distOp.distance();
108986           if (this._minDistanceFound < minDist) {
108987             this._isValid = false;
108988             var pts = distOp.nearestPoints();
108989             this._errorLocation = distOp.nearestPoints()[1];
108990             this._errorIndicator = g1.getFactory().createLineString(pts);
108991             this._errMsg = 'Distance between buffer curve and input is too small (' + this._minDistanceFound + ' at ' + WKTWriter.toLineString(pts[0], pts[1]) + ' )';
108992           }
108993         };
108994         BufferDistanceValidator.prototype.checkPositiveValid = function checkPositiveValid () {
108995           var bufCurve = this._result.getBoundary();
108996           this.checkMinimumDistance(this._input, bufCurve, this._minValidDistance);
108997           if (!this._isValid) { return null }
108998           this.checkMaximumDistance(this._input, bufCurve, this._maxValidDistance);
108999         };
109000         BufferDistanceValidator.prototype.getErrorLocation = function getErrorLocation () {
109001           return this._errorLocation
109002         };
109003         BufferDistanceValidator.prototype.getPolygonLines = function getPolygonLines (g) {
109004           var lines = new ArrayList();
109005           var lineExtracter = new LinearComponentExtracter(lines);
109006           var polys = PolygonExtracter.getPolygons(g);
109007           for (var i = polys.iterator(); i.hasNext();) {
109008             var poly = i.next();
109009             poly.apply(lineExtracter);
109010           }
109011           return g.getFactory().buildGeometry(lines)
109012         };
109013         BufferDistanceValidator.prototype.getErrorMessage = function getErrorMessage () {
109014           return this._errMsg
109015         };
109016         BufferDistanceValidator.prototype.interfaces_ = function interfaces_ () {
109017           return []
109018         };
109019         BufferDistanceValidator.prototype.getClass = function getClass () {
109020           return BufferDistanceValidator
109021         };
109022         staticAccessors$37.VERBOSE.get = function () { return false };
109023         staticAccessors$37.MAX_DISTANCE_DIFF_FRAC.get = function () { return 0.012 };
109024
109025         Object.defineProperties( BufferDistanceValidator, staticAccessors$37 );
109026
109027         var BufferResultValidator = function BufferResultValidator (input, distance, result) {
109028           this._isValid = true;
109029           this._errorMsg = null;
109030           this._errorLocation = null;
109031           this._errorIndicator = null;
109032           this._input = input || null;
109033           this._distance = distance || null;
109034           this._result = result || null;
109035         };
109036
109037         var staticAccessors$40 = { VERBOSE: { configurable: true },MAX_ENV_DIFF_FRAC: { configurable: true } };
109038         BufferResultValidator.prototype.isValid = function isValid () {
109039           this.checkPolygonal();
109040           if (!this._isValid) { return this._isValid }
109041           this.checkExpectedEmpty();
109042           if (!this._isValid) { return this._isValid }
109043           this.checkEnvelope();
109044           if (!this._isValid) { return this._isValid }
109045           this.checkArea();
109046           if (!this._isValid) { return this._isValid }
109047           this.checkDistance();
109048           return this._isValid
109049         };
109050         BufferResultValidator.prototype.checkEnvelope = function checkEnvelope () {
109051           if (this._distance < 0.0) { return null }
109052           var padding = this._distance * BufferResultValidator.MAX_ENV_DIFF_FRAC;
109053           if (padding === 0.0) { padding = 0.001; }
109054           var expectedEnv = new Envelope(this._input.getEnvelopeInternal());
109055           expectedEnv.expandBy(this._distance);
109056           var bufEnv = new Envelope(this._result.getEnvelopeInternal());
109057           bufEnv.expandBy(padding);
109058           if (!bufEnv.contains(expectedEnv)) {
109059             this._isValid = false;
109060             this._errorMsg = 'Buffer envelope is incorrect';
109061             this._errorIndicator = this._input.getFactory().toGeometry(bufEnv);
109062           }
109063           this.report('Envelope');
109064         };
109065         BufferResultValidator.prototype.checkDistance = function checkDistance () {
109066           var distValid = new BufferDistanceValidator(this._input, this._distance, this._result);
109067           if (!distValid.isValid()) {
109068             this._isValid = false;
109069             this._errorMsg = distValid.getErrorMessage();
109070             this._errorLocation = distValid.getErrorLocation();
109071             this._errorIndicator = distValid.getErrorIndicator();
109072           }
109073           this.report('Distance');
109074         };
109075         BufferResultValidator.prototype.checkArea = function checkArea () {
109076           var inputArea = this._input.getArea();
109077           var resultArea = this._result.getArea();
109078           if (this._distance > 0.0 && inputArea > resultArea) {
109079             this._isValid = false;
109080             this._errorMsg = 'Area of positive buffer is smaller than input';
109081             this._errorIndicator = this._result;
109082           }
109083           if (this._distance < 0.0 && inputArea < resultArea) {
109084             this._isValid = false;
109085             this._errorMsg = 'Area of negative buffer is larger than input';
109086             this._errorIndicator = this._result;
109087           }
109088           this.report('Area');
109089         };
109090         BufferResultValidator.prototype.checkPolygonal = function checkPolygonal () {
109091           if (!(this._result instanceof Polygon || this._result instanceof MultiPolygon)) { this._isValid = false; }
109092           this._errorMsg = 'Result is not polygonal';
109093           this._errorIndicator = this._result;
109094           this.report('Polygonal');
109095         };
109096         BufferResultValidator.prototype.getErrorIndicator = function getErrorIndicator () {
109097           return this._errorIndicator
109098         };
109099         BufferResultValidator.prototype.getErrorLocation = function getErrorLocation () {
109100           return this._errorLocation
109101         };
109102         BufferResultValidator.prototype.checkExpectedEmpty = function checkExpectedEmpty () {
109103           if (this._input.getDimension() >= 2) { return null }
109104           if (this._distance > 0.0) { return null }
109105           if (!this._result.isEmpty()) {
109106             this._isValid = false;
109107             this._errorMsg = 'Result is non-empty';
109108             this._errorIndicator = this._result;
109109           }
109110           this.report('ExpectedEmpty');
109111         };
109112         BufferResultValidator.prototype.report = function report (checkName) {
109113           if (!BufferResultValidator.VERBOSE) { return null }
109114           System.out.println('Check ' + checkName + ': ' + (this._isValid ? 'passed' : 'FAILED'));
109115         };
109116         BufferResultValidator.prototype.getErrorMessage = function getErrorMessage () {
109117           return this._errorMsg
109118         };
109119         BufferResultValidator.prototype.interfaces_ = function interfaces_ () {
109120           return []
109121         };
109122         BufferResultValidator.prototype.getClass = function getClass () {
109123           return BufferResultValidator
109124         };
109125         BufferResultValidator.isValidMsg = function isValidMsg (g, distance, result) {
109126           var validator = new BufferResultValidator(g, distance, result);
109127           if (!validator.isValid()) { return validator.getErrorMessage() }
109128           return null
109129         };
109130         BufferResultValidator.isValid = function isValid (g, distance, result) {
109131           var validator = new BufferResultValidator(g, distance, result);
109132           if (validator.isValid()) { return true }
109133           return false
109134         };
109135         staticAccessors$40.VERBOSE.get = function () { return false };
109136         staticAccessors$40.MAX_ENV_DIFF_FRAC.get = function () { return 0.012 };
109137
109138         Object.defineProperties( BufferResultValidator, staticAccessors$40 );
109139
109140         // operation.buffer
109141
109142         var BasicSegmentString = function BasicSegmentString () {
109143           this._pts = null;
109144           this._data = null;
109145           var pts = arguments[0];
109146           var data = arguments[1];
109147           this._pts = pts;
109148           this._data = data;
109149         };
109150         BasicSegmentString.prototype.getCoordinates = function getCoordinates () {
109151           return this._pts
109152         };
109153         BasicSegmentString.prototype.size = function size () {
109154           return this._pts.length
109155         };
109156         BasicSegmentString.prototype.getCoordinate = function getCoordinate (i) {
109157           return this._pts[i]
109158         };
109159         BasicSegmentString.prototype.isClosed = function isClosed () {
109160           return this._pts[0].equals(this._pts[this._pts.length - 1])
109161         };
109162         BasicSegmentString.prototype.getSegmentOctant = function getSegmentOctant (index) {
109163           if (index === this._pts.length - 1) { return -1 }
109164           return Octant.octant(this.getCoordinate(index), this.getCoordinate(index + 1))
109165         };
109166         BasicSegmentString.prototype.setData = function setData (data) {
109167           this._data = data;
109168         };
109169         BasicSegmentString.prototype.getData = function getData () {
109170           return this._data
109171         };
109172         BasicSegmentString.prototype.toString = function toString () {
109173           return WKTWriter.toLineString(new CoordinateArraySequence(this._pts))
109174         };
109175         BasicSegmentString.prototype.interfaces_ = function interfaces_ () {
109176           return [SegmentString]
109177         };
109178         BasicSegmentString.prototype.getClass = function getClass () {
109179           return BasicSegmentString
109180         };
109181
109182         var InteriorIntersectionFinder = function InteriorIntersectionFinder () {
109183           this._findAllIntersections = false;
109184           this._isCheckEndSegmentsOnly = false;
109185           this._li = null;
109186           this._interiorIntersection = null;
109187           this._intSegments = null;
109188           this._intersections = new ArrayList();
109189           this._intersectionCount = 0;
109190           this._keepIntersections = true;
109191           var li = arguments[0];
109192           this._li = li;
109193           this._interiorIntersection = null;
109194         };
109195         InteriorIntersectionFinder.prototype.getInteriorIntersection = function getInteriorIntersection () {
109196           return this._interiorIntersection
109197         };
109198         InteriorIntersectionFinder.prototype.setCheckEndSegmentsOnly = function setCheckEndSegmentsOnly (isCheckEndSegmentsOnly) {
109199           this._isCheckEndSegmentsOnly = isCheckEndSegmentsOnly;
109200         };
109201         InteriorIntersectionFinder.prototype.getIntersectionSegments = function getIntersectionSegments () {
109202           return this._intSegments
109203         };
109204         InteriorIntersectionFinder.prototype.count = function count () {
109205           return this._intersectionCount
109206         };
109207         InteriorIntersectionFinder.prototype.getIntersections = function getIntersections () {
109208           return this._intersections
109209         };
109210         InteriorIntersectionFinder.prototype.setFindAllIntersections = function setFindAllIntersections (findAllIntersections) {
109211           this._findAllIntersections = findAllIntersections;
109212         };
109213         InteriorIntersectionFinder.prototype.setKeepIntersections = function setKeepIntersections (keepIntersections) {
109214           this._keepIntersections = keepIntersections;
109215         };
109216         InteriorIntersectionFinder.prototype.processIntersections = function processIntersections (e0, segIndex0, e1, segIndex1) {
109217           if (!this._findAllIntersections && this.hasIntersection()) { return null }
109218           if (e0 === e1 && segIndex0 === segIndex1) { return null }
109219           if (this._isCheckEndSegmentsOnly) {
109220             var isEndSegPresent = this.isEndSegment(e0, segIndex0) || this.isEndSegment(e1, segIndex1);
109221             if (!isEndSegPresent) { return null }
109222           }
109223           var p00 = e0.getCoordinates()[segIndex0];
109224           var p01 = e0.getCoordinates()[segIndex0 + 1];
109225           var p10 = e1.getCoordinates()[segIndex1];
109226           var p11 = e1.getCoordinates()[segIndex1 + 1];
109227           this._li.computeIntersection(p00, p01, p10, p11);
109228           if (this._li.hasIntersection()) {
109229             if (this._li.isInteriorIntersection()) {
109230               this._intSegments = new Array(4).fill(null);
109231               this._intSegments[0] = p00;
109232               this._intSegments[1] = p01;
109233               this._intSegments[2] = p10;
109234               this._intSegments[3] = p11;
109235               this._interiorIntersection = this._li.getIntersection(0);
109236               if (this._keepIntersections) { this._intersections.add(this._interiorIntersection); }
109237               this._intersectionCount++;
109238             }
109239           }
109240         };
109241         InteriorIntersectionFinder.prototype.isEndSegment = function isEndSegment (segStr, index) {
109242           if (index === 0) { return true }
109243           if (index >= segStr.size() - 2) { return true }
109244           return false
109245         };
109246         InteriorIntersectionFinder.prototype.hasIntersection = function hasIntersection () {
109247           return this._interiorIntersection !== null
109248         };
109249         InteriorIntersectionFinder.prototype.isDone = function isDone () {
109250           if (this._findAllIntersections) { return false }
109251           return this._interiorIntersection !== null
109252         };
109253         InteriorIntersectionFinder.prototype.interfaces_ = function interfaces_ () {
109254           return [SegmentIntersector]
109255         };
109256         InteriorIntersectionFinder.prototype.getClass = function getClass () {
109257           return InteriorIntersectionFinder
109258         };
109259         InteriorIntersectionFinder.createAllIntersectionsFinder = function createAllIntersectionsFinder (li) {
109260           var finder = new InteriorIntersectionFinder(li);
109261           finder.setFindAllIntersections(true);
109262           return finder
109263         };
109264         InteriorIntersectionFinder.createAnyIntersectionFinder = function createAnyIntersectionFinder (li) {
109265           return new InteriorIntersectionFinder(li)
109266         };
109267         InteriorIntersectionFinder.createIntersectionCounter = function createIntersectionCounter (li) {
109268           var finder = new InteriorIntersectionFinder(li);
109269           finder.setFindAllIntersections(true);
109270           finder.setKeepIntersections(false);
109271           return finder
109272         };
109273
109274         var FastNodingValidator = function FastNodingValidator () {
109275           this._li = new RobustLineIntersector();
109276           this._segStrings = null;
109277           this._findAllIntersections = false;
109278           this._segInt = null;
109279           this._isValid = true;
109280           var segStrings = arguments[0];
109281           this._segStrings = segStrings;
109282         };
109283         FastNodingValidator.prototype.execute = function execute () {
109284           if (this._segInt !== null) { return null }
109285           this.checkInteriorIntersections();
109286         };
109287         FastNodingValidator.prototype.getIntersections = function getIntersections () {
109288           return this._segInt.getIntersections()
109289         };
109290         FastNodingValidator.prototype.isValid = function isValid () {
109291           this.execute();
109292           return this._isValid
109293         };
109294         FastNodingValidator.prototype.setFindAllIntersections = function setFindAllIntersections (findAllIntersections) {
109295           this._findAllIntersections = findAllIntersections;
109296         };
109297         FastNodingValidator.prototype.checkInteriorIntersections = function checkInteriorIntersections () {
109298           this._isValid = true;
109299           this._segInt = new InteriorIntersectionFinder(this._li);
109300           this._segInt.setFindAllIntersections(this._findAllIntersections);
109301           var noder = new MCIndexNoder();
109302           noder.setSegmentIntersector(this._segInt);
109303           noder.computeNodes(this._segStrings);
109304           if (this._segInt.hasIntersection()) {
109305             this._isValid = false;
109306             return null
109307           }
109308         };
109309         FastNodingValidator.prototype.checkValid = function checkValid () {
109310           this.execute();
109311           if (!this._isValid) { throw new TopologyException(this.getErrorMessage(), this._segInt.getInteriorIntersection()) }
109312         };
109313         FastNodingValidator.prototype.getErrorMessage = function getErrorMessage () {
109314           if (this._isValid) { return 'no intersections found' }
109315           var intSegs = this._segInt.getIntersectionSegments();
109316           return 'found non-noded intersection between ' + WKTWriter.toLineString(intSegs[0], intSegs[1]) + ' and ' + WKTWriter.toLineString(intSegs[2], intSegs[3])
109317         };
109318         FastNodingValidator.prototype.interfaces_ = function interfaces_ () {
109319           return []
109320         };
109321         FastNodingValidator.prototype.getClass = function getClass () {
109322           return FastNodingValidator
109323         };
109324         FastNodingValidator.computeIntersections = function computeIntersections (segStrings) {
109325           var nv = new FastNodingValidator(segStrings);
109326           nv.setFindAllIntersections(true);
109327           nv.isValid();
109328           return nv.getIntersections()
109329         };
109330
109331         var EdgeNodingValidator = function EdgeNodingValidator () {
109332           this._nv = null;
109333           var edges = arguments[0];
109334           this._nv = new FastNodingValidator(EdgeNodingValidator.toSegmentStrings(edges));
109335         };
109336         EdgeNodingValidator.prototype.checkValid = function checkValid () {
109337           this._nv.checkValid();
109338         };
109339         EdgeNodingValidator.prototype.interfaces_ = function interfaces_ () {
109340           return []
109341         };
109342         EdgeNodingValidator.prototype.getClass = function getClass () {
109343           return EdgeNodingValidator
109344         };
109345         EdgeNodingValidator.toSegmentStrings = function toSegmentStrings (edges) {
109346           var segStrings = new ArrayList();
109347           for (var i = edges.iterator(); i.hasNext();) {
109348             var e = i.next();
109349             segStrings.add(new BasicSegmentString(e.getCoordinates(), e));
109350           }
109351           return segStrings
109352         };
109353         EdgeNodingValidator.checkValid = function checkValid (edges) {
109354           var validator = new EdgeNodingValidator(edges);
109355           validator.checkValid();
109356         };
109357
109358         var GeometryCollectionMapper = function GeometryCollectionMapper (mapOp) {
109359           this._mapOp = mapOp;
109360         };
109361         GeometryCollectionMapper.prototype.map = function map (gc) {
109362             var this$1 = this;
109363
109364           var mapped = new ArrayList();
109365           for (var i = 0; i < gc.getNumGeometries(); i++) {
109366             var g = this$1._mapOp.map(gc.getGeometryN(i));
109367             if (!g.isEmpty()) { mapped.add(g); }
109368           }
109369           return gc.getFactory().createGeometryCollection(GeometryFactory.toGeometryArray(mapped))
109370         };
109371         GeometryCollectionMapper.prototype.interfaces_ = function interfaces_ () {
109372           return []
109373         };
109374         GeometryCollectionMapper.prototype.getClass = function getClass () {
109375           return GeometryCollectionMapper
109376         };
109377         GeometryCollectionMapper.map = function map (gc, op) {
109378           var mapper = new GeometryCollectionMapper(op);
109379           return mapper.map(gc)
109380         };
109381
109382         var LineBuilder = function LineBuilder () {
109383           this._op = null;
109384           this._geometryFactory = null;
109385           this._ptLocator = null;
109386           this._lineEdgesList = new ArrayList();
109387           this._resultLineList = new ArrayList();
109388           var op = arguments[0];
109389           var geometryFactory = arguments[1];
109390           var ptLocator = arguments[2];
109391           this._op = op;
109392           this._geometryFactory = geometryFactory;
109393           this._ptLocator = ptLocator;
109394         };
109395         LineBuilder.prototype.collectLines = function collectLines (opCode) {
109396             var this$1 = this;
109397
109398           for (var it = this._op.getGraph().getEdgeEnds().iterator(); it.hasNext();) {
109399             var de = it.next();
109400             this$1.collectLineEdge(de, opCode, this$1._lineEdgesList);
109401             this$1.collectBoundaryTouchEdge(de, opCode, this$1._lineEdgesList);
109402           }
109403         };
109404         LineBuilder.prototype.labelIsolatedLine = function labelIsolatedLine (e, targetIndex) {
109405           var loc = this._ptLocator.locate(e.getCoordinate(), this._op.getArgGeometry(targetIndex));
109406           e.getLabel().setLocation(targetIndex, loc);
109407         };
109408         LineBuilder.prototype.build = function build (opCode) {
109409           this.findCoveredLineEdges();
109410           this.collectLines(opCode);
109411           this.buildLines(opCode);
109412           return this._resultLineList
109413         };
109414         LineBuilder.prototype.collectLineEdge = function collectLineEdge (de, opCode, edges) {
109415           var label = de.getLabel();
109416           var e = de.getEdge();
109417           if (de.isLineEdge()) {
109418             if (!de.isVisited() && OverlayOp.isResultOfOp(label, opCode) && !e.isCovered()) {
109419               edges.add(e);
109420               de.setVisitedEdge(true);
109421             }
109422           }
109423         };
109424         LineBuilder.prototype.findCoveredLineEdges = function findCoveredLineEdges () {
109425             var this$1 = this;
109426
109427           for (var nodeit = this._op.getGraph().getNodes().iterator(); nodeit.hasNext();) {
109428             var node = nodeit.next();
109429             node.getEdges().findCoveredLineEdges();
109430           }
109431           for (var it = this._op.getGraph().getEdgeEnds().iterator(); it.hasNext();) {
109432             var de = it.next();
109433             var e = de.getEdge();
109434             if (de.isLineEdge() && !e.isCoveredSet()) {
109435               var isCovered = this$1._op.isCoveredByA(de.getCoordinate());
109436               e.setCovered(isCovered);
109437             }
109438           }
109439         };
109440         LineBuilder.prototype.labelIsolatedLines = function labelIsolatedLines (edgesList) {
109441             var this$1 = this;
109442
109443           for (var it = edgesList.iterator(); it.hasNext();) {
109444             var e = it.next();
109445             var label = e.getLabel();
109446             if (e.isIsolated()) {
109447               if (label.isNull(0)) { this$1.labelIsolatedLine(e, 0); } else { this$1.labelIsolatedLine(e, 1); }
109448             }
109449           }
109450         };
109451         LineBuilder.prototype.buildLines = function buildLines (opCode) {
109452             var this$1 = this;
109453
109454           for (var it = this._lineEdgesList.iterator(); it.hasNext();) {
109455             var e = it.next();
109456             // const label = e.getLabel()
109457             var line = this$1._geometryFactory.createLineString(e.getCoordinates());
109458             this$1._resultLineList.add(line);
109459             e.setInResult(true);
109460           }
109461         };
109462         LineBuilder.prototype.collectBoundaryTouchEdge = function collectBoundaryTouchEdge (de, opCode, edges) {
109463           var label = de.getLabel();
109464           if (de.isLineEdge()) { return null }
109465           if (de.isVisited()) { return null }
109466           if (de.isInteriorAreaEdge()) { return null }
109467           if (de.getEdge().isInResult()) { return null }
109468           Assert.isTrue(!(de.isInResult() || de.getSym().isInResult()) || !de.getEdge().isInResult());
109469           if (OverlayOp.isResultOfOp(label, opCode) && opCode === OverlayOp.INTERSECTION) {
109470             edges.add(de.getEdge());
109471             de.setVisitedEdge(true);
109472           }
109473         };
109474         LineBuilder.prototype.interfaces_ = function interfaces_ () {
109475           return []
109476         };
109477         LineBuilder.prototype.getClass = function getClass () {
109478           return LineBuilder
109479         };
109480
109481         var PointBuilder = function PointBuilder () {
109482           this._op = null;
109483           this._geometryFactory = null;
109484           this._resultPointList = new ArrayList();
109485           var op = arguments[0];
109486           var geometryFactory = arguments[1];
109487           // const ptLocator = arguments[2]
109488           this._op = op;
109489           this._geometryFactory = geometryFactory;
109490         };
109491         PointBuilder.prototype.filterCoveredNodeToPoint = function filterCoveredNodeToPoint (n) {
109492           var coord = n.getCoordinate();
109493           if (!this._op.isCoveredByLA(coord)) {
109494             var pt = this._geometryFactory.createPoint(coord);
109495             this._resultPointList.add(pt);
109496           }
109497         };
109498         PointBuilder.prototype.extractNonCoveredResultNodes = function extractNonCoveredResultNodes (opCode) {
109499             var this$1 = this;
109500
109501           for (var nodeit = this._op.getGraph().getNodes().iterator(); nodeit.hasNext();) {
109502             var n = nodeit.next();
109503             if (n.isInResult()) { continue }
109504             if (n.isIncidentEdgeInResult()) { continue }
109505             if (n.getEdges().getDegree() === 0 || opCode === OverlayOp.INTERSECTION) {
109506               var label = n.getLabel();
109507               if (OverlayOp.isResultOfOp(label, opCode)) {
109508                 this$1.filterCoveredNodeToPoint(n);
109509               }
109510             }
109511           }
109512         };
109513         PointBuilder.prototype.build = function build (opCode) {
109514           this.extractNonCoveredResultNodes(opCode);
109515           return this._resultPointList
109516         };
109517         PointBuilder.prototype.interfaces_ = function interfaces_ () {
109518           return []
109519         };
109520         PointBuilder.prototype.getClass = function getClass () {
109521           return PointBuilder
109522         };
109523
109524         var GeometryTransformer = function GeometryTransformer () {
109525           this._inputGeom = null;
109526           this._factory = null;
109527           this._pruneEmptyGeometry = true;
109528           this._preserveGeometryCollectionType = true;
109529           this._preserveCollections = false;
109530           this._preserveType = false;
109531         };
109532         GeometryTransformer.prototype.transformPoint = function transformPoint (geom, parent) {
109533           return this._factory.createPoint(this.transformCoordinates(geom.getCoordinateSequence(), geom))
109534         };
109535         GeometryTransformer.prototype.transformPolygon = function transformPolygon (geom, parent) {
109536             var this$1 = this;
109537
109538           var isAllValidLinearRings = true;
109539           var shell = this.transformLinearRing(geom.getExteriorRing(), geom);
109540           if (shell === null || !(shell instanceof LinearRing) || shell.isEmpty()) { isAllValidLinearRings = false; }
109541           var holes = new ArrayList();
109542           for (var i = 0; i < geom.getNumInteriorRing(); i++) {
109543             var hole = this$1.transformLinearRing(geom.getInteriorRingN(i), geom);
109544             if (hole === null || hole.isEmpty()) {
109545               continue
109546             }
109547             if (!(hole instanceof LinearRing)) { isAllValidLinearRings = false; }
109548             holes.add(hole);
109549           }
109550           if (isAllValidLinearRings) { return this._factory.createPolygon(shell, holes.toArray([])); } else {
109551             var components = new ArrayList();
109552             if (shell !== null) { components.add(shell); }
109553             components.addAll(holes);
109554             return this._factory.buildGeometry(components)
109555           }
109556         };
109557         GeometryTransformer.prototype.createCoordinateSequence = function createCoordinateSequence (coords) {
109558           return this._factory.getCoordinateSequenceFactory().create(coords)
109559         };
109560         GeometryTransformer.prototype.getInputGeometry = function getInputGeometry () {
109561           return this._inputGeom
109562         };
109563         GeometryTransformer.prototype.transformMultiLineString = function transformMultiLineString (geom, parent) {
109564             var this$1 = this;
109565
109566           var transGeomList = new ArrayList();
109567           for (var i = 0; i < geom.getNumGeometries(); i++) {
109568             var transformGeom = this$1.transformLineString(geom.getGeometryN(i), geom);
109569             if (transformGeom === null) { continue }
109570             if (transformGeom.isEmpty()) { continue }
109571             transGeomList.add(transformGeom);
109572           }
109573           return this._factory.buildGeometry(transGeomList)
109574         };
109575         GeometryTransformer.prototype.transformCoordinates = function transformCoordinates (coords, parent) {
109576           return this.copy(coords)
109577         };
109578         GeometryTransformer.prototype.transformLineString = function transformLineString (geom, parent) {
109579           return this._factory.createLineString(this.transformCoordinates(geom.getCoordinateSequence(), geom))
109580         };
109581         GeometryTransformer.prototype.transformMultiPoint = function transformMultiPoint (geom, parent) {
109582             var this$1 = this;
109583
109584           var transGeomList = new ArrayList();
109585           for (var i = 0; i < geom.getNumGeometries(); i++) {
109586             var transformGeom = this$1.transformPoint(geom.getGeometryN(i), geom);
109587             if (transformGeom === null) { continue }
109588             if (transformGeom.isEmpty()) { continue }
109589             transGeomList.add(transformGeom);
109590           }
109591           return this._factory.buildGeometry(transGeomList)
109592         };
109593         GeometryTransformer.prototype.transformMultiPolygon = function transformMultiPolygon (geom, parent) {
109594             var this$1 = this;
109595
109596           var transGeomList = new ArrayList();
109597           for (var i = 0; i < geom.getNumGeometries(); i++) {
109598             var transformGeom = this$1.transformPolygon(geom.getGeometryN(i), geom);
109599             if (transformGeom === null) { continue }
109600             if (transformGeom.isEmpty()) { continue }
109601             transGeomList.add(transformGeom);
109602           }
109603           return this._factory.buildGeometry(transGeomList)
109604         };
109605         GeometryTransformer.prototype.copy = function copy (seq) {
109606           return seq.copy()
109607         };
109608         GeometryTransformer.prototype.transformGeometryCollection = function transformGeometryCollection (geom, parent) {
109609             var this$1 = this;
109610
109611           var transGeomList = new ArrayList();
109612           for (var i = 0; i < geom.getNumGeometries(); i++) {
109613             var transformGeom = this$1.transform(geom.getGeometryN(i));
109614             if (transformGeom === null) { continue }
109615             if (this$1._pruneEmptyGeometry && transformGeom.isEmpty()) { continue }
109616             transGeomList.add(transformGeom);
109617           }
109618           if (this._preserveGeometryCollectionType) { return this._factory.createGeometryCollection(GeometryFactory.toGeometryArray(transGeomList)) }
109619           return this._factory.buildGeometry(transGeomList)
109620         };
109621         GeometryTransformer.prototype.transform = function transform (inputGeom) {
109622           this._inputGeom = inputGeom;
109623           this._factory = inputGeom.getFactory();
109624           if (inputGeom instanceof Point$1) { return this.transformPoint(inputGeom, null) }
109625           if (inputGeom instanceof MultiPoint) { return this.transformMultiPoint(inputGeom, null) }
109626           if (inputGeom instanceof LinearRing) { return this.transformLinearRing(inputGeom, null) }
109627           if (inputGeom instanceof LineString) { return this.transformLineString(inputGeom, null) }
109628           if (inputGeom instanceof MultiLineString) { return this.transformMultiLineString(inputGeom, null) }
109629           if (inputGeom instanceof Polygon) { return this.transformPolygon(inputGeom, null) }
109630           if (inputGeom instanceof MultiPolygon) { return this.transformMultiPolygon(inputGeom, null) }
109631           if (inputGeom instanceof GeometryCollection) { return this.transformGeometryCollection(inputGeom, null) }
109632           throw new IllegalArgumentException('Unknown Geometry subtype: ' + inputGeom.getClass().getName())
109633         };
109634         GeometryTransformer.prototype.transformLinearRing = function transformLinearRing (geom, parent) {
109635           var seq = this.transformCoordinates(geom.getCoordinateSequence(), geom);
109636           if (seq === null) { return this._factory.createLinearRing(null) }
109637           var seqSize = seq.size();
109638           if (seqSize > 0 && seqSize < 4 && !this._preserveType) { return this._factory.createLineString(seq) }
109639           return this._factory.createLinearRing(seq)
109640         };
109641         GeometryTransformer.prototype.interfaces_ = function interfaces_ () {
109642           return []
109643         };
109644         GeometryTransformer.prototype.getClass = function getClass () {
109645           return GeometryTransformer
109646         };
109647
109648         var LineStringSnapper = function LineStringSnapper () {
109649           this._snapTolerance = 0.0;
109650           this._srcPts = null;
109651           this._seg = new LineSegment();
109652           this._allowSnappingToSourceVertices = false;
109653           this._isClosed = false;
109654           if (arguments[0] instanceof LineString && typeof arguments[1] === 'number') {
109655             var srcLine = arguments[0];
109656             var snapTolerance = arguments[1];
109657             LineStringSnapper.call(this, srcLine.getCoordinates(), snapTolerance);
109658           } else if (arguments[0] instanceof Array && typeof arguments[1] === 'number') {
109659             var srcPts = arguments[0];
109660             var snapTolerance$1 = arguments[1];
109661             this._srcPts = srcPts;
109662             this._isClosed = LineStringSnapper.isClosed(srcPts);
109663             this._snapTolerance = snapTolerance$1;
109664           }
109665         };
109666         LineStringSnapper.prototype.snapVertices = function snapVertices (srcCoords, snapPts) {
109667             var this$1 = this;
109668
109669           var end = this._isClosed ? srcCoords.size() - 1 : srcCoords.size();
109670           for (var i = 0; i < end; i++) {
109671             var srcPt = srcCoords.get(i);
109672             var snapVert = this$1.findSnapForVertex(srcPt, snapPts);
109673             if (snapVert !== null) {
109674               srcCoords.set(i, new Coordinate(snapVert));
109675               if (i === 0 && this$1._isClosed) { srcCoords.set(srcCoords.size() - 1, new Coordinate(snapVert)); }
109676             }
109677           }
109678         };
109679         LineStringSnapper.prototype.findSnapForVertex = function findSnapForVertex (pt, snapPts) {
109680             var this$1 = this;
109681
109682           for (var i = 0; i < snapPts.length; i++) {
109683             if (pt.equals2D(snapPts[i])) { return null }
109684             if (pt.distance(snapPts[i]) < this$1._snapTolerance) { return snapPts[i] }
109685           }
109686           return null
109687         };
109688         LineStringSnapper.prototype.snapTo = function snapTo (snapPts) {
109689           var coordList = new CoordinateList(this._srcPts);
109690           this.snapVertices(coordList, snapPts);
109691           this.snapSegments(coordList, snapPts);
109692           var newPts = coordList.toCoordinateArray();
109693           return newPts
109694         };
109695         LineStringSnapper.prototype.snapSegments = function snapSegments (srcCoords, snapPts) {
109696             var this$1 = this;
109697
109698           if (snapPts.length === 0) { return null }
109699           var distinctPtCount = snapPts.length;
109700           if (snapPts[0].equals2D(snapPts[snapPts.length - 1])) { distinctPtCount = snapPts.length - 1; }
109701           for (var i = 0; i < distinctPtCount; i++) {
109702             var snapPt = snapPts[i];
109703             var index = this$1.findSegmentIndexToSnap(snapPt, srcCoords);
109704             if (index >= 0) {
109705               srcCoords.add(index + 1, new Coordinate(snapPt), false);
109706             }
109707           }
109708         };
109709         LineStringSnapper.prototype.findSegmentIndexToSnap = function findSegmentIndexToSnap (snapPt, srcCoords) {
109710             var this$1 = this;
109711
109712           var minDist = Double.MAX_VALUE;
109713           var snapIndex = -1;
109714           for (var i = 0; i < srcCoords.size() - 1; i++) {
109715             this$1._seg.p0 = srcCoords.get(i);
109716             this$1._seg.p1 = srcCoords.get(i + 1);
109717             if (this$1._seg.p0.equals2D(snapPt) || this$1._seg.p1.equals2D(snapPt)) {
109718               if (this$1._allowSnappingToSourceVertices) { continue; } else { return -1 }
109719             }
109720             var dist = this$1._seg.distance(snapPt);
109721             if (dist < this$1._snapTolerance && dist < minDist) {
109722               minDist = dist;
109723               snapIndex = i;
109724             }
109725           }
109726           return snapIndex
109727         };
109728         LineStringSnapper.prototype.setAllowSnappingToSourceVertices = function setAllowSnappingToSourceVertices (allowSnappingToSourceVertices) {
109729           this._allowSnappingToSourceVertices = allowSnappingToSourceVertices;
109730         };
109731         LineStringSnapper.prototype.interfaces_ = function interfaces_ () {
109732           return []
109733         };
109734         LineStringSnapper.prototype.getClass = function getClass () {
109735           return LineStringSnapper
109736         };
109737         LineStringSnapper.isClosed = function isClosed (pts) {
109738           if (pts.length <= 1) { return false }
109739           return pts[0].equals2D(pts[pts.length - 1])
109740         };
109741
109742         var GeometrySnapper = function GeometrySnapper (srcGeom) {
109743           this._srcGeom = srcGeom || null;
109744         };
109745
109746         var staticAccessors$41 = { SNAP_PRECISION_FACTOR: { configurable: true } };
109747         GeometrySnapper.prototype.snapTo = function snapTo (snapGeom, snapTolerance) {
109748           var snapPts = this.extractTargetCoordinates(snapGeom);
109749           var snapTrans = new SnapTransformer(snapTolerance, snapPts);
109750           return snapTrans.transform(this._srcGeom)
109751         };
109752         GeometrySnapper.prototype.snapToSelf = function snapToSelf (snapTolerance, cleanResult) {
109753           var snapPts = this.extractTargetCoordinates(this._srcGeom);
109754           var snapTrans = new SnapTransformer(snapTolerance, snapPts, true);
109755           var snappedGeom = snapTrans.transform(this._srcGeom);
109756           var result = snappedGeom;
109757           if (cleanResult && hasInterface(result, Polygonal)) {
109758             result = snappedGeom.buffer(0);
109759           }
109760           return result
109761         };
109762         GeometrySnapper.prototype.computeSnapTolerance = function computeSnapTolerance (ringPts) {
109763           var minSegLen = this.computeMinimumSegmentLength(ringPts);
109764           var snapTol = minSegLen / 10;
109765           return snapTol
109766         };
109767         GeometrySnapper.prototype.extractTargetCoordinates = function extractTargetCoordinates (g) {
109768           var ptSet = new TreeSet();
109769           var pts = g.getCoordinates();
109770           for (var i = 0; i < pts.length; i++) {
109771             ptSet.add(pts[i]);
109772           }
109773           return ptSet.toArray(new Array(0).fill(null))
109774         };
109775         GeometrySnapper.prototype.computeMinimumSegmentLength = function computeMinimumSegmentLength (pts) {
109776           var minSegLen = Double.MAX_VALUE;
109777           for (var i = 0; i < pts.length - 1; i++) {
109778             var segLen = pts[i].distance(pts[i + 1]);
109779             if (segLen < minSegLen) { minSegLen = segLen; }
109780           }
109781           return minSegLen
109782         };
109783         GeometrySnapper.prototype.interfaces_ = function interfaces_ () {
109784           return []
109785         };
109786         GeometrySnapper.prototype.getClass = function getClass () {
109787           return GeometrySnapper
109788         };
109789         GeometrySnapper.snap = function snap (g0, g1, snapTolerance) {
109790           var snapGeom = new Array(2).fill(null);
109791           var snapper0 = new GeometrySnapper(g0);
109792           snapGeom[0] = snapper0.snapTo(g1, snapTolerance);
109793           var snapper1 = new GeometrySnapper(g1);
109794           snapGeom[1] = snapper1.snapTo(snapGeom[0], snapTolerance);
109795           return snapGeom
109796         };
109797         GeometrySnapper.computeOverlaySnapTolerance = function computeOverlaySnapTolerance () {
109798           if (arguments.length === 1) {
109799             var g = arguments[0];
109800             var snapTolerance = GeometrySnapper.computeSizeBasedSnapTolerance(g);
109801             var pm = g.getPrecisionModel();
109802             if (pm.getType() === PrecisionModel.FIXED) {
109803               var fixedSnapTol = 1 / pm.getScale() * 2 / 1.415;
109804               if (fixedSnapTol > snapTolerance) { snapTolerance = fixedSnapTol; }
109805             }
109806             return snapTolerance
109807           } else if (arguments.length === 2) {
109808             var g0 = arguments[0];
109809             var g1 = arguments[1];
109810             return Math.min(GeometrySnapper.computeOverlaySnapTolerance(g0), GeometrySnapper.computeOverlaySnapTolerance(g1))
109811           }
109812         };
109813         GeometrySnapper.computeSizeBasedSnapTolerance = function computeSizeBasedSnapTolerance (g) {
109814           var env = g.getEnvelopeInternal();
109815           var minDimension = Math.min(env.getHeight(), env.getWidth());
109816           var snapTol = minDimension * GeometrySnapper.SNAP_PRECISION_FACTOR;
109817           return snapTol
109818         };
109819         GeometrySnapper.snapToSelf = function snapToSelf (geom, snapTolerance, cleanResult) {
109820           var snapper0 = new GeometrySnapper(geom);
109821           return snapper0.snapToSelf(snapTolerance, cleanResult)
109822         };
109823         staticAccessors$41.SNAP_PRECISION_FACTOR.get = function () { return 1e-9 };
109824
109825         Object.defineProperties( GeometrySnapper, staticAccessors$41 );
109826
109827         var SnapTransformer = (function (GeometryTransformer$$1) {
109828           function SnapTransformer (snapTolerance, snapPts, isSelfSnap) {
109829             GeometryTransformer$$1.call(this);
109830             this._snapTolerance = snapTolerance || null;
109831             this._snapPts = snapPts || null;
109832             this._isSelfSnap = (isSelfSnap !== undefined) ? isSelfSnap : false;
109833           }
109834
109835           if ( GeometryTransformer$$1 ) SnapTransformer.__proto__ = GeometryTransformer$$1;
109836           SnapTransformer.prototype = Object.create( GeometryTransformer$$1 && GeometryTransformer$$1.prototype );
109837           SnapTransformer.prototype.constructor = SnapTransformer;
109838           SnapTransformer.prototype.snapLine = function snapLine (srcPts, snapPts) {
109839             var snapper = new LineStringSnapper(srcPts, this._snapTolerance);
109840             snapper.setAllowSnappingToSourceVertices(this._isSelfSnap);
109841             return snapper.snapTo(snapPts)
109842           };
109843           SnapTransformer.prototype.transformCoordinates = function transformCoordinates (coords, parent) {
109844             var srcPts = coords.toCoordinateArray();
109845             var newPts = this.snapLine(srcPts, this._snapPts);
109846             return this._factory.getCoordinateSequenceFactory().create(newPts)
109847           };
109848           SnapTransformer.prototype.interfaces_ = function interfaces_ () {
109849             return []
109850           };
109851           SnapTransformer.prototype.getClass = function getClass () {
109852             return SnapTransformer
109853           };
109854
109855           return SnapTransformer;
109856         }(GeometryTransformer));
109857
109858         var CommonBits = function CommonBits () {
109859           this._isFirst = true;
109860           this._commonMantissaBitsCount = 53;
109861           this._commonBits = 0;
109862           this._commonSignExp = null;
109863         };
109864         CommonBits.prototype.getCommon = function getCommon () {
109865           return Double.longBitsToDouble(this._commonBits)
109866         };
109867         CommonBits.prototype.add = function add (num) {
109868           var numBits = Double.doubleToLongBits(num);
109869           if (this._isFirst) {
109870             this._commonBits = numBits;
109871             this._commonSignExp = CommonBits.signExpBits(this._commonBits);
109872             this._isFirst = false;
109873             return null
109874           }
109875           var numSignExp = CommonBits.signExpBits(numBits);
109876           if (numSignExp !== this._commonSignExp) {
109877             this._commonBits = 0;
109878             return null
109879           }
109880           this._commonMantissaBitsCount = CommonBits.numCommonMostSigMantissaBits(this._commonBits, numBits);
109881           this._commonBits = CommonBits.zeroLowerBits(this._commonBits, 64 - (12 + this._commonMantissaBitsCount));
109882         };
109883         CommonBits.prototype.toString = function toString () {
109884           if (arguments.length === 1) {
109885             var bits = arguments[0];
109886             var x = Double.longBitsToDouble(bits);
109887             var numStr = Double.toBinaryString(bits);
109888             var padStr = '0000000000000000000000000000000000000000000000000000000000000000' + numStr;
109889             var bitStr = padStr.substring(padStr.length - 64);
109890             var str = bitStr.substring(0, 1) + '  ' + bitStr.substring(1, 12) + '(exp) ' + bitStr.substring(12) + ' [ ' + x + ' ]';
109891             return str
109892           }
109893         };
109894         CommonBits.prototype.interfaces_ = function interfaces_ () {
109895           return []
109896         };
109897         CommonBits.prototype.getClass = function getClass () {
109898           return CommonBits
109899         };
109900         CommonBits.getBit = function getBit (bits, i) {
109901           var mask = 1 << i;
109902           return (bits & mask) !== 0 ? 1 : 0
109903         };
109904         CommonBits.signExpBits = function signExpBits (num) {
109905           return num >> 52
109906         };
109907         CommonBits.zeroLowerBits = function zeroLowerBits (bits, nBits) {
109908           var invMask = (1 << nBits) - 1;
109909           var mask = ~invMask;
109910           var zeroed = bits & mask;
109911           return zeroed
109912         };
109913         CommonBits.numCommonMostSigMantissaBits = function numCommonMostSigMantissaBits (num1, num2) {
109914           var count = 0;
109915           for (var i = 52; i >= 0; i--) {
109916             if (CommonBits.getBit(num1, i) !== CommonBits.getBit(num2, i)) { return count }
109917             count++;
109918           }
109919           return 52
109920         };
109921
109922         var CommonBitsRemover = function CommonBitsRemover () {
109923           this._commonCoord = null;
109924           this._ccFilter = new CommonCoordinateFilter();
109925         };
109926
109927         var staticAccessors$42 = { CommonCoordinateFilter: { configurable: true },Translater: { configurable: true } };
109928         CommonBitsRemover.prototype.addCommonBits = function addCommonBits (geom) {
109929           var trans = new Translater(this._commonCoord);
109930           geom.apply(trans);
109931           geom.geometryChanged();
109932         };
109933         CommonBitsRemover.prototype.removeCommonBits = function removeCommonBits (geom) {
109934           if (this._commonCoord.x === 0.0 && this._commonCoord.y === 0.0) { return geom }
109935           var invCoord = new Coordinate(this._commonCoord);
109936           invCoord.x = -invCoord.x;
109937           invCoord.y = -invCoord.y;
109938           var trans = new Translater(invCoord);
109939           geom.apply(trans);
109940           geom.geometryChanged();
109941           return geom
109942         };
109943         CommonBitsRemover.prototype.getCommonCoordinate = function getCommonCoordinate () {
109944           return this._commonCoord
109945         };
109946         CommonBitsRemover.prototype.add = function add (geom) {
109947           geom.apply(this._ccFilter);
109948           this._commonCoord = this._ccFilter.getCommonCoordinate();
109949         };
109950         CommonBitsRemover.prototype.interfaces_ = function interfaces_ () {
109951           return []
109952         };
109953         CommonBitsRemover.prototype.getClass = function getClass () {
109954           return CommonBitsRemover
109955         };
109956         staticAccessors$42.CommonCoordinateFilter.get = function () { return CommonCoordinateFilter };
109957         staticAccessors$42.Translater.get = function () { return Translater };
109958
109959         Object.defineProperties( CommonBitsRemover, staticAccessors$42 );
109960
109961         var CommonCoordinateFilter = function CommonCoordinateFilter () {
109962           this._commonBitsX = new CommonBits();
109963           this._commonBitsY = new CommonBits();
109964         };
109965         CommonCoordinateFilter.prototype.filter = function filter (coord) {
109966           this._commonBitsX.add(coord.x);
109967           this._commonBitsY.add(coord.y);
109968         };
109969         CommonCoordinateFilter.prototype.getCommonCoordinate = function getCommonCoordinate () {
109970           return new Coordinate(this._commonBitsX.getCommon(), this._commonBitsY.getCommon())
109971         };
109972         CommonCoordinateFilter.prototype.interfaces_ = function interfaces_ () {
109973           return [CoordinateFilter]
109974         };
109975         CommonCoordinateFilter.prototype.getClass = function getClass () {
109976           return CommonCoordinateFilter
109977         };
109978
109979         var Translater = function Translater () {
109980           this.trans = null;
109981           var trans = arguments[0];
109982           this.trans = trans;
109983         };
109984         Translater.prototype.filter = function filter (seq, i) {
109985           var xp = seq.getOrdinate(i, 0) + this.trans.x;
109986           var yp = seq.getOrdinate(i, 1) + this.trans.y;
109987           seq.setOrdinate(i, 0, xp);
109988           seq.setOrdinate(i, 1, yp);
109989         };
109990         Translater.prototype.isDone = function isDone () {
109991           return false
109992         };
109993         Translater.prototype.isGeometryChanged = function isGeometryChanged () {
109994           return true
109995         };
109996         Translater.prototype.interfaces_ = function interfaces_ () {
109997           return [CoordinateSequenceFilter]
109998         };
109999         Translater.prototype.getClass = function getClass () {
110000           return Translater
110001         };
110002
110003         var SnapOverlayOp = function SnapOverlayOp (g1, g2) {
110004           this._geom = new Array(2).fill(null);
110005           this._snapTolerance = null;
110006           this._cbr = null;
110007           this._geom[0] = g1;
110008           this._geom[1] = g2;
110009           this.computeSnapTolerance();
110010         };
110011         SnapOverlayOp.prototype.selfSnap = function selfSnap (geom) {
110012           var snapper0 = new GeometrySnapper(geom);
110013           var snapGeom = snapper0.snapTo(geom, this._snapTolerance);
110014           return snapGeom
110015         };
110016         SnapOverlayOp.prototype.removeCommonBits = function removeCommonBits (geom) {
110017           this._cbr = new CommonBitsRemover();
110018           this._cbr.add(geom[0]);
110019           this._cbr.add(geom[1]);
110020           var remGeom = new Array(2).fill(null);
110021           remGeom[0] = this._cbr.removeCommonBits(geom[0].copy());
110022           remGeom[1] = this._cbr.removeCommonBits(geom[1].copy());
110023           return remGeom
110024         };
110025         SnapOverlayOp.prototype.prepareResult = function prepareResult (geom) {
110026           this._cbr.addCommonBits(geom);
110027           return geom
110028         };
110029         SnapOverlayOp.prototype.getResultGeometry = function getResultGeometry (opCode) {
110030           var prepGeom = this.snap(this._geom);
110031           var result = OverlayOp.overlayOp(prepGeom[0], prepGeom[1], opCode);
110032           return this.prepareResult(result)
110033         };
110034         SnapOverlayOp.prototype.checkValid = function checkValid (g) {
110035           if (!g.isValid()) {
110036             System.out.println('Snapped geometry is invalid');
110037           }
110038         };
110039         SnapOverlayOp.prototype.computeSnapTolerance = function computeSnapTolerance () {
110040           this._snapTolerance = GeometrySnapper.computeOverlaySnapTolerance(this._geom[0], this._geom[1]);
110041         };
110042         SnapOverlayOp.prototype.snap = function snap (geom) {
110043           var remGeom = this.removeCommonBits(geom);
110044           var snapGeom = GeometrySnapper.snap(remGeom[0], remGeom[1], this._snapTolerance);
110045           return snapGeom
110046         };
110047         SnapOverlayOp.prototype.interfaces_ = function interfaces_ () {
110048           return []
110049         };
110050         SnapOverlayOp.prototype.getClass = function getClass () {
110051           return SnapOverlayOp
110052         };
110053         SnapOverlayOp.overlayOp = function overlayOp (g0, g1, opCode) {
110054           var op = new SnapOverlayOp(g0, g1);
110055           return op.getResultGeometry(opCode)
110056         };
110057         SnapOverlayOp.union = function union (g0, g1) {
110058           return SnapOverlayOp.overlayOp(g0, g1, OverlayOp.UNION)
110059         };
110060         SnapOverlayOp.intersection = function intersection (g0, g1) {
110061           return SnapOverlayOp.overlayOp(g0, g1, OverlayOp.INTERSECTION)
110062         };
110063         SnapOverlayOp.symDifference = function symDifference (g0, g1) {
110064           return SnapOverlayOp.overlayOp(g0, g1, OverlayOp.SYMDIFFERENCE)
110065         };
110066         SnapOverlayOp.difference = function difference (g0, g1) {
110067           return SnapOverlayOp.overlayOp(g0, g1, OverlayOp.DIFFERENCE)
110068         };
110069
110070         var SnapIfNeededOverlayOp = function SnapIfNeededOverlayOp (g1, g2) {
110071           this._geom = new Array(2).fill(null);
110072           this._geom[0] = g1;
110073           this._geom[1] = g2;
110074         };
110075         SnapIfNeededOverlayOp.prototype.getResultGeometry = function getResultGeometry (opCode) {
110076           var result = null;
110077           var isSuccess = false;
110078           var savedException = null;
110079           try {
110080             result = OverlayOp.overlayOp(this._geom[0], this._geom[1], opCode);
110081             var isValid = true;
110082             if (isValid) { isSuccess = true; }
110083           } catch (ex) {
110084             if (ex instanceof RuntimeException) {
110085               savedException = ex;
110086             } else { throw ex }
110087           } finally {}
110088           if (!isSuccess) {
110089             try {
110090               result = SnapOverlayOp.overlayOp(this._geom[0], this._geom[1], opCode);
110091             } catch (ex) {
110092               if (ex instanceof RuntimeException) {
110093                 throw savedException
110094               } else { throw ex }
110095             } finally {}
110096           }
110097           return result
110098         };
110099         SnapIfNeededOverlayOp.prototype.interfaces_ = function interfaces_ () {
110100           return []
110101         };
110102         SnapIfNeededOverlayOp.prototype.getClass = function getClass () {
110103           return SnapIfNeededOverlayOp
110104         };
110105         SnapIfNeededOverlayOp.overlayOp = function overlayOp (g0, g1, opCode) {
110106           var op = new SnapIfNeededOverlayOp(g0, g1);
110107           return op.getResultGeometry(opCode)
110108         };
110109         SnapIfNeededOverlayOp.union = function union (g0, g1) {
110110           return SnapIfNeededOverlayOp.overlayOp(g0, g1, OverlayOp.UNION)
110111         };
110112         SnapIfNeededOverlayOp.intersection = function intersection (g0, g1) {
110113           return SnapIfNeededOverlayOp.overlayOp(g0, g1, OverlayOp.INTERSECTION)
110114         };
110115         SnapIfNeededOverlayOp.symDifference = function symDifference (g0, g1) {
110116           return SnapIfNeededOverlayOp.overlayOp(g0, g1, OverlayOp.SYMDIFFERENCE)
110117         };
110118         SnapIfNeededOverlayOp.difference = function difference (g0, g1) {
110119           return SnapIfNeededOverlayOp.overlayOp(g0, g1, OverlayOp.DIFFERENCE)
110120         };
110121
110122         var MonotoneChain$2 = function MonotoneChain () {
110123           this.mce = null;
110124           this.chainIndex = null;
110125           var mce = arguments[0];
110126           var chainIndex = arguments[1];
110127           this.mce = mce;
110128           this.chainIndex = chainIndex;
110129         };
110130         MonotoneChain$2.prototype.computeIntersections = function computeIntersections (mc, si) {
110131           this.mce.computeIntersectsForChain(this.chainIndex, mc.mce, mc.chainIndex, si);
110132         };
110133         MonotoneChain$2.prototype.interfaces_ = function interfaces_ () {
110134           return []
110135         };
110136         MonotoneChain$2.prototype.getClass = function getClass () {
110137           return MonotoneChain$2
110138         };
110139
110140         var SweepLineEvent = function SweepLineEvent () {
110141           this._label = null;
110142           this._xValue = null;
110143           this._eventType = null;
110144           this._insertEvent = null;
110145           this._deleteEventIndex = null;
110146           this._obj = null;
110147           if (arguments.length === 2) {
110148             var x = arguments[0];
110149             var insertEvent = arguments[1];
110150             this._eventType = SweepLineEvent.DELETE;
110151             this._xValue = x;
110152             this._insertEvent = insertEvent;
110153           } else if (arguments.length === 3) {
110154             var label = arguments[0];
110155             var x$1 = arguments[1];
110156             var obj = arguments[2];
110157             this._eventType = SweepLineEvent.INSERT;
110158             this._label = label;
110159             this._xValue = x$1;
110160             this._obj = obj;
110161           }
110162         };
110163
110164         var staticAccessors$43 = { INSERT: { configurable: true },DELETE: { configurable: true } };
110165         SweepLineEvent.prototype.isDelete = function isDelete () {
110166           return this._eventType === SweepLineEvent.DELETE
110167         };
110168         SweepLineEvent.prototype.setDeleteEventIndex = function setDeleteEventIndex (deleteEventIndex) {
110169           this._deleteEventIndex = deleteEventIndex;
110170         };
110171         SweepLineEvent.prototype.getObject = function getObject () {
110172           return this._obj
110173         };
110174         SweepLineEvent.prototype.compareTo = function compareTo (o) {
110175           var pe = o;
110176           if (this._xValue < pe._xValue) { return -1 }
110177           if (this._xValue > pe._xValue) { return 1 }
110178           if (this._eventType < pe._eventType) { return -1 }
110179           if (this._eventType > pe._eventType) { return 1 }
110180           return 0
110181         };
110182         SweepLineEvent.prototype.getInsertEvent = function getInsertEvent () {
110183           return this._insertEvent
110184         };
110185         SweepLineEvent.prototype.isInsert = function isInsert () {
110186           return this._eventType === SweepLineEvent.INSERT
110187         };
110188         SweepLineEvent.prototype.isSameLabel = function isSameLabel (ev) {
110189           if (this._label === null) { return false }
110190           return this._label === ev._label
110191         };
110192         SweepLineEvent.prototype.getDeleteEventIndex = function getDeleteEventIndex () {
110193           return this._deleteEventIndex
110194         };
110195         SweepLineEvent.prototype.interfaces_ = function interfaces_ () {
110196           return [Comparable]
110197         };
110198         SweepLineEvent.prototype.getClass = function getClass () {
110199           return SweepLineEvent
110200         };
110201         staticAccessors$43.INSERT.get = function () { return 1 };
110202         staticAccessors$43.DELETE.get = function () { return 2 };
110203
110204         Object.defineProperties( SweepLineEvent, staticAccessors$43 );
110205
110206         var EdgeSetIntersector = function EdgeSetIntersector () {};
110207
110208         EdgeSetIntersector.prototype.interfaces_ = function interfaces_ () {
110209           return []
110210         };
110211         EdgeSetIntersector.prototype.getClass = function getClass () {
110212           return EdgeSetIntersector
110213         };
110214
110215         var SegmentIntersector$2 = function SegmentIntersector () {
110216           this._hasIntersection = false;
110217           this._hasProper = false;
110218           this._hasProperInterior = false;
110219           this._properIntersectionPoint = null;
110220           this._li = null;
110221           this._includeProper = null;
110222           this._recordIsolated = null;
110223           this._isSelfIntersection = null;
110224           this._numIntersections = 0;
110225           this.numTests = 0;
110226           this._bdyNodes = null;
110227           this._isDone = false;
110228           this._isDoneWhenProperInt = false;
110229           var li = arguments[0];
110230           var includeProper = arguments[1];
110231           var recordIsolated = arguments[2];
110232           this._li = li;
110233           this._includeProper = includeProper;
110234           this._recordIsolated = recordIsolated;
110235         };
110236         SegmentIntersector$2.prototype.isTrivialIntersection = function isTrivialIntersection (e0, segIndex0, e1, segIndex1) {
110237           if (e0 === e1) {
110238             if (this._li.getIntersectionNum() === 1) {
110239               if (SegmentIntersector$2.isAdjacentSegments(segIndex0, segIndex1)) { return true }
110240               if (e0.isClosed()) {
110241                 var maxSegIndex = e0.getNumPoints() - 1;
110242                 if ((segIndex0 === 0 && segIndex1 === maxSegIndex) ||
110243                     (segIndex1 === 0 && segIndex0 === maxSegIndex)) {
110244                   return true
110245                 }
110246               }
110247             }
110248           }
110249           return false
110250         };
110251         SegmentIntersector$2.prototype.getProperIntersectionPoint = function getProperIntersectionPoint () {
110252           return this._properIntersectionPoint
110253         };
110254         SegmentIntersector$2.prototype.setIsDoneIfProperInt = function setIsDoneIfProperInt (isDoneWhenProperInt) {
110255           this._isDoneWhenProperInt = isDoneWhenProperInt;
110256         };
110257         SegmentIntersector$2.prototype.hasProperInteriorIntersection = function hasProperInteriorIntersection () {
110258           return this._hasProperInterior
110259         };
110260         SegmentIntersector$2.prototype.isBoundaryPointInternal = function isBoundaryPointInternal (li, bdyNodes) {
110261           for (var i = bdyNodes.iterator(); i.hasNext();) {
110262             var node = i.next();
110263             var pt = node.getCoordinate();
110264             if (li.isIntersection(pt)) { return true }
110265           }
110266           return false
110267         };
110268         SegmentIntersector$2.prototype.hasProperIntersection = function hasProperIntersection () {
110269           return this._hasProper
110270         };
110271         SegmentIntersector$2.prototype.hasIntersection = function hasIntersection () {
110272           return this._hasIntersection
110273         };
110274         SegmentIntersector$2.prototype.isDone = function isDone () {
110275           return this._isDone
110276         };
110277         SegmentIntersector$2.prototype.isBoundaryPoint = function isBoundaryPoint (li, bdyNodes) {
110278           if (bdyNodes === null) { return false }
110279           if (this.isBoundaryPointInternal(li, bdyNodes[0])) { return true }
110280           if (this.isBoundaryPointInternal(li, bdyNodes[1])) { return true }
110281           return false
110282         };
110283         SegmentIntersector$2.prototype.setBoundaryNodes = function setBoundaryNodes (bdyNodes0, bdyNodes1) {
110284           this._bdyNodes = new Array(2).fill(null);
110285           this._bdyNodes[0] = bdyNodes0;
110286           this._bdyNodes[1] = bdyNodes1;
110287         };
110288         SegmentIntersector$2.prototype.addIntersections = function addIntersections (e0, segIndex0, e1, segIndex1) {
110289           if (e0 === e1 && segIndex0 === segIndex1) { return null }
110290           this.numTests++;
110291           var p00 = e0.getCoordinates()[segIndex0];
110292           var p01 = e0.getCoordinates()[segIndex0 + 1];
110293           var p10 = e1.getCoordinates()[segIndex1];
110294           var p11 = e1.getCoordinates()[segIndex1 + 1];
110295           this._li.computeIntersection(p00, p01, p10, p11);
110296           if (this._li.hasIntersection()) {
110297             if (this._recordIsolated) {
110298               e0.setIsolated(false);
110299               e1.setIsolated(false);
110300             }
110301             this._numIntersections++;
110302             if (!this.isTrivialIntersection(e0, segIndex0, e1, segIndex1)) {
110303               this._hasIntersection = true;
110304               if (this._includeProper || !this._li.isProper()) {
110305                 e0.addIntersections(this._li, segIndex0, 0);
110306                 e1.addIntersections(this._li, segIndex1, 1);
110307               }
110308               if (this._li.isProper()) {
110309                 this._properIntersectionPoint = this._li.getIntersection(0).copy();
110310                 this._hasProper = true;
110311                 if (this._isDoneWhenProperInt) {
110312                   this._isDone = true;
110313                 }
110314                 if (!this.isBoundaryPoint(this._li, this._bdyNodes)) { this._hasProperInterior = true; }
110315               }
110316             }
110317           }
110318         };
110319         SegmentIntersector$2.prototype.interfaces_ = function interfaces_ () {
110320           return []
110321         };
110322         SegmentIntersector$2.prototype.getClass = function getClass () {
110323           return SegmentIntersector$2
110324         };
110325         SegmentIntersector$2.isAdjacentSegments = function isAdjacentSegments (i1, i2) {
110326           return Math.abs(i1 - i2) === 1
110327         };
110328
110329         var SimpleMCSweepLineIntersector = (function (EdgeSetIntersector$$1) {
110330           function SimpleMCSweepLineIntersector () {
110331             EdgeSetIntersector$$1.call(this);
110332             this.events = new ArrayList();
110333             this.nOverlaps = null;
110334           }
110335
110336           if ( EdgeSetIntersector$$1 ) SimpleMCSweepLineIntersector.__proto__ = EdgeSetIntersector$$1;
110337           SimpleMCSweepLineIntersector.prototype = Object.create( EdgeSetIntersector$$1 && EdgeSetIntersector$$1.prototype );
110338           SimpleMCSweepLineIntersector.prototype.constructor = SimpleMCSweepLineIntersector;
110339           SimpleMCSweepLineIntersector.prototype.prepareEvents = function prepareEvents () {
110340             var this$1 = this;
110341
110342             Collections.sort(this.events);
110343             for (var i = 0; i < this.events.size(); i++) {
110344               var ev = this$1.events.get(i);
110345               if (ev.isDelete()) {
110346                 ev.getInsertEvent().setDeleteEventIndex(i);
110347               }
110348             }
110349           };
110350           SimpleMCSweepLineIntersector.prototype.computeIntersections = function computeIntersections () {
110351             var this$1 = this;
110352
110353             if (arguments.length === 1) {
110354               var si = arguments[0];
110355               this.nOverlaps = 0;
110356               this.prepareEvents();
110357               for (var i = 0; i < this.events.size(); i++) {
110358                 var ev = this$1.events.get(i);
110359                 if (ev.isInsert()) {
110360                   this$1.processOverlaps(i, ev.getDeleteEventIndex(), ev, si);
110361                 }
110362                 if (si.isDone()) {
110363                   break
110364                 }
110365               }
110366             } else if (arguments.length === 3) {
110367               if (arguments[2] instanceof SegmentIntersector$2 && (hasInterface(arguments[0], List) && hasInterface(arguments[1], List))) {
110368                 var edges0 = arguments[0];
110369                 var edges1 = arguments[1];
110370                 var si$1 = arguments[2];
110371                 this.addEdges(edges0, edges0);
110372                 this.addEdges(edges1, edges1);
110373                 this.computeIntersections(si$1);
110374               } else if (typeof arguments[2] === 'boolean' && (hasInterface(arguments[0], List) && arguments[1] instanceof SegmentIntersector$2)) {
110375                 var edges = arguments[0];
110376                 var si$2 = arguments[1];
110377                 var testAllSegments = arguments[2];
110378                 if (testAllSegments) { this.addEdges(edges, null); } else { this.addEdges(edges); }
110379                 this.computeIntersections(si$2);
110380               }
110381             }
110382           };
110383           SimpleMCSweepLineIntersector.prototype.addEdge = function addEdge (edge, edgeSet) {
110384             var this$1 = this;
110385
110386             var mce = edge.getMonotoneChainEdge();
110387             var startIndex = mce.getStartIndexes();
110388             for (var i = 0; i < startIndex.length - 1; i++) {
110389               var mc = new MonotoneChain$2(mce, i);
110390               var insertEvent = new SweepLineEvent(edgeSet, mce.getMinX(i), mc);
110391               this$1.events.add(insertEvent);
110392               this$1.events.add(new SweepLineEvent(mce.getMaxX(i), insertEvent));
110393             }
110394           };
110395           SimpleMCSweepLineIntersector.prototype.processOverlaps = function processOverlaps (start, end, ev0, si) {
110396             var this$1 = this;
110397
110398             var mc0 = ev0.getObject();
110399             for (var i = start; i < end; i++) {
110400               var ev1 = this$1.events.get(i);
110401               if (ev1.isInsert()) {
110402                 var mc1 = ev1.getObject();
110403                 if (!ev0.isSameLabel(ev1)) {
110404                   mc0.computeIntersections(mc1, si);
110405                   this$1.nOverlaps++;
110406                 }
110407               }
110408             }
110409           };
110410           SimpleMCSweepLineIntersector.prototype.addEdges = function addEdges () {
110411             var this$1 = this;
110412
110413             if (arguments.length === 1) {
110414               var edges = arguments[0];
110415               for (var i = edges.iterator(); i.hasNext();) {
110416                 var edge = i.next();
110417                 this$1.addEdge(edge, edge);
110418               }
110419             } else if (arguments.length === 2) {
110420               var edges$1 = arguments[0];
110421               var edgeSet = arguments[1];
110422               for (var i$1 = edges$1.iterator(); i$1.hasNext();) {
110423                 var edge$1 = i$1.next();
110424                 this$1.addEdge(edge$1, edgeSet);
110425               }
110426             }
110427           };
110428           SimpleMCSweepLineIntersector.prototype.interfaces_ = function interfaces_ () {
110429             return []
110430           };
110431           SimpleMCSweepLineIntersector.prototype.getClass = function getClass () {
110432             return SimpleMCSweepLineIntersector
110433           };
110434
110435           return SimpleMCSweepLineIntersector;
110436         }(EdgeSetIntersector));
110437
110438         var IntervalRTreeNode = function IntervalRTreeNode () {
110439           this._min = Double.POSITIVE_INFINITY;
110440           this._max = Double.NEGATIVE_INFINITY;
110441         };
110442
110443         var staticAccessors$45 = { NodeComparator: { configurable: true } };
110444         IntervalRTreeNode.prototype.getMin = function getMin () {
110445           return this._min
110446         };
110447         IntervalRTreeNode.prototype.intersects = function intersects (queryMin, queryMax) {
110448           if (this._min > queryMax || this._max < queryMin) { return false }
110449           return true
110450         };
110451         IntervalRTreeNode.prototype.getMax = function getMax () {
110452           return this._max
110453         };
110454         IntervalRTreeNode.prototype.toString = function toString () {
110455           return WKTWriter.toLineString(new Coordinate(this._min, 0), new Coordinate(this._max, 0))
110456         };
110457         IntervalRTreeNode.prototype.interfaces_ = function interfaces_ () {
110458           return []
110459         };
110460         IntervalRTreeNode.prototype.getClass = function getClass () {
110461           return IntervalRTreeNode
110462         };
110463         staticAccessors$45.NodeComparator.get = function () { return NodeComparator };
110464
110465         Object.defineProperties( IntervalRTreeNode, staticAccessors$45 );
110466
110467         var NodeComparator = function NodeComparator () {};
110468
110469         NodeComparator.prototype.compare = function compare (o1, o2) {
110470           var n1 = o1;
110471           var n2 = o2;
110472           var mid1 = (n1._min + n1._max) / 2;
110473           var mid2 = (n2._min + n2._max) / 2;
110474           if (mid1 < mid2) { return -1 }
110475           if (mid1 > mid2) { return 1 }
110476           return 0
110477         };
110478         NodeComparator.prototype.interfaces_ = function interfaces_ () {
110479           return [Comparator]
110480         };
110481         NodeComparator.prototype.getClass = function getClass () {
110482           return NodeComparator
110483         };
110484
110485         var IntervalRTreeLeafNode = (function (IntervalRTreeNode$$1) {
110486           function IntervalRTreeLeafNode () {
110487             IntervalRTreeNode$$1.call(this);
110488             this._item = null;
110489             var min = arguments[0];
110490             var max = arguments[1];
110491             var item = arguments[2];
110492             this._min = min;
110493             this._max = max;
110494             this._item = item;
110495           }
110496
110497           if ( IntervalRTreeNode$$1 ) IntervalRTreeLeafNode.__proto__ = IntervalRTreeNode$$1;
110498           IntervalRTreeLeafNode.prototype = Object.create( IntervalRTreeNode$$1 && IntervalRTreeNode$$1.prototype );
110499           IntervalRTreeLeafNode.prototype.constructor = IntervalRTreeLeafNode;
110500           IntervalRTreeLeafNode.prototype.query = function query (queryMin, queryMax, visitor) {
110501             if (!this.intersects(queryMin, queryMax)) { return null }
110502             visitor.visitItem(this._item);
110503           };
110504           IntervalRTreeLeafNode.prototype.interfaces_ = function interfaces_ () {
110505             return []
110506           };
110507           IntervalRTreeLeafNode.prototype.getClass = function getClass () {
110508             return IntervalRTreeLeafNode
110509           };
110510
110511           return IntervalRTreeLeafNode;
110512         }(IntervalRTreeNode));
110513
110514         var IntervalRTreeBranchNode = (function (IntervalRTreeNode$$1) {
110515           function IntervalRTreeBranchNode () {
110516             IntervalRTreeNode$$1.call(this);
110517             this._node1 = null;
110518             this._node2 = null;
110519             var n1 = arguments[0];
110520             var n2 = arguments[1];
110521             this._node1 = n1;
110522             this._node2 = n2;
110523             this.buildExtent(this._node1, this._node2);
110524           }
110525
110526           if ( IntervalRTreeNode$$1 ) IntervalRTreeBranchNode.__proto__ = IntervalRTreeNode$$1;
110527           IntervalRTreeBranchNode.prototype = Object.create( IntervalRTreeNode$$1 && IntervalRTreeNode$$1.prototype );
110528           IntervalRTreeBranchNode.prototype.constructor = IntervalRTreeBranchNode;
110529           IntervalRTreeBranchNode.prototype.buildExtent = function buildExtent (n1, n2) {
110530             this._min = Math.min(n1._min, n2._min);
110531             this._max = Math.max(n1._max, n2._max);
110532           };
110533           IntervalRTreeBranchNode.prototype.query = function query (queryMin, queryMax, visitor) {
110534             if (!this.intersects(queryMin, queryMax)) {
110535               return null
110536             }
110537             if (this._node1 !== null) { this._node1.query(queryMin, queryMax, visitor); }
110538             if (this._node2 !== null) { this._node2.query(queryMin, queryMax, visitor); }
110539           };
110540           IntervalRTreeBranchNode.prototype.interfaces_ = function interfaces_ () {
110541             return []
110542           };
110543           IntervalRTreeBranchNode.prototype.getClass = function getClass () {
110544             return IntervalRTreeBranchNode
110545           };
110546
110547           return IntervalRTreeBranchNode;
110548         }(IntervalRTreeNode));
110549
110550         var SortedPackedIntervalRTree = function SortedPackedIntervalRTree () {
110551           this._leaves = new ArrayList();
110552           this._root = null;
110553           this._level = 0;
110554         };
110555         SortedPackedIntervalRTree.prototype.buildTree = function buildTree () {
110556             var this$1 = this;
110557
110558           Collections.sort(this._leaves, new IntervalRTreeNode.NodeComparator());
110559           var src = this._leaves;
110560           var temp = null;
110561           var dest = new ArrayList();
110562           while (true) {
110563             this$1.buildLevel(src, dest);
110564             if (dest.size() === 1) { return dest.get(0) }
110565             temp = src;
110566             src = dest;
110567             dest = temp;
110568           }
110569         };
110570         SortedPackedIntervalRTree.prototype.insert = function insert (min, max, item) {
110571           if (this._root !== null) { throw new Error('Index cannot be added to once it has been queried') }
110572           this._leaves.add(new IntervalRTreeLeafNode(min, max, item));
110573         };
110574         SortedPackedIntervalRTree.prototype.query = function query (min, max, visitor) {
110575           this.init();
110576           this._root.query(min, max, visitor);
110577         };
110578         SortedPackedIntervalRTree.prototype.buildRoot = function buildRoot () {
110579           if (this._root !== null) { return null }
110580           this._root = this.buildTree();
110581         };
110582         SortedPackedIntervalRTree.prototype.printNode = function printNode (node) {
110583           System.out.println(WKTWriter.toLineString(new Coordinate(node._min, this._level), new Coordinate(node._max, this._level)));
110584         };
110585         SortedPackedIntervalRTree.prototype.init = function init () {
110586           if (this._root !== null) { return null }
110587           this.buildRoot();
110588         };
110589         SortedPackedIntervalRTree.prototype.buildLevel = function buildLevel (src, dest) {
110590           this._level++;
110591           dest.clear();
110592           for (var i = 0; i < src.size(); i += 2) {
110593             var n1 = src.get(i);
110594             var n2 = i + 1 < src.size() ? src.get(i) : null;
110595             if (n2 === null) {
110596               dest.add(n1);
110597             } else {
110598               var node = new IntervalRTreeBranchNode(src.get(i), src.get(i + 1));
110599               dest.add(node);
110600             }
110601           }
110602         };
110603         SortedPackedIntervalRTree.prototype.interfaces_ = function interfaces_ () {
110604           return []
110605         };
110606         SortedPackedIntervalRTree.prototype.getClass = function getClass () {
110607           return SortedPackedIntervalRTree
110608         };
110609
110610         var ArrayListVisitor = function ArrayListVisitor () {
110611           this._items = new ArrayList();
110612         };
110613         ArrayListVisitor.prototype.visitItem = function visitItem (item) {
110614           this._items.add(item);
110615         };
110616         ArrayListVisitor.prototype.getItems = function getItems () {
110617           return this._items
110618         };
110619         ArrayListVisitor.prototype.interfaces_ = function interfaces_ () {
110620           return [ItemVisitor]
110621         };
110622         ArrayListVisitor.prototype.getClass = function getClass () {
110623           return ArrayListVisitor
110624         };
110625
110626         var IndexedPointInAreaLocator = function IndexedPointInAreaLocator () {
110627           this._index = null;
110628           var g = arguments[0];
110629           if (!hasInterface(g, Polygonal)) { throw new IllegalArgumentException('Argument must be Polygonal') }
110630           this._index = new IntervalIndexedGeometry(g);
110631         };
110632
110633         var staticAccessors$44 = { SegmentVisitor: { configurable: true },IntervalIndexedGeometry: { configurable: true } };
110634         IndexedPointInAreaLocator.prototype.locate = function locate (p) {
110635           var rcc = new RayCrossingCounter(p);
110636           var visitor = new SegmentVisitor(rcc);
110637           this._index.query(p.y, p.y, visitor);
110638           return rcc.getLocation()
110639         };
110640         IndexedPointInAreaLocator.prototype.interfaces_ = function interfaces_ () {
110641           return [PointOnGeometryLocator]
110642         };
110643         IndexedPointInAreaLocator.prototype.getClass = function getClass () {
110644           return IndexedPointInAreaLocator
110645         };
110646         staticAccessors$44.SegmentVisitor.get = function () { return SegmentVisitor };
110647         staticAccessors$44.IntervalIndexedGeometry.get = function () { return IntervalIndexedGeometry };
110648
110649         Object.defineProperties( IndexedPointInAreaLocator, staticAccessors$44 );
110650
110651         var SegmentVisitor = function SegmentVisitor () {
110652           this._counter = null;
110653           var counter = arguments[0];
110654           this._counter = counter;
110655         };
110656         SegmentVisitor.prototype.visitItem = function visitItem (item) {
110657           var seg = item;
110658           this._counter.countSegment(seg.getCoordinate(0), seg.getCoordinate(1));
110659         };
110660         SegmentVisitor.prototype.interfaces_ = function interfaces_ () {
110661           return [ItemVisitor]
110662         };
110663         SegmentVisitor.prototype.getClass = function getClass () {
110664           return SegmentVisitor
110665         };
110666
110667         var IntervalIndexedGeometry = function IntervalIndexedGeometry () {
110668           this._index = new SortedPackedIntervalRTree();
110669           var geom = arguments[0];
110670           this.init(geom);
110671         };
110672         IntervalIndexedGeometry.prototype.init = function init (geom) {
110673             var this$1 = this;
110674
110675           var lines = LinearComponentExtracter.getLines(geom);
110676           for (var i = lines.iterator(); i.hasNext();) {
110677             var line = i.next();
110678             var pts = line.getCoordinates();
110679             this$1.addLine(pts);
110680           }
110681         };
110682         IntervalIndexedGeometry.prototype.addLine = function addLine (pts) {
110683             var this$1 = this;
110684
110685           for (var i = 1; i < pts.length; i++) {
110686             var seg = new LineSegment(pts[i - 1], pts[i]);
110687             var min = Math.min(seg.p0.y, seg.p1.y);
110688             var max = Math.max(seg.p0.y, seg.p1.y);
110689             this$1._index.insert(min, max, seg);
110690           }
110691         };
110692         IntervalIndexedGeometry.prototype.query = function query () {
110693           if (arguments.length === 2) {
110694             var min = arguments[0];
110695             var max = arguments[1];
110696             var visitor = new ArrayListVisitor();
110697             this._index.query(min, max, visitor);
110698             return visitor.getItems()
110699           } else if (arguments.length === 3) {
110700             var min$1 = arguments[0];
110701             var max$1 = arguments[1];
110702             var visitor$1 = arguments[2];
110703             this._index.query(min$1, max$1, visitor$1);
110704           }
110705         };
110706         IntervalIndexedGeometry.prototype.interfaces_ = function interfaces_ () {
110707           return []
110708         };
110709         IntervalIndexedGeometry.prototype.getClass = function getClass () {
110710           return IntervalIndexedGeometry
110711         };
110712
110713         var GeometryGraph = (function (PlanarGraph$$1) {
110714           function GeometryGraph () {
110715             PlanarGraph$$1.call(this);
110716             this._parentGeom = null;
110717             this._lineEdgeMap = new HashMap();
110718             this._boundaryNodeRule = null;
110719             this._useBoundaryDeterminationRule = true;
110720             this._argIndex = null;
110721             this._boundaryNodes = null;
110722             this._hasTooFewPoints = false;
110723             this._invalidPoint = null;
110724             this._areaPtLocator = null;
110725             this._ptLocator = new PointLocator();
110726             if (arguments.length === 2) {
110727               var argIndex = arguments[0];
110728               var parentGeom = arguments[1];
110729               var boundaryNodeRule = BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE;
110730               this._argIndex = argIndex;
110731               this._parentGeom = parentGeom;
110732               this._boundaryNodeRule = boundaryNodeRule;
110733               if (parentGeom !== null) {
110734                 this.add(parentGeom);
110735               }
110736             } else if (arguments.length === 3) {
110737               var argIndex$1 = arguments[0];
110738               var parentGeom$1 = arguments[1];
110739               var boundaryNodeRule$1 = arguments[2];
110740               this._argIndex = argIndex$1;
110741               this._parentGeom = parentGeom$1;
110742               this._boundaryNodeRule = boundaryNodeRule$1;
110743               if (parentGeom$1 !== null) {
110744                 this.add(parentGeom$1);
110745               }
110746             }
110747           }
110748
110749           if ( PlanarGraph$$1 ) GeometryGraph.__proto__ = PlanarGraph$$1;
110750           GeometryGraph.prototype = Object.create( PlanarGraph$$1 && PlanarGraph$$1.prototype );
110751           GeometryGraph.prototype.constructor = GeometryGraph;
110752           GeometryGraph.prototype.insertBoundaryPoint = function insertBoundaryPoint (argIndex, coord) {
110753             var n = this._nodes.addNode(coord);
110754             var lbl = n.getLabel();
110755             var boundaryCount = 1;
110756             var loc = Location.NONE;
110757             loc = lbl.getLocation(argIndex, Position.ON);
110758             if (loc === Location.BOUNDARY) { boundaryCount++; }
110759             var newLoc = GeometryGraph.determineBoundary(this._boundaryNodeRule, boundaryCount);
110760             lbl.setLocation(argIndex, newLoc);
110761           };
110762           GeometryGraph.prototype.computeSelfNodes = function computeSelfNodes () {
110763             if (arguments.length === 2) {
110764               var li = arguments[0];
110765               var computeRingSelfNodes = arguments[1];
110766               return this.computeSelfNodes(li, computeRingSelfNodes, false)
110767             } else if (arguments.length === 3) {
110768               var li$1 = arguments[0];
110769               var computeRingSelfNodes$1 = arguments[1];
110770               var isDoneIfProperInt = arguments[2];
110771               var si = new SegmentIntersector$2(li$1, true, false);
110772               si.setIsDoneIfProperInt(isDoneIfProperInt);
110773               var esi = this.createEdgeSetIntersector();
110774               var isRings = this._parentGeom instanceof LinearRing || this._parentGeom instanceof Polygon || this._parentGeom instanceof MultiPolygon;
110775               var computeAllSegments = computeRingSelfNodes$1 || !isRings;
110776               esi.computeIntersections(this._edges, si, computeAllSegments);
110777               this.addSelfIntersectionNodes(this._argIndex);
110778               return si
110779             }
110780           };
110781           GeometryGraph.prototype.computeSplitEdges = function computeSplitEdges (edgelist) {
110782             for (var i = this._edges.iterator(); i.hasNext();) {
110783               var e = i.next();
110784               e.eiList.addSplitEdges(edgelist);
110785             }
110786           };
110787           GeometryGraph.prototype.computeEdgeIntersections = function computeEdgeIntersections (g, li, includeProper) {
110788             var si = new SegmentIntersector$2(li, includeProper, true);
110789             si.setBoundaryNodes(this.getBoundaryNodes(), g.getBoundaryNodes());
110790             var esi = this.createEdgeSetIntersector();
110791             esi.computeIntersections(this._edges, g._edges, si);
110792             return si
110793           };
110794           GeometryGraph.prototype.getGeometry = function getGeometry () {
110795             return this._parentGeom
110796           };
110797           GeometryGraph.prototype.getBoundaryNodeRule = function getBoundaryNodeRule () {
110798             return this._boundaryNodeRule
110799           };
110800           GeometryGraph.prototype.hasTooFewPoints = function hasTooFewPoints () {
110801             return this._hasTooFewPoints
110802           };
110803           GeometryGraph.prototype.addPoint = function addPoint () {
110804             if (arguments[0] instanceof Point$1) {
110805               var p = arguments[0];
110806               var coord = p.getCoordinate();
110807               this.insertPoint(this._argIndex, coord, Location.INTERIOR);
110808             } else if (arguments[0] instanceof Coordinate) {
110809               var pt = arguments[0];
110810               this.insertPoint(this._argIndex, pt, Location.INTERIOR);
110811             }
110812           };
110813           GeometryGraph.prototype.addPolygon = function addPolygon (p) {
110814             var this$1 = this;
110815
110816             this.addPolygonRing(p.getExteriorRing(), Location.EXTERIOR, Location.INTERIOR);
110817             for (var i = 0; i < p.getNumInteriorRing(); i++) {
110818               var hole = p.getInteriorRingN(i);
110819               this$1.addPolygonRing(hole, Location.INTERIOR, Location.EXTERIOR);
110820             }
110821           };
110822           GeometryGraph.prototype.addEdge = function addEdge (e) {
110823             this.insertEdge(e);
110824             var coord = e.getCoordinates();
110825             this.insertPoint(this._argIndex, coord[0], Location.BOUNDARY);
110826             this.insertPoint(this._argIndex, coord[coord.length - 1], Location.BOUNDARY);
110827           };
110828           GeometryGraph.prototype.addLineString = function addLineString (line) {
110829             var coord = CoordinateArrays.removeRepeatedPoints(line.getCoordinates());
110830             if (coord.length < 2) {
110831               this._hasTooFewPoints = true;
110832               this._invalidPoint = coord[0];
110833               return null
110834             }
110835             var e = new Edge(coord, new Label(this._argIndex, Location.INTERIOR));
110836             this._lineEdgeMap.put(line, e);
110837             this.insertEdge(e);
110838             Assert.isTrue(coord.length >= 2, 'found LineString with single point');
110839             this.insertBoundaryPoint(this._argIndex, coord[0]);
110840             this.insertBoundaryPoint(this._argIndex, coord[coord.length - 1]);
110841           };
110842           GeometryGraph.prototype.getInvalidPoint = function getInvalidPoint () {
110843             return this._invalidPoint
110844           };
110845           GeometryGraph.prototype.getBoundaryPoints = function getBoundaryPoints () {
110846             var coll = this.getBoundaryNodes();
110847             var pts = new Array(coll.size()).fill(null);
110848             var i = 0;
110849             for (var it = coll.iterator(); it.hasNext();) {
110850               var node = it.next();
110851               pts[i++] = node.getCoordinate().copy();
110852             }
110853             return pts
110854           };
110855           GeometryGraph.prototype.getBoundaryNodes = function getBoundaryNodes () {
110856             if (this._boundaryNodes === null) { this._boundaryNodes = this._nodes.getBoundaryNodes(this._argIndex); }
110857             return this._boundaryNodes
110858           };
110859           GeometryGraph.prototype.addSelfIntersectionNode = function addSelfIntersectionNode (argIndex, coord, loc) {
110860             if (this.isBoundaryNode(argIndex, coord)) { return null }
110861             if (loc === Location.BOUNDARY && this._useBoundaryDeterminationRule) { this.insertBoundaryPoint(argIndex, coord); } else { this.insertPoint(argIndex, coord, loc); }
110862           };
110863           GeometryGraph.prototype.addPolygonRing = function addPolygonRing (lr, cwLeft, cwRight) {
110864             if (lr.isEmpty()) { return null }
110865             var coord = CoordinateArrays.removeRepeatedPoints(lr.getCoordinates());
110866             if (coord.length < 4) {
110867               this._hasTooFewPoints = true;
110868               this._invalidPoint = coord[0];
110869               return null
110870             }
110871             var left = cwLeft;
110872             var right = cwRight;
110873             if (CGAlgorithms.isCCW(coord)) {
110874               left = cwRight;
110875               right = cwLeft;
110876             }
110877             var e = new Edge(coord, new Label(this._argIndex, Location.BOUNDARY, left, right));
110878             this._lineEdgeMap.put(lr, e);
110879             this.insertEdge(e);
110880             this.insertPoint(this._argIndex, coord[0], Location.BOUNDARY);
110881           };
110882           GeometryGraph.prototype.insertPoint = function insertPoint (argIndex, coord, onLocation) {
110883             var n = this._nodes.addNode(coord);
110884             var lbl = n.getLabel();
110885             if (lbl === null) {
110886               n._label = new Label(argIndex, onLocation);
110887             } else { lbl.setLocation(argIndex, onLocation); }
110888           };
110889           GeometryGraph.prototype.createEdgeSetIntersector = function createEdgeSetIntersector () {
110890             return new SimpleMCSweepLineIntersector()
110891           };
110892           GeometryGraph.prototype.addSelfIntersectionNodes = function addSelfIntersectionNodes (argIndex) {
110893             var this$1 = this;
110894
110895             for (var i = this._edges.iterator(); i.hasNext();) {
110896               var e = i.next();
110897               var eLoc = e.getLabel().getLocation(argIndex);
110898               for (var eiIt = e.eiList.iterator(); eiIt.hasNext();) {
110899                 var ei = eiIt.next();
110900                 this$1.addSelfIntersectionNode(argIndex, ei.coord, eLoc);
110901               }
110902             }
110903           };
110904           GeometryGraph.prototype.add = function add () {
110905             if (arguments.length === 1) {
110906               var g = arguments[0];
110907               if (g.isEmpty()) { return null }
110908               if (g instanceof MultiPolygon) { this._useBoundaryDeterminationRule = false; }
110909               if (g instanceof Polygon) { this.addPolygon(g); }
110910               else if (g instanceof LineString) { this.addLineString(g); }
110911               else if (g instanceof Point$1) { this.addPoint(g); }
110912               else if (g instanceof MultiPoint) { this.addCollection(g); }
110913               else if (g instanceof MultiLineString) { this.addCollection(g); }
110914               else if (g instanceof MultiPolygon) { this.addCollection(g); }
110915               else if (g instanceof GeometryCollection) { this.addCollection(g); }
110916               else { throw new Error(g.getClass().getName()) }
110917             } else { return PlanarGraph$$1.prototype.add.apply(this, arguments) }
110918           };
110919           GeometryGraph.prototype.addCollection = function addCollection (gc) {
110920             var this$1 = this;
110921
110922             for (var i = 0; i < gc.getNumGeometries(); i++) {
110923               var g = gc.getGeometryN(i);
110924               this$1.add(g);
110925             }
110926           };
110927           GeometryGraph.prototype.locate = function locate (pt) {
110928             if (hasInterface(this._parentGeom, Polygonal) && this._parentGeom.getNumGeometries() > 50) {
110929               if (this._areaPtLocator === null) {
110930                 this._areaPtLocator = new IndexedPointInAreaLocator(this._parentGeom);
110931               }
110932               return this._areaPtLocator.locate(pt)
110933             }
110934             return this._ptLocator.locate(pt, this._parentGeom)
110935           };
110936           GeometryGraph.prototype.findEdge = function findEdge () {
110937             if (arguments.length === 1) {
110938               var line = arguments[0];
110939               return this._lineEdgeMap.get(line)
110940             } else { return PlanarGraph$$1.prototype.findEdge.apply(this, arguments) }
110941           };
110942           GeometryGraph.prototype.interfaces_ = function interfaces_ () {
110943             return []
110944           };
110945           GeometryGraph.prototype.getClass = function getClass () {
110946             return GeometryGraph
110947           };
110948           GeometryGraph.determineBoundary = function determineBoundary (boundaryNodeRule, boundaryCount) {
110949             return boundaryNodeRule.isInBoundary(boundaryCount) ? Location.BOUNDARY : Location.INTERIOR
110950           };
110951
110952           return GeometryGraph;
110953         }(PlanarGraph));
110954
110955         var GeometryGraphOp = function GeometryGraphOp () {
110956           this._li = new RobustLineIntersector();
110957           this._resultPrecisionModel = null;
110958           this._arg = null;
110959           if (arguments.length === 1) {
110960             var g0 = arguments[0];
110961             this.setComputationPrecision(g0.getPrecisionModel());
110962             this._arg = new Array(1).fill(null);
110963             this._arg[0] = new GeometryGraph(0, g0);
110964           } else if (arguments.length === 2) {
110965             var g0$1 = arguments[0];
110966             var g1 = arguments[1];
110967             var boundaryNodeRule = BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE;
110968             if (g0$1.getPrecisionModel().compareTo(g1.getPrecisionModel()) >= 0) { this.setComputationPrecision(g0$1.getPrecisionModel()); } else { this.setComputationPrecision(g1.getPrecisionModel()); }
110969             this._arg = new Array(2).fill(null);
110970             this._arg[0] = new GeometryGraph(0, g0$1, boundaryNodeRule);
110971             this._arg[1] = new GeometryGraph(1, g1, boundaryNodeRule);
110972           } else if (arguments.length === 3) {
110973             var g0$2 = arguments[0];
110974             var g1$1 = arguments[1];
110975             var boundaryNodeRule$1 = arguments[2];
110976             if (g0$2.getPrecisionModel().compareTo(g1$1.getPrecisionModel()) >= 0) { this.setComputationPrecision(g0$2.getPrecisionModel()); } else { this.setComputationPrecision(g1$1.getPrecisionModel()); }
110977             this._arg = new Array(2).fill(null);
110978             this._arg[0] = new GeometryGraph(0, g0$2, boundaryNodeRule$1);
110979             this._arg[1] = new GeometryGraph(1, g1$1, boundaryNodeRule$1);
110980           }
110981         };
110982         GeometryGraphOp.prototype.getArgGeometry = function getArgGeometry (i) {
110983           return this._arg[i].getGeometry()
110984         };
110985         GeometryGraphOp.prototype.setComputationPrecision = function setComputationPrecision (pm) {
110986           this._resultPrecisionModel = pm;
110987           this._li.setPrecisionModel(this._resultPrecisionModel);
110988         };
110989         GeometryGraphOp.prototype.interfaces_ = function interfaces_ () {
110990           return []
110991         };
110992         GeometryGraphOp.prototype.getClass = function getClass () {
110993           return GeometryGraphOp
110994         };
110995
110996         // operation.geometrygraph
110997
110998         var GeometryMapper = function GeometryMapper () {};
110999
111000         GeometryMapper.prototype.interfaces_ = function interfaces_ () {
111001           return []
111002         };
111003         GeometryMapper.prototype.getClass = function getClass () {
111004           return GeometryMapper
111005         };
111006         GeometryMapper.map = function map () {
111007           if (arguments[0] instanceof Geometry && hasInterface(arguments[1], GeometryMapper.MapOp)) {
111008             var geom = arguments[0];
111009             var op = arguments[1];
111010             var mapped = new ArrayList();
111011             for (var i = 0; i < geom.getNumGeometries(); i++) {
111012               var g = op.map(geom.getGeometryN(i));
111013               if (g !== null) { mapped.add(g); }
111014             }
111015             return geom.getFactory().buildGeometry(mapped)
111016           } else if (hasInterface(arguments[0], Collection) && hasInterface(arguments[1], GeometryMapper.MapOp)) {
111017             var geoms = arguments[0];
111018             var op$1 = arguments[1];
111019             var mapped$1 = new ArrayList();
111020             for (var i$1 = geoms.iterator(); i$1.hasNext();) {
111021               var g$1 = i$1.next();
111022               var gr = op$1.map(g$1);
111023               if (gr !== null) { mapped$1.add(gr); }
111024             }
111025             return mapped$1
111026           }
111027         };
111028         GeometryMapper.MapOp = function MapOp () {};
111029
111030         var OverlayOp = (function (GeometryGraphOp) {
111031           function OverlayOp () {
111032             var g0 = arguments[0];
111033             var g1 = arguments[1];
111034             GeometryGraphOp.call(this, g0, g1);
111035             this._ptLocator = new PointLocator();
111036             this._geomFact = null;
111037             this._resultGeom = null;
111038             this._graph = null;
111039             this._edgeList = new EdgeList();
111040             this._resultPolyList = new ArrayList();
111041             this._resultLineList = new ArrayList();
111042             this._resultPointList = new ArrayList();
111043             this._graph = new PlanarGraph(new OverlayNodeFactory());
111044             this._geomFact = g0.getFactory();
111045           }
111046
111047           if ( GeometryGraphOp ) OverlayOp.__proto__ = GeometryGraphOp;
111048           OverlayOp.prototype = Object.create( GeometryGraphOp && GeometryGraphOp.prototype );
111049           OverlayOp.prototype.constructor = OverlayOp;
111050           OverlayOp.prototype.insertUniqueEdge = function insertUniqueEdge (e) {
111051             var existingEdge = this._edgeList.findEqualEdge(e);
111052             if (existingEdge !== null) {
111053               var existingLabel = existingEdge.getLabel();
111054               var labelToMerge = e.getLabel();
111055               if (!existingEdge.isPointwiseEqual(e)) {
111056                 labelToMerge = new Label(e.getLabel());
111057                 labelToMerge.flip();
111058               }
111059               var depth = existingEdge.getDepth();
111060               if (depth.isNull()) {
111061                 depth.add(existingLabel);
111062               }
111063               depth.add(labelToMerge);
111064               existingLabel.merge(labelToMerge);
111065             } else {
111066               this._edgeList.add(e);
111067             }
111068           };
111069           OverlayOp.prototype.getGraph = function getGraph () {
111070             return this._graph
111071           };
111072           OverlayOp.prototype.cancelDuplicateResultEdges = function cancelDuplicateResultEdges () {
111073             for (var it = this._graph.getEdgeEnds().iterator(); it.hasNext();) {
111074               var de = it.next();
111075               var sym = de.getSym();
111076               if (de.isInResult() && sym.isInResult()) {
111077                 de.setInResult(false);
111078                 sym.setInResult(false);
111079               }
111080             }
111081           };
111082           OverlayOp.prototype.isCoveredByLA = function isCoveredByLA (coord) {
111083             if (this.isCovered(coord, this._resultLineList)) { return true }
111084             if (this.isCovered(coord, this._resultPolyList)) { return true }
111085             return false
111086           };
111087           OverlayOp.prototype.computeGeometry = function computeGeometry (resultPointList, resultLineList, resultPolyList, opcode) {
111088             var geomList = new ArrayList();
111089             geomList.addAll(resultPointList);
111090             geomList.addAll(resultLineList);
111091             geomList.addAll(resultPolyList);
111092             if (geomList.isEmpty()) { return OverlayOp.createEmptyResult(opcode, this._arg[0].getGeometry(), this._arg[1].getGeometry(), this._geomFact) }
111093             return this._geomFact.buildGeometry(geomList)
111094           };
111095           OverlayOp.prototype.mergeSymLabels = function mergeSymLabels () {
111096             for (var nodeit = this._graph.getNodes().iterator(); nodeit.hasNext();) {
111097               var node = nodeit.next();
111098               node.getEdges().mergeSymLabels();
111099             }
111100           };
111101           OverlayOp.prototype.isCovered = function isCovered (coord, geomList) {
111102             var this$1 = this;
111103
111104             for (var it = geomList.iterator(); it.hasNext();) {
111105               var geom = it.next();
111106               var loc = this$1._ptLocator.locate(coord, geom);
111107               if (loc !== Location.EXTERIOR) { return true }
111108             }
111109             return false
111110           };
111111           OverlayOp.prototype.replaceCollapsedEdges = function replaceCollapsedEdges () {
111112             var newEdges = new ArrayList();
111113             for (var it = this._edgeList.iterator(); it.hasNext();) {
111114               var e = it.next();
111115               if (e.isCollapsed()) {
111116                 it.remove();
111117                 newEdges.add(e.getCollapsedEdge());
111118               }
111119             }
111120             this._edgeList.addAll(newEdges);
111121           };
111122           OverlayOp.prototype.updateNodeLabelling = function updateNodeLabelling () {
111123             for (var nodeit = this._graph.getNodes().iterator(); nodeit.hasNext();) {
111124               var node = nodeit.next();
111125               var lbl = node.getEdges().getLabel();
111126               node.getLabel().merge(lbl);
111127             }
111128           };
111129           OverlayOp.prototype.getResultGeometry = function getResultGeometry (overlayOpCode) {
111130             this.computeOverlay(overlayOpCode);
111131             return this._resultGeom
111132           };
111133           OverlayOp.prototype.insertUniqueEdges = function insertUniqueEdges (edges) {
111134             var this$1 = this;
111135
111136             for (var i = edges.iterator(); i.hasNext();) {
111137               var e = i.next();
111138               this$1.insertUniqueEdge(e);
111139             }
111140           };
111141           OverlayOp.prototype.computeOverlay = function computeOverlay (opCode) {
111142             this.copyPoints(0);
111143             this.copyPoints(1);
111144             this._arg[0].computeSelfNodes(this._li, false);
111145             this._arg[1].computeSelfNodes(this._li, false);
111146             this._arg[0].computeEdgeIntersections(this._arg[1], this._li, true);
111147             var baseSplitEdges = new ArrayList();
111148             this._arg[0].computeSplitEdges(baseSplitEdges);
111149             this._arg[1].computeSplitEdges(baseSplitEdges);
111150             // const splitEdges = baseSplitEdges
111151             this.insertUniqueEdges(baseSplitEdges);
111152             this.computeLabelsFromDepths();
111153             this.replaceCollapsedEdges();
111154             EdgeNodingValidator.checkValid(this._edgeList.getEdges());
111155             this._graph.addEdges(this._edgeList.getEdges());
111156             this.computeLabelling();
111157             this.labelIncompleteNodes();
111158             this.findResultAreaEdges(opCode);
111159             this.cancelDuplicateResultEdges();
111160             var polyBuilder = new PolygonBuilder(this._geomFact);
111161             polyBuilder.add(this._graph);
111162             this._resultPolyList = polyBuilder.getPolygons();
111163             var lineBuilder = new LineBuilder(this, this._geomFact, this._ptLocator);
111164             this._resultLineList = lineBuilder.build(opCode);
111165             var pointBuilder = new PointBuilder(this, this._geomFact, this._ptLocator);
111166             this._resultPointList = pointBuilder.build(opCode);
111167             this._resultGeom = this.computeGeometry(this._resultPointList, this._resultLineList, this._resultPolyList, opCode);
111168           };
111169           OverlayOp.prototype.labelIncompleteNode = function labelIncompleteNode (n, targetIndex) {
111170             var loc = this._ptLocator.locate(n.getCoordinate(), this._arg[targetIndex].getGeometry());
111171             n.getLabel().setLocation(targetIndex, loc);
111172           };
111173           OverlayOp.prototype.copyPoints = function copyPoints (argIndex) {
111174             var this$1 = this;
111175
111176             for (var i = this._arg[argIndex].getNodeIterator(); i.hasNext();) {
111177               var graphNode = i.next();
111178               var newNode = this$1._graph.addNode(graphNode.getCoordinate());
111179               newNode.setLabel(argIndex, graphNode.getLabel().getLocation(argIndex));
111180             }
111181           };
111182           OverlayOp.prototype.findResultAreaEdges = function findResultAreaEdges (opCode) {
111183             for (var it = this._graph.getEdgeEnds().iterator(); it.hasNext();) {
111184               var de = it.next();
111185               var label = de.getLabel();
111186               if (label.isArea() && !de.isInteriorAreaEdge() && OverlayOp.isResultOfOp(label.getLocation(0, Position.RIGHT), label.getLocation(1, Position.RIGHT), opCode)) {
111187                 de.setInResult(true);
111188               }
111189             }
111190           };
111191           OverlayOp.prototype.computeLabelsFromDepths = function computeLabelsFromDepths () {
111192             for (var it = this._edgeList.iterator(); it.hasNext();) {
111193               var e = it.next();
111194               var lbl = e.getLabel();
111195               var depth = e.getDepth();
111196               if (!depth.isNull()) {
111197                 depth.normalize();
111198                 for (var i = 0; i < 2; i++) {
111199                   if (!lbl.isNull(i) && lbl.isArea() && !depth.isNull(i)) {
111200                     if (depth.getDelta(i) === 0) {
111201                       lbl.toLine(i);
111202                     } else {
111203                       Assert.isTrue(!depth.isNull(i, Position.LEFT), 'depth of LEFT side has not been initialized');
111204                       lbl.setLocation(i, Position.LEFT, depth.getLocation(i, Position.LEFT));
111205                       Assert.isTrue(!depth.isNull(i, Position.RIGHT), 'depth of RIGHT side has not been initialized');
111206                       lbl.setLocation(i, Position.RIGHT, depth.getLocation(i, Position.RIGHT));
111207                     }
111208                   }
111209                 }
111210               }
111211             }
111212           };
111213           OverlayOp.prototype.computeLabelling = function computeLabelling () {
111214             var this$1 = this;
111215
111216             for (var nodeit = this._graph.getNodes().iterator(); nodeit.hasNext();) {
111217               var node = nodeit.next();
111218               node.getEdges().computeLabelling(this$1._arg);
111219             }
111220             this.mergeSymLabels();
111221             this.updateNodeLabelling();
111222           };
111223           OverlayOp.prototype.labelIncompleteNodes = function labelIncompleteNodes () {
111224             var this$1 = this;
111225
111226             // let nodeCount = 0
111227             for (var ni = this._graph.getNodes().iterator(); ni.hasNext();) {
111228               var n = ni.next();
111229               var label = n.getLabel();
111230               if (n.isIsolated()) {
111231                 // nodeCount++
111232                 if (label.isNull(0)) { this$1.labelIncompleteNode(n, 0); } else { this$1.labelIncompleteNode(n, 1); }
111233               }
111234               n.getEdges().updateLabelling(label);
111235             }
111236           };
111237           OverlayOp.prototype.isCoveredByA = function isCoveredByA (coord) {
111238             if (this.isCovered(coord, this._resultPolyList)) { return true }
111239             return false
111240           };
111241           OverlayOp.prototype.interfaces_ = function interfaces_ () {
111242             return []
111243           };
111244           OverlayOp.prototype.getClass = function getClass () {
111245             return OverlayOp
111246           };
111247
111248           return OverlayOp;
111249         }(GeometryGraphOp));
111250
111251         OverlayOp.overlayOp = function (geom0, geom1, opCode) {
111252           var gov = new OverlayOp(geom0, geom1);
111253           var geomOv = gov.getResultGeometry(opCode);
111254           return geomOv
111255         };
111256         OverlayOp.intersection = function (g, other) {
111257           if (g.isEmpty() || other.isEmpty()) { return OverlayOp.createEmptyResult(OverlayOp.INTERSECTION, g, other, g.getFactory()) }
111258           if (g.isGeometryCollection()) {
111259             var g2 = other;
111260             return GeometryCollectionMapper.map(g, {
111261               interfaces_: function () {
111262                 return [GeometryMapper.MapOp]
111263               },
111264               map: function (g) {
111265                 return g.intersection(g2)
111266               }
111267             })
111268           }
111269           g.checkNotGeometryCollection(g);
111270           g.checkNotGeometryCollection(other);
111271           return SnapIfNeededOverlayOp.overlayOp(g, other, OverlayOp.INTERSECTION)
111272         };
111273         OverlayOp.symDifference = function (g, other) {
111274           if (g.isEmpty() || other.isEmpty()) {
111275             if (g.isEmpty() && other.isEmpty()) { return OverlayOp.createEmptyResult(OverlayOp.SYMDIFFERENCE, g, other, g.getFactory()) }
111276             if (g.isEmpty()) { return other.copy() }
111277             if (other.isEmpty()) { return g.copy() }
111278           }
111279           g.checkNotGeometryCollection(g);
111280           g.checkNotGeometryCollection(other);
111281           return SnapIfNeededOverlayOp.overlayOp(g, other, OverlayOp.SYMDIFFERENCE)
111282         };
111283         OverlayOp.resultDimension = function (opCode, g0, g1) {
111284           var dim0 = g0.getDimension();
111285           var dim1 = g1.getDimension();
111286           var resultDimension = -1;
111287           switch (opCode) {
111288             case OverlayOp.INTERSECTION:
111289               resultDimension = Math.min(dim0, dim1);
111290               break
111291             case OverlayOp.UNION:
111292               resultDimension = Math.max(dim0, dim1);
111293               break
111294             case OverlayOp.DIFFERENCE:
111295               resultDimension = dim0;
111296               break
111297             case OverlayOp.SYMDIFFERENCE:
111298               resultDimension = Math.max(dim0, dim1);
111299               break
111300           }
111301           return resultDimension
111302         };
111303         OverlayOp.createEmptyResult = function (overlayOpCode, a, b, geomFact) {
111304           var result = null;
111305           switch (OverlayOp.resultDimension(overlayOpCode, a, b)) {
111306             case -1:
111307               result = geomFact.createGeometryCollection(new Array(0).fill(null));
111308               break
111309             case 0:
111310               result = geomFact.createPoint();
111311               break
111312             case 1:
111313               result = geomFact.createLineString();
111314               break
111315             case 2:
111316               result = geomFact.createPolygon();
111317               break
111318           }
111319           return result
111320         };
111321         OverlayOp.difference = function (g, other) {
111322           if (g.isEmpty()) { return OverlayOp.createEmptyResult(OverlayOp.DIFFERENCE, g, other, g.getFactory()) }
111323           if (other.isEmpty()) { return g.copy() }
111324           g.checkNotGeometryCollection(g);
111325           g.checkNotGeometryCollection(other);
111326           return SnapIfNeededOverlayOp.overlayOp(g, other, OverlayOp.DIFFERENCE)
111327         };
111328         OverlayOp.isResultOfOp = function () {
111329           if (arguments.length === 2) {
111330             var label = arguments[0];
111331             var opCode = arguments[1];
111332             var loc0 = label.getLocation(0);
111333             var loc1 = label.getLocation(1);
111334             return OverlayOp.isResultOfOp(loc0, loc1, opCode)
111335           } else if (arguments.length === 3) {
111336             var loc0$1 = arguments[0];
111337             var loc1$1 = arguments[1];
111338             var overlayOpCode = arguments[2];
111339             if (loc0$1 === Location.BOUNDARY) { loc0$1 = Location.INTERIOR; }
111340             if (loc1$1 === Location.BOUNDARY) { loc1$1 = Location.INTERIOR; }
111341             switch (overlayOpCode) {
111342               case OverlayOp.INTERSECTION:
111343                 return loc0$1 === Location.INTERIOR && loc1$1 === Location.INTERIOR
111344               case OverlayOp.UNION:
111345                 return loc0$1 === Location.INTERIOR || loc1$1 === Location.INTERIOR
111346               case OverlayOp.DIFFERENCE:
111347                 return loc0$1 === Location.INTERIOR && loc1$1 !== Location.INTERIOR
111348               case OverlayOp.SYMDIFFERENCE:
111349                 return (loc0$1 === Location.INTERIOR && loc1$1 !== Location.INTERIOR) || (loc0$1 !== Location.INTERIOR && loc1$1 === Location.INTERIOR)
111350             }
111351             return false
111352           }
111353         };
111354         OverlayOp.INTERSECTION = 1;
111355         OverlayOp.UNION = 2;
111356         OverlayOp.DIFFERENCE = 3;
111357         OverlayOp.SYMDIFFERENCE = 4;
111358
111359         var FuzzyPointLocator = function FuzzyPointLocator () {
111360           this._g = null;
111361           this._boundaryDistanceTolerance = null;
111362           this._linework = null;
111363           this._ptLocator = new PointLocator();
111364           this._seg = new LineSegment();
111365           var g = arguments[0];
111366           var boundaryDistanceTolerance = arguments[1];
111367           this._g = g;
111368           this._boundaryDistanceTolerance = boundaryDistanceTolerance;
111369           this._linework = this.extractLinework(g);
111370         };
111371         FuzzyPointLocator.prototype.isWithinToleranceOfBoundary = function isWithinToleranceOfBoundary (pt) {
111372             var this$1 = this;
111373
111374           for (var i = 0; i < this._linework.getNumGeometries(); i++) {
111375             var line = this$1._linework.getGeometryN(i);
111376             var seq = line.getCoordinateSequence();
111377             for (var j = 0; j < seq.size() - 1; j++) {
111378               seq.getCoordinate(j, this$1._seg.p0);
111379               seq.getCoordinate(j + 1, this$1._seg.p1);
111380               var dist = this$1._seg.distance(pt);
111381               if (dist <= this$1._boundaryDistanceTolerance) { return true }
111382             }
111383           }
111384           return false
111385         };
111386         FuzzyPointLocator.prototype.getLocation = function getLocation (pt) {
111387           if (this.isWithinToleranceOfBoundary(pt)) { return Location.BOUNDARY }
111388           return this._ptLocator.locate(pt, this._g)
111389         };
111390         FuzzyPointLocator.prototype.extractLinework = function extractLinework (g) {
111391           var extracter = new PolygonalLineworkExtracter();
111392           g.apply(extracter);
111393           var linework = extracter.getLinework();
111394           var lines = GeometryFactory.toLineStringArray(linework);
111395           return g.getFactory().createMultiLineString(lines)
111396         };
111397         FuzzyPointLocator.prototype.interfaces_ = function interfaces_ () {
111398           return []
111399         };
111400         FuzzyPointLocator.prototype.getClass = function getClass () {
111401           return FuzzyPointLocator
111402         };
111403
111404         var PolygonalLineworkExtracter = function PolygonalLineworkExtracter () {
111405           this._linework = null;
111406           this._linework = new ArrayList();
111407         };
111408         PolygonalLineworkExtracter.prototype.getLinework = function getLinework () {
111409           return this._linework
111410         };
111411         PolygonalLineworkExtracter.prototype.filter = function filter (g) {
111412             var this$1 = this;
111413
111414           if (g instanceof Polygon) {
111415             var poly = g;
111416             this._linework.add(poly.getExteriorRing());
111417             for (var i = 0; i < poly.getNumInteriorRing(); i++) {
111418               this$1._linework.add(poly.getInteriorRingN(i));
111419             }
111420           }
111421         };
111422         PolygonalLineworkExtracter.prototype.interfaces_ = function interfaces_ () {
111423           return [GeometryFilter]
111424         };
111425         PolygonalLineworkExtracter.prototype.getClass = function getClass () {
111426           return PolygonalLineworkExtracter
111427         };
111428
111429         var OffsetPointGenerator = function OffsetPointGenerator () {
111430           this._g = null;
111431           this._doLeft = true;
111432           this._doRight = true;
111433           var g = arguments[0];
111434           this._g = g;
111435         };
111436         OffsetPointGenerator.prototype.extractPoints = function extractPoints (line, offsetDistance, offsetPts) {
111437             var this$1 = this;
111438
111439           var pts = line.getCoordinates();
111440           for (var i = 0; i < pts.length - 1; i++) {
111441             this$1.computeOffsetPoints(pts[i], pts[i + 1], offsetDistance, offsetPts);
111442           }
111443         };
111444         OffsetPointGenerator.prototype.setSidesToGenerate = function setSidesToGenerate (doLeft, doRight) {
111445           this._doLeft = doLeft;
111446           this._doRight = doRight;
111447         };
111448         OffsetPointGenerator.prototype.getPoints = function getPoints (offsetDistance) {
111449             var this$1 = this;
111450
111451           var offsetPts = new ArrayList();
111452           var lines = LinearComponentExtracter.getLines(this._g);
111453           for (var i = lines.iterator(); i.hasNext();) {
111454             var line = i.next();
111455             this$1.extractPoints(line, offsetDistance, offsetPts);
111456           }
111457           return offsetPts
111458         };
111459         OffsetPointGenerator.prototype.computeOffsetPoints = function computeOffsetPoints (p0, p1, offsetDistance, offsetPts) {
111460           var dx = p1.x - p0.x;
111461           var dy = p1.y - p0.y;
111462           var len = Math.sqrt(dx * dx + dy * dy);
111463           var ux = offsetDistance * dx / len;
111464           var uy = offsetDistance * dy / len;
111465           var midX = (p1.x + p0.x) / 2;
111466           var midY = (p1.y + p0.y) / 2;
111467           if (this._doLeft) {
111468             var offsetLeft = new Coordinate(midX - uy, midY + ux);
111469             offsetPts.add(offsetLeft);
111470           }
111471           if (this._doRight) {
111472             var offsetRight = new Coordinate(midX + uy, midY - ux);
111473             offsetPts.add(offsetRight);
111474           }
111475         };
111476         OffsetPointGenerator.prototype.interfaces_ = function interfaces_ () {
111477           return []
111478         };
111479         OffsetPointGenerator.prototype.getClass = function getClass () {
111480           return OffsetPointGenerator
111481         };
111482
111483         var OverlayResultValidator = function OverlayResultValidator () {
111484           this._geom = null;
111485           this._locFinder = null;
111486           this._location = new Array(3).fill(null);
111487           this._invalidLocation = null;
111488           this._boundaryDistanceTolerance = OverlayResultValidator.TOLERANCE;
111489           this._testCoords = new ArrayList();
111490           var a = arguments[0];
111491           var b = arguments[1];
111492           var result = arguments[2];
111493           this._boundaryDistanceTolerance = OverlayResultValidator.computeBoundaryDistanceTolerance(a, b);
111494           this._geom = [a, b, result];
111495           this._locFinder = [new FuzzyPointLocator(this._geom[0], this._boundaryDistanceTolerance), new FuzzyPointLocator(this._geom[1], this._boundaryDistanceTolerance), new FuzzyPointLocator(this._geom[2], this._boundaryDistanceTolerance)];
111496         };
111497
111498         var staticAccessors$46 = { TOLERANCE: { configurable: true } };
111499         OverlayResultValidator.prototype.reportResult = function reportResult (overlayOp, location, expectedInterior) {
111500           System.out.println('Overlay result invalid - A:' + Location.toLocationSymbol(location[0]) + ' B:' + Location.toLocationSymbol(location[1]) + ' expected:' + (expectedInterior ? 'i' : 'e') + ' actual:' + Location.toLocationSymbol(location[2]));
111501         };
111502         OverlayResultValidator.prototype.isValid = function isValid (overlayOp) {
111503           this.addTestPts(this._geom[0]);
111504           this.addTestPts(this._geom[1]);
111505           var isValid = this.checkValid(overlayOp);
111506           return isValid
111507         };
111508         OverlayResultValidator.prototype.checkValid = function checkValid () {
111509             var this$1 = this;
111510
111511           if (arguments.length === 1) {
111512             var overlayOp = arguments[0];
111513             for (var i = 0; i < this._testCoords.size(); i++) {
111514               var pt = this$1._testCoords.get(i);
111515               if (!this$1.checkValid(overlayOp, pt)) {
111516                 this$1._invalidLocation = pt;
111517                 return false
111518               }
111519             }
111520             return true
111521           } else if (arguments.length === 2) {
111522             var overlayOp$1 = arguments[0];
111523             var pt$1 = arguments[1];
111524             this._location[0] = this._locFinder[0].getLocation(pt$1);
111525             this._location[1] = this._locFinder[1].getLocation(pt$1);
111526             this._location[2] = this._locFinder[2].getLocation(pt$1);
111527             if (OverlayResultValidator.hasLocation(this._location, Location.BOUNDARY)) { return true }
111528             return this.isValidResult(overlayOp$1, this._location)
111529           }
111530         };
111531         OverlayResultValidator.prototype.addTestPts = function addTestPts (g) {
111532           var ptGen = new OffsetPointGenerator(g);
111533           this._testCoords.addAll(ptGen.getPoints(5 * this._boundaryDistanceTolerance));
111534         };
111535         OverlayResultValidator.prototype.isValidResult = function isValidResult (overlayOp, location) {
111536           var expectedInterior = OverlayOp.isResultOfOp(location[0], location[1], overlayOp);
111537           var resultInInterior = location[2] === Location.INTERIOR;
111538           var isValid = !(expectedInterior ^ resultInInterior);
111539           if (!isValid) { this.reportResult(overlayOp, location, expectedInterior); }
111540           return isValid
111541         };
111542         OverlayResultValidator.prototype.getInvalidLocation = function getInvalidLocation () {
111543           return this._invalidLocation
111544         };
111545         OverlayResultValidator.prototype.interfaces_ = function interfaces_ () {
111546           return []
111547         };
111548         OverlayResultValidator.prototype.getClass = function getClass () {
111549           return OverlayResultValidator
111550         };
111551         OverlayResultValidator.hasLocation = function hasLocation (location, loc) {
111552           for (var i = 0; i < 3; i++) {
111553             if (location[i] === loc) { return true }
111554           }
111555           return false
111556         };
111557         OverlayResultValidator.computeBoundaryDistanceTolerance = function computeBoundaryDistanceTolerance (g0, g1) {
111558           return Math.min(GeometrySnapper.computeSizeBasedSnapTolerance(g0), GeometrySnapper.computeSizeBasedSnapTolerance(g1))
111559         };
111560         OverlayResultValidator.isValid = function isValid (a, b, overlayOp, result) {
111561           var validator = new OverlayResultValidator(a, b, result);
111562           return validator.isValid(overlayOp)
111563         };
111564         staticAccessors$46.TOLERANCE.get = function () { return 0.000001 };
111565
111566         Object.defineProperties( OverlayResultValidator, staticAccessors$46 );
111567
111568         // operation.overlay
111569
111570         var GeometryCombiner = function GeometryCombiner (geoms) {
111571           this._geomFactory = null;
111572           this._skipEmpty = false;
111573           this._inputGeoms = null;
111574           this._geomFactory = GeometryCombiner.extractFactory(geoms);
111575           this._inputGeoms = geoms;
111576         };
111577         GeometryCombiner.prototype.extractElements = function extractElements (geom, elems) {
111578             var this$1 = this;
111579
111580           if (geom === null) { return null }
111581           for (var i = 0; i < geom.getNumGeometries(); i++) {
111582             var elemGeom = geom.getGeometryN(i);
111583             if (this$1._skipEmpty && elemGeom.isEmpty()) { continue }
111584             elems.add(elemGeom);
111585           }
111586         };
111587         GeometryCombiner.prototype.combine = function combine () {
111588             var this$1 = this;
111589
111590           var elems = new ArrayList();
111591           for (var i = this._inputGeoms.iterator(); i.hasNext();) {
111592             var g = i.next();
111593             this$1.extractElements(g, elems);
111594           }
111595           if (elems.size() === 0) {
111596             if (this._geomFactory !== null) {
111597               return this._geomFactory.createGeometryCollection(null)
111598             }
111599             return null
111600           }
111601           return this._geomFactory.buildGeometry(elems)
111602         };
111603         GeometryCombiner.prototype.interfaces_ = function interfaces_ () {
111604           return []
111605         };
111606         GeometryCombiner.prototype.getClass = function getClass () {
111607           return GeometryCombiner
111608         };
111609         GeometryCombiner.combine = function combine () {
111610           if (arguments.length === 1) {
111611             var geoms = arguments[0];
111612             var combiner = new GeometryCombiner(geoms);
111613             return combiner.combine()
111614           } else if (arguments.length === 2) {
111615             var g0 = arguments[0];
111616             var g1 = arguments[1];
111617             var combiner$1 = new GeometryCombiner(GeometryCombiner.createList(g0, g1));
111618             return combiner$1.combine()
111619           } else if (arguments.length === 3) {
111620             var g0$1 = arguments[0];
111621             var g1$1 = arguments[1];
111622             var g2 = arguments[2];
111623             var combiner$2 = new GeometryCombiner(GeometryCombiner.createList(g0$1, g1$1, g2));
111624             return combiner$2.combine()
111625           }
111626         };
111627         GeometryCombiner.extractFactory = function extractFactory (geoms) {
111628           if (geoms.isEmpty()) { return null }
111629           return geoms.iterator().next().getFactory()
111630         };
111631         GeometryCombiner.createList = function createList () {
111632           if (arguments.length === 2) {
111633             var obj0 = arguments[0];
111634             var obj1 = arguments[1];
111635             var list = new ArrayList();
111636             list.add(obj0);
111637             list.add(obj1);
111638             return list
111639           } else if (arguments.length === 3) {
111640             var obj0$1 = arguments[0];
111641             var obj1$1 = arguments[1];
111642             var obj2 = arguments[2];
111643             var list$1 = new ArrayList();
111644             list$1.add(obj0$1);
111645             list$1.add(obj1$1);
111646             list$1.add(obj2);
111647             return list$1
111648           }
111649         };
111650
111651         var CascadedPolygonUnion = function CascadedPolygonUnion () {
111652           this._inputPolys = null;
111653           this._geomFactory = null;
111654           var polys = arguments[0];
111655           this._inputPolys = polys;
111656           if (this._inputPolys === null) { this._inputPolys = new ArrayList(); }
111657         };
111658
111659         var staticAccessors$47 = { STRTREE_NODE_CAPACITY: { configurable: true } };
111660         CascadedPolygonUnion.prototype.reduceToGeometries = function reduceToGeometries (geomTree) {
111661             var this$1 = this;
111662
111663           var geoms = new ArrayList();
111664           for (var i = geomTree.iterator(); i.hasNext();) {
111665             var o = i.next();
111666             var geom = null;
111667             if (hasInterface(o, List)) {
111668               geom = this$1.unionTree(o);
111669             } else if (o instanceof Geometry) {
111670               geom = o;
111671             }
111672             geoms.add(geom);
111673           }
111674           return geoms
111675         };
111676         CascadedPolygonUnion.prototype.extractByEnvelope = function extractByEnvelope (env, geom, disjointGeoms) {
111677           var intersectingGeoms = new ArrayList();
111678           for (var i = 0; i < geom.getNumGeometries(); i++) {
111679             var elem = geom.getGeometryN(i);
111680             if (elem.getEnvelopeInternal().intersects(env)) { intersectingGeoms.add(elem); } else { disjointGeoms.add(elem); }
111681           }
111682           return this._geomFactory.buildGeometry(intersectingGeoms)
111683         };
111684         CascadedPolygonUnion.prototype.unionOptimized = function unionOptimized (g0, g1) {
111685           var g0Env = g0.getEnvelopeInternal();
111686           var g1Env = g1.getEnvelopeInternal();
111687           if (!g0Env.intersects(g1Env)) {
111688             var combo = GeometryCombiner.combine(g0, g1);
111689             return combo
111690           }
111691           if (g0.getNumGeometries() <= 1 && g1.getNumGeometries() <= 1) { return this.unionActual(g0, g1) }
111692           var commonEnv = g0Env.intersection(g1Env);
111693           return this.unionUsingEnvelopeIntersection(g0, g1, commonEnv)
111694         };
111695         CascadedPolygonUnion.prototype.union = function union () {
111696           if (this._inputPolys === null) { throw new Error('union() method cannot be called twice') }
111697           if (this._inputPolys.isEmpty()) { return null }
111698           this._geomFactory = this._inputPolys.iterator().next().getFactory();
111699           var index = new STRtree(CascadedPolygonUnion.STRTREE_NODE_CAPACITY);
111700           for (var i = this._inputPolys.iterator(); i.hasNext();) {
111701             var item = i.next();
111702             index.insert(item.getEnvelopeInternal(), item);
111703           }
111704           this._inputPolys = null;
111705           var itemTree = index.itemsTree();
111706           var unionAll = this.unionTree(itemTree);
111707           return unionAll
111708         };
111709         CascadedPolygonUnion.prototype.binaryUnion = function binaryUnion () {
111710           if (arguments.length === 1) {
111711             var geoms = arguments[0];
111712             return this.binaryUnion(geoms, 0, geoms.size())
111713           } else if (arguments.length === 3) {
111714             var geoms$1 = arguments[0];
111715             var start = arguments[1];
111716             var end = arguments[2];
111717             if (end - start <= 1) {
111718               var g0 = CascadedPolygonUnion.getGeometry(geoms$1, start);
111719               return this.unionSafe(g0, null)
111720             } else if (end - start === 2) {
111721               return this.unionSafe(CascadedPolygonUnion.getGeometry(geoms$1, start), CascadedPolygonUnion.getGeometry(geoms$1, start + 1))
111722             } else {
111723               var mid = Math.trunc((end + start) / 2);
111724               var g0$1 = this.binaryUnion(geoms$1, start, mid);
111725               var g1 = this.binaryUnion(geoms$1, mid, end);
111726               return this.unionSafe(g0$1, g1)
111727             }
111728           }
111729         };
111730         CascadedPolygonUnion.prototype.repeatedUnion = function repeatedUnion (geoms) {
111731           var union = null;
111732           for (var i = geoms.iterator(); i.hasNext();) {
111733             var g = i.next();
111734             if (union === null) { union = g.copy(); } else { union = union.union(g); }
111735           }
111736           return union
111737         };
111738         CascadedPolygonUnion.prototype.unionSafe = function unionSafe (g0, g1) {
111739           if (g0 === null && g1 === null) { return null }
111740           if (g0 === null) { return g1.copy() }
111741           if (g1 === null) { return g0.copy() }
111742           return this.unionOptimized(g0, g1)
111743         };
111744         CascadedPolygonUnion.prototype.unionActual = function unionActual (g0, g1) {
111745           return CascadedPolygonUnion.restrictToPolygons(g0.union(g1))
111746         };
111747         CascadedPolygonUnion.prototype.unionTree = function unionTree (geomTree) {
111748           var geoms = this.reduceToGeometries(geomTree);
111749           var union = this.binaryUnion(geoms);
111750           return union
111751         };
111752         CascadedPolygonUnion.prototype.unionUsingEnvelopeIntersection = function unionUsingEnvelopeIntersection (g0, g1, common) {
111753           var disjointPolys = new ArrayList();
111754           var g0Int = this.extractByEnvelope(common, g0, disjointPolys);
111755           var g1Int = this.extractByEnvelope(common, g1, disjointPolys);
111756           var union = this.unionActual(g0Int, g1Int);
111757           disjointPolys.add(union);
111758           var overallUnion = GeometryCombiner.combine(disjointPolys);
111759           return overallUnion
111760         };
111761         CascadedPolygonUnion.prototype.bufferUnion = function bufferUnion () {
111762           if (arguments.length === 1) {
111763             var geoms = arguments[0];
111764             var factory = geoms.get(0).getFactory();
111765             var gColl = factory.buildGeometry(geoms);
111766             var unionAll = gColl.buffer(0.0);
111767             return unionAll
111768           } else if (arguments.length === 2) {
111769             var g0 = arguments[0];
111770             var g1 = arguments[1];
111771             var factory$1 = g0.getFactory();
111772             var gColl$1 = factory$1.createGeometryCollection([g0, g1]);
111773             var unionAll$1 = gColl$1.buffer(0.0);
111774             return unionAll$1
111775           }
111776         };
111777         CascadedPolygonUnion.prototype.interfaces_ = function interfaces_ () {
111778           return []
111779         };
111780         CascadedPolygonUnion.prototype.getClass = function getClass () {
111781           return CascadedPolygonUnion
111782         };
111783         CascadedPolygonUnion.restrictToPolygons = function restrictToPolygons (g) {
111784           if (hasInterface(g, Polygonal)) {
111785             return g
111786           }
111787           var polygons = PolygonExtracter.getPolygons(g);
111788           if (polygons.size() === 1) { return polygons.get(0) }
111789           return g.getFactory().createMultiPolygon(GeometryFactory.toPolygonArray(polygons))
111790         };
111791         CascadedPolygonUnion.getGeometry = function getGeometry (list, index) {
111792           if (index >= list.size()) { return null }
111793           return list.get(index)
111794         };
111795         CascadedPolygonUnion.union = function union (polys) {
111796           var op = new CascadedPolygonUnion(polys);
111797           return op.union()
111798         };
111799         staticAccessors$47.STRTREE_NODE_CAPACITY.get = function () { return 4 };
111800
111801         Object.defineProperties( CascadedPolygonUnion, staticAccessors$47 );
111802
111803         var UnionOp = function UnionOp () {};
111804
111805         UnionOp.prototype.interfaces_ = function interfaces_ () {
111806           return []
111807         };
111808         UnionOp.prototype.getClass = function getClass () {
111809           return UnionOp
111810         };
111811         UnionOp.union = function union (g, other) {
111812           if (g.isEmpty() || other.isEmpty()) {
111813             if (g.isEmpty() && other.isEmpty()) { return OverlayOp.createEmptyResult(OverlayOp.UNION, g, other, g.getFactory()) }
111814             if (g.isEmpty()) { return other.copy() }
111815             if (other.isEmpty()) { return g.copy() }
111816           }
111817           g.checkNotGeometryCollection(g);
111818           g.checkNotGeometryCollection(other);
111819           return SnapIfNeededOverlayOp.overlayOp(g, other, OverlayOp.UNION)
111820         };
111821
111822         /**
111823          * Earth Radius used with the Harvesine formula and approximates using a spherical (non-ellipsoid) Earth.
111824          */
111825
111826         /**
111827          * Wraps a GeoJSON {@link Geometry} in a GeoJSON {@link Feature}.
111828          *
111829          * @name feature
111830          * @param {Geometry} geometry input geometry
111831          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
111832          * @param {Object} [options={}] Optional Parameters
111833          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
111834          * @param {string|number} [options.id] Identifier associated with the Feature
111835          * @returns {Feature} a GeoJSON Feature
111836          * @example
111837          * var geometry = {
111838          *   "type": "Point",
111839          *   "coordinates": [110, 50]
111840          * };
111841          *
111842          * var feature = turf.feature(geometry);
111843          *
111844          * //=feature
111845          */
111846         function feature$1(geometry, properties, options) {
111847             // Optional Parameters
111848             options = options || {};
111849             if (!isObject$4(options)) throw new Error('options is invalid');
111850             var bbox = options.bbox;
111851             var id = options.id;
111852
111853             // Validation
111854             if (geometry === undefined) throw new Error('geometry is required');
111855             if (properties && properties.constructor !== Object) throw new Error('properties must be an Object');
111856             if (bbox) validateBBox(bbox);
111857             if (id) validateId(id);
111858
111859             // Main
111860             var feat = {type: 'Feature'};
111861             if (id) feat.id = id;
111862             if (bbox) feat.bbox = bbox;
111863             feat.properties = properties || {};
111864             feat.geometry = geometry;
111865             return feat;
111866         }
111867
111868         /**
111869          * isNumber
111870          *
111871          * @param {*} num Number to validate
111872          * @returns {boolean} true/false
111873          * @example
111874          * turf.isNumber(123)
111875          * //=true
111876          * turf.isNumber('foo')
111877          * //=false
111878          */
111879         function isNumber$1(num) {
111880             return !isNaN(num) && num !== null && !Array.isArray(num);
111881         }
111882
111883         /**
111884          * isObject
111885          *
111886          * @param {*} input variable to validate
111887          * @returns {boolean} true/false
111888          * @example
111889          * turf.isObject({elevation: 10})
111890          * //=true
111891          * turf.isObject('foo')
111892          * //=false
111893          */
111894         function isObject$4(input) {
111895             return (!!input) && (input.constructor === Object);
111896         }
111897
111898         /**
111899          * Validate BBox
111900          *
111901          * @private
111902          * @param {Array<number>} bbox BBox to validate
111903          * @returns {void}
111904          * @throws Error if BBox is not valid
111905          * @example
111906          * validateBBox([-180, -40, 110, 50])
111907          * //=OK
111908          * validateBBox([-180, -40])
111909          * //=Error
111910          * validateBBox('Foo')
111911          * //=Error
111912          * validateBBox(5)
111913          * //=Error
111914          * validateBBox(null)
111915          * //=Error
111916          * validateBBox(undefined)
111917          * //=Error
111918          */
111919         function validateBBox(bbox) {
111920             if (!bbox) throw new Error('bbox is required');
111921             if (!Array.isArray(bbox)) throw new Error('bbox must be an Array');
111922             if (bbox.length !== 4 && bbox.length !== 6) throw new Error('bbox must be an Array of 4 or 6 numbers');
111923             bbox.forEach(function (num) {
111924                 if (!isNumber$1(num)) throw new Error('bbox must only contain numbers');
111925             });
111926         }
111927
111928         /**
111929          * Validate Id
111930          *
111931          * @private
111932          * @param {string|number} id Id to validate
111933          * @returns {void}
111934          * @throws Error if Id is not valid
111935          * @example
111936          * validateId([-180, -40, 110, 50])
111937          * //=Error
111938          * validateId([-180, -40])
111939          * //=Error
111940          * validateId('Foo')
111941          * //=OK
111942          * validateId(5)
111943          * //=OK
111944          * validateId(null)
111945          * //=Error
111946          * validateId(undefined)
111947          * //=Error
111948          */
111949         function validateId(id) {
111950             if (!id) throw new Error('id is required');
111951             if (['string', 'number'].indexOf(typeof id) === -1) throw new Error('id must be a number or a string');
111952         }
111953
111954         /**
111955          * Callback for geomEach
111956          *
111957          * @callback geomEachCallback
111958          * @param {Geometry} currentGeometry The current Geometry being processed.
111959          * @param {number} featureIndex The current index of the Feature being processed.
111960          * @param {Object} featureProperties The current Feature Properties being processed.
111961          * @param {Array<number>} featureBBox The current Feature BBox being processed.
111962          * @param {number|string} featureId The current Feature Id being processed.
111963          */
111964
111965         /**
111966          * Iterate over each geometry in any GeoJSON object, similar to Array.forEach()
111967          *
111968          * @name geomEach
111969          * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object
111970          * @param {Function} callback a method that takes (currentGeometry, featureIndex, featureProperties, featureBBox, featureId)
111971          * @returns {void}
111972          * @example
111973          * var features = turf.featureCollection([
111974          *     turf.point([26, 37], {foo: 'bar'}),
111975          *     turf.point([36, 53], {hello: 'world'})
111976          * ]);
111977          *
111978          * turf.geomEach(features, function (currentGeometry, featureIndex, featureProperties, featureBBox, featureId) {
111979          *   //=currentGeometry
111980          *   //=featureIndex
111981          *   //=featureProperties
111982          *   //=featureBBox
111983          *   //=featureId
111984          * });
111985          */
111986         function geomEach(geojson, callback) {
111987             var i, j, g, geometry, stopG,
111988                 geometryMaybeCollection,
111989                 isGeometryCollection,
111990                 featureProperties,
111991                 featureBBox,
111992                 featureId,
111993                 featureIndex = 0,
111994                 isFeatureCollection = geojson.type === 'FeatureCollection',
111995                 isFeature = geojson.type === 'Feature',
111996                 stop = isFeatureCollection ? geojson.features.length : 1;
111997
111998             // This logic may look a little weird. The reason why it is that way
111999             // is because it's trying to be fast. GeoJSON supports multiple kinds
112000             // of objects at its root: FeatureCollection, Features, Geometries.
112001             // This function has the responsibility of handling all of them, and that
112002             // means that some of the `for` loops you see below actually just don't apply
112003             // to certain inputs. For instance, if you give this just a
112004             // Point geometry, then both loops are short-circuited and all we do
112005             // is gradually rename the input until it's called 'geometry'.
112006             //
112007             // This also aims to allocate as few resources as possible: just a
112008             // few numbers and booleans, rather than any temporary arrays as would
112009             // be required with the normalization approach.
112010             for (i = 0; i < stop; i++) {
112011
112012                 geometryMaybeCollection = (isFeatureCollection ? geojson.features[i].geometry :
112013                     (isFeature ? geojson.geometry : geojson));
112014                 featureProperties = (isFeatureCollection ? geojson.features[i].properties :
112015                     (isFeature ? geojson.properties : {}));
112016                 featureBBox = (isFeatureCollection ? geojson.features[i].bbox :
112017                     (isFeature ? geojson.bbox : undefined));
112018                 featureId = (isFeatureCollection ? geojson.features[i].id :
112019                     (isFeature ? geojson.id : undefined));
112020                 isGeometryCollection = (geometryMaybeCollection) ? geometryMaybeCollection.type === 'GeometryCollection' : false;
112021                 stopG = isGeometryCollection ? geometryMaybeCollection.geometries.length : 1;
112022
112023                 for (g = 0; g < stopG; g++) {
112024                     geometry = isGeometryCollection ?
112025                         geometryMaybeCollection.geometries[g] : geometryMaybeCollection;
112026
112027                     // Handle null Geometry
112028                     if (geometry === null) {
112029                         if (callback(null, featureIndex, featureProperties, featureBBox, featureId) === false) return false;
112030                         continue;
112031                     }
112032                     switch (geometry.type) {
112033                     case 'Point':
112034                     case 'LineString':
112035                     case 'MultiPoint':
112036                     case 'Polygon':
112037                     case 'MultiLineString':
112038                     case 'MultiPolygon': {
112039                         if (callback(geometry, featureIndex, featureProperties, featureBBox, featureId) === false) return false;
112040                         break;
112041                     }
112042                     case 'GeometryCollection': {
112043                         for (j = 0; j < geometry.geometries.length; j++) {
112044                             if (callback(geometry.geometries[j], featureIndex, featureProperties, featureBBox, featureId) === false) return false;
112045                         }
112046                         break;
112047                     }
112048                     default:
112049                         throw new Error('Unknown Geometry Type');
112050                     }
112051                 }
112052                 // Only increase `featureIndex` per each feature
112053                 featureIndex++;
112054             }
112055         }
112056
112057         /**
112058          * Callback for geomReduce
112059          *
112060          * The first time the callback function is called, the values provided as arguments depend
112061          * on whether the reduce method has an initialValue argument.
112062          *
112063          * If an initialValue is provided to the reduce method:
112064          *  - The previousValue argument is initialValue.
112065          *  - The currentValue argument is the value of the first element present in the array.
112066          *
112067          * If an initialValue is not provided:
112068          *  - The previousValue argument is the value of the first element present in the array.
112069          *  - The currentValue argument is the value of the second element present in the array.
112070          *
112071          * @callback geomReduceCallback
112072          * @param {*} previousValue The accumulated value previously returned in the last invocation
112073          * of the callback, or initialValue, if supplied.
112074          * @param {Geometry} currentGeometry The current Geometry being processed.
112075          * @param {number} featureIndex The current index of the Feature being processed.
112076          * @param {Object} featureProperties The current Feature Properties being processed.
112077          * @param {Array<number>} featureBBox The current Feature BBox being processed.
112078          * @param {number|string} featureId The current Feature Id being processed.
112079          */
112080
112081         /**
112082          * Reduce geometry in any GeoJSON object, similar to Array.reduce().
112083          *
112084          * @name geomReduce
112085          * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object
112086          * @param {Function} callback a method that takes (previousValue, currentGeometry, featureIndex, featureProperties, featureBBox, featureId)
112087          * @param {*} [initialValue] Value to use as the first argument to the first call of the callback.
112088          * @returns {*} The value that results from the reduction.
112089          * @example
112090          * var features = turf.featureCollection([
112091          *     turf.point([26, 37], {foo: 'bar'}),
112092          *     turf.point([36, 53], {hello: 'world'})
112093          * ]);
112094          *
112095          * turf.geomReduce(features, function (previousValue, currentGeometry, featureIndex, featureProperties, featureBBox, featureId) {
112096          *   //=previousValue
112097          *   //=currentGeometry
112098          *   //=featureIndex
112099          *   //=featureProperties
112100          *   //=featureBBox
112101          *   //=featureId
112102          *   return currentGeometry
112103          * });
112104          */
112105         function geomReduce(geojson, callback, initialValue) {
112106             var previousValue = initialValue;
112107             geomEach(geojson, function (currentGeometry, featureIndex, featureProperties, featureBBox, featureId) {
112108                 if (featureIndex === 0 && initialValue === undefined) previousValue = currentGeometry;
112109                 else previousValue = callback(previousValue, currentGeometry, featureIndex, featureProperties, featureBBox, featureId);
112110             });
112111             return previousValue;
112112         }
112113
112114         /**
112115          * Callback for flattenEach
112116          *
112117          * @callback flattenEachCallback
112118          * @param {Feature} currentFeature The current flattened feature being processed.
112119          * @param {number} featureIndex The current index of the Feature being processed.
112120          * @param {number} multiFeatureIndex The current index of the Multi-Feature being processed.
112121          */
112122
112123         /**
112124          * Iterate over flattened features in any GeoJSON object, similar to
112125          * Array.forEach.
112126          *
112127          * @name flattenEach
112128          * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object
112129          * @param {Function} callback a method that takes (currentFeature, featureIndex, multiFeatureIndex)
112130          * @example
112131          * var features = turf.featureCollection([
112132          *     turf.point([26, 37], {foo: 'bar'}),
112133          *     turf.multiPoint([[40, 30], [36, 53]], {hello: 'world'})
112134          * ]);
112135          *
112136          * turf.flattenEach(features, function (currentFeature, featureIndex, multiFeatureIndex) {
112137          *   //=currentFeature
112138          *   //=featureIndex
112139          *   //=multiFeatureIndex
112140          * });
112141          */
112142         function flattenEach(geojson, callback) {
112143             geomEach(geojson, function (geometry, featureIndex, properties, bbox, id) {
112144                 // Callback for single geometry
112145                 var type = (geometry === null) ? null : geometry.type;
112146                 switch (type) {
112147                 case null:
112148                 case 'Point':
112149                 case 'LineString':
112150                 case 'Polygon':
112151                     if (callback(feature$1(geometry, properties, {bbox: bbox, id: id}), featureIndex, 0) === false) return false;
112152                     return;
112153                 }
112154
112155                 var geomType;
112156
112157                 // Callback for multi-geometry
112158                 switch (type) {
112159                 case 'MultiPoint':
112160                     geomType = 'Point';
112161                     break;
112162                 case 'MultiLineString':
112163                     geomType = 'LineString';
112164                     break;
112165                 case 'MultiPolygon':
112166                     geomType = 'Polygon';
112167                     break;
112168                 }
112169
112170                 for (var multiFeatureIndex = 0; multiFeatureIndex < geometry.coordinates.length; multiFeatureIndex++) {
112171                     var coordinate = geometry.coordinates[multiFeatureIndex];
112172                     var geom = {
112173                         type: geomType,
112174                         coordinates: coordinate
112175                     };
112176                     if (callback(feature$1(geom, properties), featureIndex, multiFeatureIndex) === false) return false;
112177                 }
112178             });
112179         }
112180
112181         /**
112182          * Takes one or more features and returns their area in square meters.
112183          *
112184          * @name area
112185          * @param {GeoJSON} geojson input GeoJSON feature(s)
112186          * @returns {number} area in square meters
112187          * @example
112188          * var polygon = turf.polygon([[[125, -15], [113, -22], [154, -27], [144, -15], [125, -15]]]);
112189          *
112190          * var area = turf.area(polygon);
112191          *
112192          * //addToMap
112193          * var addToMap = [polygon]
112194          * polygon.properties.area = area
112195          */
112196         function area(geojson) {
112197             return geomReduce(geojson, function (value, geom) {
112198                 return value + calculateArea(geom);
112199             }, 0);
112200         }
112201
112202         var RADIUS$1 = 6378137;
112203         // var FLATTENING_DENOM = 298.257223563;
112204         // var FLATTENING = 1 / FLATTENING_DENOM;
112205         // var POLAR_RADIUS = RADIUS * (1 - FLATTENING);
112206
112207         /**
112208          * Calculate Area
112209          *
112210          * @private
112211          * @param {GeoJSON} geojson GeoJSON
112212          * @returns {number} area
112213          */
112214         function calculateArea(geojson) {
112215             var area = 0, i;
112216             switch (geojson.type) {
112217             case 'Polygon':
112218                 return polygonArea$1(geojson.coordinates);
112219             case 'MultiPolygon':
112220                 for (i = 0; i < geojson.coordinates.length; i++) {
112221                     area += polygonArea$1(geojson.coordinates[i]);
112222                 }
112223                 return area;
112224             case 'Point':
112225             case 'MultiPoint':
112226             case 'LineString':
112227             case 'MultiLineString':
112228                 return 0;
112229             case 'GeometryCollection':
112230                 for (i = 0; i < geojson.geometries.length; i++) {
112231                     area += calculateArea(geojson.geometries[i]);
112232                 }
112233                 return area;
112234             }
112235         }
112236
112237         function polygonArea$1(coords) {
112238             var area = 0;
112239             if (coords && coords.length > 0) {
112240                 area += Math.abs(ringArea$1(coords[0]));
112241                 for (var i = 1; i < coords.length; i++) {
112242                     area -= Math.abs(ringArea$1(coords[i]));
112243                 }
112244             }
112245             return area;
112246         }
112247
112248         /**
112249          * @private
112250          * Calculate the approximate area of the polygon were it projected onto the earth.
112251          * Note that this area will be positive if ring is oriented clockwise, otherwise it will be negative.
112252          *
112253          * Reference:
112254          * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
112255          * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
112256          *
112257          * @param {Array<Array<number>>} coords Ring Coordinates
112258          * @returns {number} The approximate signed geodesic area of the polygon in square meters.
112259          */
112260         function ringArea$1(coords) {
112261             var p1;
112262             var p2;
112263             var p3;
112264             var lowerIndex;
112265             var middleIndex;
112266             var upperIndex;
112267             var i;
112268             var area = 0;
112269             var coordsLength = coords.length;
112270
112271             if (coordsLength > 2) {
112272                 for (i = 0; i < coordsLength; i++) {
112273                     if (i === coordsLength - 2) { // i = N-2
112274                         lowerIndex = coordsLength - 2;
112275                         middleIndex = coordsLength - 1;
112276                         upperIndex = 0;
112277                     } else if (i === coordsLength - 1) { // i = N-1
112278                         lowerIndex = coordsLength - 1;
112279                         middleIndex = 0;
112280                         upperIndex = 1;
112281                     } else { // i = 0 to N-3
112282                         lowerIndex = i;
112283                         middleIndex = i + 1;
112284                         upperIndex = i + 2;
112285                     }
112286                     p1 = coords[lowerIndex];
112287                     p2 = coords[middleIndex];
112288                     p3 = coords[upperIndex];
112289                     area += (rad$1(p3[0]) - rad$1(p1[0])) * Math.sin(rad$1(p2[1]));
112290                 }
112291
112292                 area = area * RADIUS$1 * RADIUS$1 / 2;
112293             }
112294
112295             return area;
112296         }
112297
112298         function rad$1(_) {
112299             return _ * Math.PI / 180;
112300         }
112301
112302         /**
112303          * Get Geometry from Feature or Geometry Object
112304          *
112305          * @param {Feature|Geometry} geojson GeoJSON Feature or Geometry Object
112306          * @returns {Geometry|null} GeoJSON Geometry Object
112307          * @throws {Error} if geojson is not a Feature or Geometry Object
112308          * @example
112309          * var point = {
112310          *   "type": "Feature",
112311          *   "properties": {},
112312          *   "geometry": {
112313          *     "type": "Point",
112314          *     "coordinates": [110, 40]
112315          *   }
112316          * }
112317          * var geom = turf.getGeom(point)
112318          * //={"type": "Point", "coordinates": [110, 40]}
112319          */
112320         function getGeom(geojson) {
112321             if (!geojson) throw new Error('geojson is required');
112322             if (geojson.geometry !== undefined) return geojson.geometry;
112323             if (geojson.coordinates || geojson.geometries) return geojson;
112324             throw new Error('geojson must be a valid Feature or Geometry Object');
112325         }
112326
112327         /**
112328          * Finds the difference between two {@link Polygon|polygons} by clipping the second polygon from the first.
112329          *
112330          * @name difference
112331          * @param {Feature<Polygon|MultiPolygon>} polygon1 input Polygon feature
112332          * @param {Feature<Polygon|MultiPolygon>} polygon2 Polygon feature to difference from polygon1
112333          * @returns {Feature<Polygon|MultiPolygon>|null} a Polygon or MultiPolygon feature showing the area of `polygon1` excluding the area of `polygon2` (if empty returns `null`)
112334          * @example
112335          * var polygon1 = turf.polygon([[
112336          *   [128, -26],
112337          *   [141, -26],
112338          *   [141, -21],
112339          *   [128, -21],
112340          *   [128, -26]
112341          * ]], {
112342          *   "fill": "#F00",
112343          *   "fill-opacity": 0.1
112344          * });
112345          * var polygon2 = turf.polygon([[
112346          *   [126, -28],
112347          *   [140, -28],
112348          *   [140, -20],
112349          *   [126, -20],
112350          *   [126, -28]
112351          * ]], {
112352          *   "fill": "#00F",
112353          *   "fill-opacity": 0.1
112354          * });
112355          *
112356          * var difference = turf.difference(polygon1, polygon2);
112357          *
112358          * //addToMap
112359          * var addToMap = [polygon1, polygon2, difference];
112360          */
112361         function difference(polygon1, polygon2) {
112362             var geom1 = getGeom(polygon1);
112363             var geom2 = getGeom(polygon2);
112364             var properties = polygon1.properties || {};
112365
112366             // Issue #721 - JSTS can't handle empty polygons
112367             geom1 = removeEmptyPolygon(geom1);
112368             geom2 = removeEmptyPolygon(geom2);
112369             if (!geom1) return null;
112370             if (!geom2) return feature$1(geom1, properties);
112371
112372             // JSTS difference operation
112373             var reader = new GeoJSONReader();
112374             var a = reader.read(geom1);
112375             var b = reader.read(geom2);
112376             var differenced = OverlayOp.difference(a, b);
112377             if (differenced.isEmpty()) return null;
112378             var writer = new GeoJSONWriter();
112379             var geom = writer.write(differenced);
112380
112381             return feature$1(geom, properties);
112382         }
112383
112384         /**
112385          * Detect Empty Polygon
112386          *
112387          * @private
112388          * @param {Geometry<Polygon|MultiPolygon>} geom Geometry Object
112389          * @returns {Geometry<Polygon|MultiPolygon>|null} removed any polygons with no areas
112390          */
112391         function removeEmptyPolygon(geom) {
112392             switch (geom.type) {
112393             case 'Polygon':
112394                 if (area(geom) > 1) return geom;
112395                 return null;
112396             case 'MultiPolygon':
112397                 var coordinates = [];
112398                 flattenEach(geom, function (feature$$1) {
112399                     if (area(feature$$1) > 1) coordinates.push(feature$$1.geometry.coordinates);
112400                 });
112401                 if (coordinates.length) return {type: 'MultiPolygon', coordinates: coordinates};
112402             }
112403         }
112404
112405         /**
112406          * Takes two or more {@link Polygon|polygons} and returns a combined polygon. If the input polygons are not contiguous, this function returns a {@link MultiPolygon} feature.
112407          *
112408          * @name union
112409          * @param {...Feature<Polygon>} A polygon to combine
112410          * @returns {Feature<(Polygon|MultiPolygon)>} a combined {@link Polygon} or {@link MultiPolygon} feature
112411          * @example
112412          * var poly1 = turf.polygon([[
112413          *     [-82.574787, 35.594087],
112414          *     [-82.574787, 35.615581],
112415          *     [-82.545261, 35.615581],
112416          *     [-82.545261, 35.594087],
112417          *     [-82.574787, 35.594087]
112418          * ]], {"fill": "#0f0"});
112419          * var poly2 = turf.polygon([[
112420          *     [-82.560024, 35.585153],
112421          *     [-82.560024, 35.602602],
112422          *     [-82.52964, 35.602602],
112423          *     [-82.52964, 35.585153],
112424          *     [-82.560024, 35.585153]
112425          * ]], {"fill": "#00f"});
112426          *
112427          * var union = turf.union(poly1, poly2);
112428          *
112429          * //addToMap
112430          * var addToMap = [poly1, poly2, union];
112431          */
112432         function union$1() {
112433             var reader = new GeoJSONReader();
112434             var result = reader.read(JSON.stringify(arguments[0].geometry));
112435
112436             for (var i = 1; i < arguments.length; i++) {
112437                 result = UnionOp.union(result, reader.read(JSON.stringify(arguments[i].geometry)));
112438             }
112439
112440             var writer = new GeoJSONWriter();
112441             result = writer.write(result);
112442
112443             return {
112444                 type: 'Feature',
112445                 geometry: result,
112446                 properties: arguments[0].properties
112447             };
112448         }
112449
112450         // Reduce an array of locations into a single GeoJSON feature
112451         function _locationReducer(accumulator, location) {
112452           /* eslint-disable no-console, no-invalid-this */
112453           let result;
112454           try {
112455             let resolved = this.resolveLocation(location);
112456             if (!resolved || !resolved.feature) {
112457               console.warn(`Warning:  Couldn't resolve location "${location}"`);
112458               return accumulator;
112459             }
112460             result = !accumulator ? resolved.feature : union$1(accumulator, resolved.feature);
112461           } catch (e) {
112462             console.warn(`Warning:  Error resolving location "${location}"`);
112463             console.warn(e);
112464             result = accumulator;
112465           }
112466
112467           return result;
112468           /* eslint-enable no-console, no-invalid-this */
112469         }
112470
112471
112472
112473         function _cloneDeep(obj) {
112474           return JSON.parse(JSON.stringify(obj));
112475         }
112476
112477
112478         class LocationConflation {
112479
112480           // constructor
112481           //
112482           // Optionally pass a GeoJSON FeatureCollection of known features which we can refer to later.
112483           // Each feature must have a filename-like `id`, for example: `something.geojson`
112484           //
112485           // {
112486           //   "type": "FeatureCollection"
112487           //   "features": [
112488           //     {
112489           //       "type": "Feature",
112490           //       "id": "philly_metro.geojson",
112491           //       "properties": { … },
112492           //       "geometry": { … }
112493           //     }
112494           //   ]
112495           // }
112496           constructor(fc) {
112497             this._cache = {};
112498
112499             // process input FeatureCollection
112500             if (fc && fc.type === 'FeatureCollection' && Array.isArray(fc.features)) {
112501               fc.features.forEach(feature => {
112502                 feature.properties = feature.properties || {};
112503                 let props = feature.properties;
112504
112505                 // get `id` from either `id` or `properties`
112506                 let id = feature.id || props.id;
112507                 if (!id || !/^\S+\.geojson$/i.test(id)) return;
112508
112509                 // ensure id exists and is lowercase
112510                 id = id.toLowerCase();
112511                 feature.id = id;
112512                 props.id = id;
112513
112514                 // ensure area property exists
112515                 if (!props.area) {
112516                   const area = geojsonArea.geometry(feature.geometry) / 1e6;  // m² to km²
112517                   props.area = Number(area.toFixed(2));
112518                 }
112519
112520                 this._cache[id] = feature;
112521               });
112522             }
112523
112524             // Replace CountryCoder world geometry to have a polygon covering the world.
112525             let world = _cloneDeep(feature('Q2'));
112526             world.geometry = {
112527               type: 'Polygon',
112528               coordinates: [[[-180, -90], [180, -90], [180, 90], [-180, 90], [-180, -90]]]
112529             };
112530             world.id = 'Q2';
112531             world.properties.id = 'Q2';
112532             world.properties.area = geojsonArea.geometry(world.geometry) / 1e6;  // m² to km²
112533             this._cache.Q2 = world;
112534           }
112535
112536
112537           // validateLocation
112538           //
112539           // Pass a `location` identifier
112540           // Returns a result like
112541           //   {
112542           //     type:     'point', 'geojson', or 'countrycoder'
112543           //     location:  the queried location
112544           //     id:        a unique identifier
112545           //   }
112546           //  or `null` if the location is invalid
112547           //
112548           validateLocation(location) {
112549             if (Array.isArray(location)) {   // a [lon,lat] coordinate pair?
112550               if (location.length === 2 && Number.isFinite(location[0]) && Number.isFinite(location[1]) &&
112551                 location[0] >= -180 && location[0] <= 180 && location[1] >= -90 && location[1] <= 90
112552               ) {
112553                 const id = '[' + location.toString() + ']';
112554                 return { type: 'point', location: location, id: id };
112555               }
112556
112557             } else if (typeof location === 'string' && /^\S+\.geojson$/i.test(location)) {   // a .geojson filename?
112558               const id = location.toLowerCase();
112559               if (this._cache[id]) {
112560                 return { type: 'geojson', location: location, id: id };
112561               }
112562
112563             } else if (typeof location === 'string' || typeof location === 'number') {   // a country-coder value?
112564               const feature$1 = feature(location);
112565               if (feature$1) {
112566                 // Use wikidata QID as the identifier, since that seems to be the only
112567                 // property that everything in CountryCoder is guaranteed to have.
112568                 const id = feature$1.properties.wikidata;
112569                 return { type: 'countrycoder', location: location, id: id };
112570               }
112571             }
112572
112573             return null;
112574           }
112575
112576
112577           // resolveLocation
112578           //
112579           // Pass a `location` identifier
112580           // Returns a result like
112581           //   {
112582           //     type:      'point', 'geojson', or 'countrycoder'
112583           //     location:  the queried location
112584           //     id:        a unique identifier
112585           //     feature:   the geojson feature
112586           //   }
112587           //  or `null` if the location is invalid
112588           //
112589           resolveLocation(location) {
112590             const valid = this.validateLocation(location);
112591             if (!valid) return null;
112592
112593             // return a result from cache if we can
112594             if (this._cache[valid.id]) {
112595               return Object.assign(valid, { feature: this._cache[valid.id] });
112596             }
112597
112598             // a [lon,lat] coordinate pair?
112599             if (valid.type === 'point') {
112600               const RADIUS = 25000;  // meters
112601               const EDGES = 10;
112602               const PRECISION = 3;
112603               const area = Math.PI * RADIUS * RADIUS / 1e6;     // m² to km²
112604               const feature = this._cache[valid.id] = geojsonPrecision({
112605                 type: 'Feature',
112606                 id: valid.id,
112607                 properties: { id: valid.id, area: Number(area.toFixed(2)) },
112608                 geometry: circleToPolygon(location, RADIUS, EDGES)
112609               }, PRECISION);
112610               return Object.assign(valid, { feature: feature });
112611
112612             // a .geojson filename?
112613             } else if (valid.type === 'geojson') ; else if (valid.type === 'countrycoder') {
112614               let feature$1 = _cloneDeep(feature(valid.id));
112615               let props = feature$1.properties;
112616
112617               // -> This block of code is weird and requires some explanation. <-
112618               // CountryCoder includes higher level features which are made up of members.
112619               // These features don't have their own geometry, but CountryCoder provides an
112620               //   `aggregateFeature` method to combine these members into a MultiPolygon.
112621               // BUT, when we try to actually work with these aggregated MultiPolygons,
112622               //   Turf/JSTS gets crashy because of topography bugs.
112623               // SO, we'll aggregate the features ourselves by unioning them together.
112624               // This approach also has the benefit of removing all the internal boaders and
112625               //   simplifying the regional polygons a lot.
112626               if (Array.isArray(props.members)) {
112627                 let seed = feature$1.geometry ? feature$1 : null;
112628                 let aggregate = props.members.reduce(_locationReducer.bind(this), seed);
112629                 feature$1.geometry = aggregate.geometry;
112630               }
112631
112632               // ensure area property exists
112633               if (!props.area) {
112634                 const area = geojsonArea.geometry(feature$1.geometry) / 1e6;  // m² to km²
112635                 props.area = Number(area.toFixed(2));
112636               }
112637
112638               // ensure id property exists
112639               feature$1.id = valid.id;
112640               props.id = valid.id;
112641
112642               this._cache[valid.id] = feature$1;
112643               return Object.assign(valid, { feature: feature$1 });
112644             }
112645
112646             return null;
112647           }
112648
112649
112650           // resolveLocationSet
112651           //
112652           // Pass a `locationSet` Object like:
112653           //   `{ include: [ Array ], exclude: [ Array ] }`
112654           // Returns a stable identifier string of the form:
112655           //   "+[included]-[excluded]"
112656           //
112657           resolveLocationSet(locationSet) {
112658             locationSet = locationSet || {};
112659             const resolve = this.resolveLocation.bind(this);
112660             let include = (locationSet.include || []).map(resolve).filter(Boolean);
112661             let exclude = (locationSet.exclude || []).map(resolve).filter(Boolean);
112662
112663             if (!include.length) {
112664               include = [resolve('Q2')];   // default to 'the world'
112665             }
112666
112667             // return quickly if it's a single included location..
112668             if (include.length === 1 && exclude.length === 0) {
112669               return include[0].feature;
112670             }
112671
112672             // generate stable identifier
112673             include.sort(sortFeatures);
112674             let id = '+[' + include.map(d => d.id).join(',') + ']';
112675             if (exclude.length) {
112676               exclude.sort(sortFeatures);
112677               id += '-[' + exclude.map(d => d.id).join(',') + ']';
112678             }
112679
112680             // return cached?
112681             if (this._cache[id]) {
112682               return this._cache[id];
112683             }
112684
112685             // calculate unions
112686             let includeGeoJSON = include.map(d => d.location).reduce(_locationReducer.bind(this), null);
112687             let excludeGeoJSON = exclude.map(d => d.location).reduce(_locationReducer.bind(this), null);
112688
112689             // calculate difference, update area and return result
112690             let resultGeoJSON = excludeGeoJSON ? difference(includeGeoJSON, excludeGeoJSON) : includeGeoJSON;
112691             const area = geojsonArea.geometry(resultGeoJSON.geometry) / 1e6;  // m² to km²
112692             resultGeoJSON.id = id;
112693             resultGeoJSON.properties = { id: id, area: Number(area.toFixed(2)) };
112694
112695             return this._cache[id] = resultGeoJSON;
112696
112697
112698             // Sorting the location lists is ok because they end up unioned together.
112699             // This sorting makes it possible to generate a deterministic id.
112700             function sortFeatures(a, b) {
112701               const rank = { countrycoder: 1, geojson: 2, point: 3 };
112702               const aRank = rank[a.type];
112703               const bRank = rank[b.type];
112704
112705               return (aRank > bRank) ? 1
112706                 : (aRank < bRank) ? -1
112707                 : a.id.localeCompare(b.id);
112708             }
112709           }
112710
112711
112712           cache() {
112713             return this._cache;
112714           }
112715         }
112716
112717         let _oci = null;
112718
112719         function uiSuccess(context) {
112720           const MAXEVENTS = 2;
112721           const dispatch$1 = dispatch('cancel');
112722           let _changeset;
112723           let _location;
112724           ensureOSMCommunityIndex();   // start fetching the data
112725
112726
112727           function ensureOSMCommunityIndex() {
112728             const data = _mainFileFetcher;
112729             return Promise.all([ data.get('oci_resources'), data.get('oci_features') ])
112730               .then(vals => {
112731                 if (_oci) return _oci;
112732
112733                 const ociResources = vals[0].resources;
112734                 const loco = new LocationConflation(vals[1]);
112735                 let ociFeatures = {};
112736
112737                 Object.values(ociResources).forEach(resource => {
112738                   const feature = loco.resolveLocationSet(resource.locationSet);
112739                   let ociFeature = ociFeatures[feature.id];
112740                   if (!ociFeature) {
112741                     ociFeature = JSON.parse(JSON.stringify(feature));  // deep clone
112742                     ociFeature.properties.resourceIDs = new Set();
112743                     ociFeatures[feature.id] = ociFeature;
112744                   }
112745                   ociFeature.properties.resourceIDs.add(resource.id);
112746                 });
112747
112748                 return _oci = {
112749                   features: ociFeatures,
112750                   resources: ociResources,
112751                   query: whichPolygon_1({ type: 'FeatureCollection', features: Object.values(ociFeatures) })
112752                 };
112753               });
112754           }
112755
112756
112757           // string-to-date parsing in JavaScript is weird
112758           function parseEventDate(when) {
112759             if (!when) return;
112760
112761             let raw = when.trim();
112762             if (!raw) return;
112763
112764             if (!/Z$/.test(raw)) {   // if no trailing 'Z', add one
112765               raw += 'Z';            // this forces date to be parsed as a UTC date
112766             }
112767
112768             const parsed = new Date(raw);
112769             return new Date(parsed.toUTCString().substr(0, 25));  // convert to local timezone
112770           }
112771
112772
112773           function success(selection) {
112774             let header = selection
112775               .append('div')
112776               .attr('class', 'header fillL');
112777
112778             header
112779               .append('h3')
112780               .text(_t('success.just_edited'));
112781
112782             header
112783               .append('button')
112784               .attr('class', 'close')
112785               .on('click', () => dispatch$1.call('cancel'))
112786               .call(svgIcon('#iD-icon-close'));
112787
112788             let body = selection
112789               .append('div')
112790               .attr('class', 'body save-success fillL');
112791
112792             let summary = body
112793               .append('div')
112794               .attr('class', 'save-summary');
112795
112796             summary
112797               .append('h3')
112798               .text(_t('success.thank_you' + (_location ? '_location' : ''), { where: _location }));
112799
112800             summary
112801               .append('p')
112802               .text(_t('success.help_html'))
112803               .append('a')
112804               .attr('class', 'link-out')
112805               .attr('target', '_blank')
112806               .attr('tabindex', -1)
112807               .attr('href', _t('success.help_link_url'))
112808               .call(svgIcon('#iD-icon-out-link', 'inline'))
112809               .append('span')
112810               .text(_t('success.help_link_text'));
112811
112812             let osm = context.connection();
112813             if (!osm) return;
112814
112815             let changesetURL = osm.changesetURL(_changeset.id);
112816
112817             let table = summary
112818               .append('table')
112819               .attr('class', 'summary-table');
112820
112821             let row = table
112822               .append('tr')
112823               .attr('class', 'summary-row');
112824
112825             row
112826               .append('td')
112827               .attr('class', 'cell-icon summary-icon')
112828               .append('a')
112829               .attr('target', '_blank')
112830               .attr('href', changesetURL)
112831               .append('svg')
112832               .attr('class', 'logo-small')
112833               .append('use')
112834               .attr('xlink:href', '#iD-logo-osm');
112835
112836             let summaryDetail = row
112837               .append('td')
112838               .attr('class', 'cell-detail summary-detail');
112839
112840             summaryDetail
112841               .append('a')
112842               .attr('class', 'cell-detail summary-view-on-osm')
112843               .attr('target', '_blank')
112844               .attr('href', changesetURL)
112845               .text(_t('success.view_on_osm'));
112846
112847             summaryDetail
112848               .append('div')
112849               .html(_t('success.changeset_id', {
112850                 changeset_id: `<a href="${changesetURL}" target="_blank">${_changeset.id}</a>`
112851               }));
112852
112853
112854             // Get OSM community index features intersecting the map..
112855             ensureOSMCommunityIndex()
112856               .then(oci => {
112857                 let communities = [];
112858                 const properties = oci.query(context.map().center(), true) || [];
112859
112860                 // Gather the communities from the result
112861                 properties.forEach(props => {
112862                   const resourceIDs = Array.from(props.resourceIDs);
112863                   resourceIDs.forEach(resourceID => {
112864                     const resource = oci.resources[resourceID];
112865                     communities.push({
112866                       area: props.area || Infinity,
112867                       order: resource.order || 0,
112868                       resource: resource
112869                     });
112870                   });
112871                 });
112872
112873                 // sort communities by feature area ascending, community order descending
112874                 communities.sort((a, b) => a.area - b.area || b.order - a.order);
112875
112876                 body
112877                   .call(showCommunityLinks, communities.map(c => c.resource));
112878               });
112879           }
112880
112881
112882           function showCommunityLinks(selection, resources) {
112883             let communityLinks = selection
112884               .append('div')
112885               .attr('class', 'save-communityLinks');
112886
112887             communityLinks
112888               .append('h3')
112889               .text(_t('success.like_osm'));
112890
112891             let table = communityLinks
112892               .append('table')
112893               .attr('class', 'community-table');
112894
112895             let row = table.selectAll('.community-row')
112896               .data(resources);
112897
112898             let rowEnter = row.enter()
112899               .append('tr')
112900               .attr('class', 'community-row');
112901
112902             rowEnter
112903               .append('td')
112904               .attr('class', 'cell-icon community-icon')
112905               .append('a')
112906               .attr('target', '_blank')
112907               .attr('href', d => d.url)
112908               .append('svg')
112909               .attr('class', 'logo-small')
112910               .append('use')
112911               .attr('xlink:href', d => `#community-${d.type}`);
112912
112913             let communityDetail = rowEnter
112914               .append('td')
112915               .attr('class', 'cell-detail community-detail');
112916
112917             communityDetail
112918               .each(showCommunityDetails);
112919
112920             communityLinks
112921               .append('div')
112922               .attr('class', 'community-missing')
112923               .text(_t('success.missing'))
112924               .append('a')
112925               .attr('class', 'link-out')
112926               .attr('target', '_blank')
112927               .attr('tabindex', -1)
112928               .call(svgIcon('#iD-icon-out-link', 'inline'))
112929               .attr('href', 'https://github.com/osmlab/osm-community-index/issues')
112930               .append('span')
112931               .text(_t('success.tell_us'));
112932           }
112933
112934
112935           function showCommunityDetails(d) {
112936             let selection = select(this);
112937             let communityID = d.id;
112938             let replacements = {
112939               url: linkify(d.url),
112940               signupUrl: linkify(d.signupUrl || d.url)
112941             };
112942
112943             selection
112944               .append('div')
112945               .attr('class', 'community-name')
112946               .append('a')
112947               .attr('target', '_blank')
112948               .attr('href', d.url)
112949               .text(_t(`community.${d.id}.name`));
112950
112951             let descriptionHTML = _t(`community.${d.id}.description`, replacements);
112952
112953             if (d.type === 'reddit') {   // linkify subreddits  #4997
112954               descriptionHTML = descriptionHTML
112955                 .replace(/(\/r\/\w*\/*)/i, match => linkify(d.url, match));
112956             }
112957
112958             selection
112959               .append('div')
112960               .attr('class', 'community-description')
112961               .html(descriptionHTML);
112962
112963             if (d.extendedDescription || (d.languageCodes && d.languageCodes.length)) {
112964               selection
112965                 .append('div')
112966                 .call(uiDisclosure(context, `community-more-${d.id}`, false)
112967                   .expanded(false)
112968                   .updatePreference(false)
112969                   .title(_t('success.more'))
112970                   .content(showMore)
112971                 );
112972             }
112973
112974             let nextEvents = (d.events || [])
112975               .map(event => {
112976                 event.date = parseEventDate(event.when);
112977                 return event;
112978               })
112979               .filter(event => {      // date is valid and future (or today)
112980                 const t = event.date.getTime();
112981                 const now = (new Date()).setHours(0,0,0,0);
112982                 return !isNaN(t) && t >= now;
112983               })
112984               .sort((a, b) => {       // sort by date ascending
112985                 return a.date < b.date ? -1 : a.date > b.date ? 1 : 0;
112986               })
112987               .slice(0, MAXEVENTS);   // limit number of events shown
112988
112989             if (nextEvents.length) {
112990               selection
112991                 .append('div')
112992                 .call(uiDisclosure(context, `community-events-${d.id}`, false)
112993                   .expanded(false)
112994                   .updatePreference(false)
112995                   .title(_t('success.events'))
112996                   .content(showNextEvents)
112997                 )
112998                 .select('.hide-toggle')
112999                 .append('span')
113000                 .attr('class', 'badge-text')
113001                 .text(nextEvents.length);
113002             }
113003
113004
113005             function showMore(selection) {
113006               let more = selection.selectAll('.community-more')
113007                 .data([0]);
113008
113009               let moreEnter = more.enter()
113010                 .append('div')
113011                 .attr('class', 'community-more');
113012
113013               if (d.extendedDescription) {
113014                 moreEnter
113015                   .append('div')
113016                   .attr('class', 'community-extended-description')
113017                   .html(_t(`community.${d.id}.extendedDescription`, replacements));
113018               }
113019
113020               if (d.languageCodes && d.languageCodes.length) {
113021                 const languageList = d.languageCodes
113022                   .map(code => _mainLocalizer.languageName(code))
113023                   .join(', ');
113024
113025                 moreEnter
113026                   .append('div')
113027                   .attr('class', 'community-languages')
113028                   .text(_t('success.languages', { languages: languageList }));
113029               }
113030             }
113031
113032
113033             function showNextEvents(selection) {
113034               let events = selection
113035                 .append('div')
113036                 .attr('class', 'community-events');
113037
113038               let item = events.selectAll('.community-event')
113039                 .data(nextEvents);
113040
113041               let itemEnter = item.enter()
113042                 .append('div')
113043                 .attr('class', 'community-event');
113044
113045               itemEnter
113046                 .append('div')
113047                 .attr('class', 'community-event-name')
113048                 .append('a')
113049                 .attr('target', '_blank')
113050                 .attr('href', d => d.url)
113051                 .text(d => {
113052                   let name = d.name;
113053                   if (d.i18n && d.id) {
113054                     name = _t(`community.${communityID}.events.${d.id}.name`, { default: name });
113055                   }
113056                   return name;
113057                 });
113058
113059               itemEnter
113060                 .append('div')
113061                 .attr('class', 'community-event-when')
113062                 .text(d => {
113063                   let options = { weekday: 'short', day: 'numeric', month: 'short', year: 'numeric' };
113064                   if (d.date.getHours() || d.date.getMinutes()) {   // include time if it has one
113065                     options.hour = 'numeric';
113066                     options.minute = 'numeric';
113067                   }
113068                   return d.date.toLocaleString(_mainLocalizer.localeCode(), options);
113069                 });
113070
113071               itemEnter
113072                 .append('div')
113073                 .attr('class', 'community-event-where')
113074                 .text(d => {
113075                   let where = d.where;
113076                   if (d.i18n && d.id) {
113077                     where = _t(`community.${communityID}.events.${d.id}.where`, { default: where });
113078                   }
113079                   return where;
113080                 });
113081
113082               itemEnter
113083                 .append('div')
113084                 .attr('class', 'community-event-description')
113085                 .text(d => {
113086                   let description = d.description;
113087                   if (d.i18n && d.id) {
113088                     description = _t(`community.${communityID}.events.${d.id}.description`, { default: description });
113089                   }
113090                   return description;
113091                 });
113092             }
113093
113094
113095             function linkify(url, text) {
113096               text = text || url;
113097               return `<a target="_blank" href="${url}">${text}</a>`;
113098             }
113099           }
113100
113101
113102           success.changeset = function(val) {
113103             if (!arguments.length) return _changeset;
113104             _changeset = val;
113105             return success;
113106           };
113107
113108
113109           success.location = function(val) {
113110             if (!arguments.length) return _location;
113111             _location = val;
113112             return success;
113113           };
113114
113115
113116           return utilRebind(success, dispatch$1, 'on');
113117         }
113118
113119         function modeSave(context) {
113120             var mode = { id: 'save' };
113121             var keybinding = utilKeybinding('modeSave');
113122
113123             var commit = uiCommit(context)
113124                 .on('cancel', cancel);
113125             var _conflictsUi; // uiConflicts
113126
113127             var _location;
113128             var _success;
113129
113130             var uploader = context.uploader()
113131                 .on('saveStarted.modeSave', function() {
113132                     keybindingOff();
113133                 })
113134                 // fire off some async work that we want to be ready later
113135                 .on('willAttemptUpload.modeSave', prepareForSuccess)
113136                 .on('progressChanged.modeSave', showProgress)
113137                 .on('resultNoChanges.modeSave', function() {
113138                     cancel();
113139                 })
113140                 .on('resultErrors.modeSave', showErrors)
113141                 .on('resultConflicts.modeSave', showConflicts)
113142                 .on('resultSuccess.modeSave', showSuccess);
113143
113144
113145             function cancel() {
113146                 context.enter(modeBrowse(context));
113147             }
113148
113149
113150             function showProgress(num, total) {
113151                 var modal = context.container().select('.loading-modal .modal-section');
113152                 var progress = modal.selectAll('.progress')
113153                     .data([0]);
113154
113155                 // enter/update
113156                 progress.enter()
113157                     .append('div')
113158                     .attr('class', 'progress')
113159                     .merge(progress)
113160                     .text(_t('save.conflict_progress', { num: num, total: total }));
113161             }
113162
113163
113164             function showConflicts(changeset, conflicts, origChanges) {
113165
113166                 var selection = context.container()
113167                     .select('.sidebar')
113168                     .append('div')
113169                     .attr('class','sidebar-component');
113170
113171                 context.container().selectAll('.main-content')
113172                     .classed('active', true)
113173                     .classed('inactive', false);
113174
113175                 _conflictsUi = uiConflicts(context)
113176                     .conflictList(conflicts)
113177                     .origChanges(origChanges)
113178                     .on('cancel', function() {
113179                         context.container().selectAll('.main-content')
113180                             .classed('active', false)
113181                             .classed('inactive', true);
113182                         selection.remove();
113183                         keybindingOn();
113184
113185                         uploader.cancelConflictResolution();
113186                     })
113187                     .on('save', function() {
113188                         context.container().selectAll('.main-content')
113189                             .classed('active', false)
113190                             .classed('inactive', true);
113191                         selection.remove();
113192
113193                         uploader.processResolvedConflicts(changeset);
113194                     });
113195
113196                 selection.call(_conflictsUi);
113197             }
113198
113199
113200             function showErrors(errors) {
113201                 keybindingOn();
113202
113203                 var selection = uiConfirm(context.container());
113204                 selection
113205                     .select('.modal-section.header')
113206                     .append('h3')
113207                     .text(_t('save.error'));
113208
113209                 addErrors(selection, errors);
113210                 selection.okButton();
113211             }
113212
113213
113214             function addErrors(selection, data) {
113215                 var message = selection
113216                     .select('.modal-section.message-text');
113217
113218                 var items = message
113219                     .selectAll('.error-container')
113220                     .data(data);
113221
113222                 var enter = items.enter()
113223                     .append('div')
113224                     .attr('class', 'error-container');
113225
113226                 enter
113227                     .append('a')
113228                     .attr('class', 'error-description')
113229                     .attr('href', '#')
113230                     .classed('hide-toggle', true)
113231                     .text(function(d) { return d.msg || _t('save.unknown_error_details'); })
113232                     .on('click', function() {
113233                         event.preventDefault();
113234
113235                         var error = select(this);
113236                         var detail = select(this.nextElementSibling);
113237                         var exp = error.classed('expanded');
113238
113239                         detail.style('display', exp ? 'none' : 'block');
113240                         error.classed('expanded', !exp);
113241                     });
113242
113243                 var details = enter
113244                     .append('div')
113245                     .attr('class', 'error-detail-container')
113246                     .style('display', 'none');
113247
113248                 details
113249                     .append('ul')
113250                     .attr('class', 'error-detail-list')
113251                     .selectAll('li')
113252                     .data(function(d) { return d.details || []; })
113253                     .enter()
113254                     .append('li')
113255                     .attr('class', 'error-detail-item')
113256                     .text(function(d) { return d; });
113257
113258                 items.exit()
113259                     .remove();
113260             }
113261
113262
113263             function showSuccess(changeset) {
113264                 commit.reset();
113265
113266                 var ui = _success
113267                     .changeset(changeset)
113268                     .location(_location)
113269                     .on('cancel', function() { context.ui().sidebar.hide(); });
113270
113271                 context.enter(modeBrowse(context).sidebar(ui));
113272             }
113273
113274
113275             function keybindingOn() {
113276                 select(document)
113277                     .call(keybinding.on('⎋', cancel, true));
113278             }
113279
113280
113281             function keybindingOff() {
113282                 select(document)
113283                     .call(keybinding.unbind);
113284             }
113285
113286
113287             // Reverse geocode current map location so we can display a message on
113288             // the success screen like "Thank you for editing around place, region."
113289             function prepareForSuccess() {
113290                 _success = uiSuccess(context);
113291                 _location = null;
113292                 if (!services.geocoder) return;
113293
113294                 services.geocoder.reverse(context.map().center(), function(err, result) {
113295                     if (err || !result || !result.address) return;
113296
113297                     var addr = result.address;
113298                     var place = (addr && (addr.town || addr.city || addr.county)) || '';
113299                     var region = (addr && (addr.state || addr.country)) || '';
113300                     var separator = (place && region) ? _t('success.thank_you_where.separator') : '';
113301
113302                     _location = _t('success.thank_you_where.format',
113303                         { place: place, separator: separator, region: region }
113304                     );
113305                 });
113306             }
113307
113308
113309             mode.selectedIDs = function() {
113310                 return _conflictsUi ? _conflictsUi.shownEntityIds() : [];
113311             };
113312
113313
113314             mode.enter = function() {
113315                 // Show sidebar
113316                 context.ui().sidebar.expand();
113317
113318                 function done() {
113319                     context.ui().sidebar.show(commit);
113320                 }
113321
113322                 keybindingOn();
113323
113324                 context.container().selectAll('.main-content')
113325                     .classed('active', false)
113326                     .classed('inactive', true);
113327
113328                 var osm = context.connection();
113329                 if (!osm) {
113330                     cancel();
113331                     return;
113332                 }
113333
113334                 if (osm.authenticated()) {
113335                     done();
113336                 } else {
113337                     osm.authenticate(function(err) {
113338                         if (err) {
113339                             cancel();
113340                         } else {
113341                             done();
113342                         }
113343                     });
113344                 }
113345             };
113346
113347
113348             mode.exit = function() {
113349
113350                 keybindingOff();
113351
113352                 context.container().selectAll('.main-content')
113353                     .classed('active', true)
113354                     .classed('inactive', false);
113355
113356                 context.ui().sidebar.hide();
113357             };
113358
113359             return mode;
113360         }
113361
113362         function uiToolOldDrawModes(context) {
113363
113364             var tool = {
113365                 id: 'old_modes',
113366                 label: _t('toolbar.add_feature')
113367             };
113368
113369             var modes = [
113370                 modeAddPoint(context, {
113371                     title: _t('modes.add_point.title'),
113372                     button: 'point',
113373                     description: _t('modes.add_point.description'),
113374                     preset: _mainPresetIndex.item('point'),
113375                     key: '1'
113376                 }),
113377                 modeAddLine(context, {
113378                     title: _t('modes.add_line.title'),
113379                     button: 'line',
113380                     description: _t('modes.add_line.description'),
113381                     preset: _mainPresetIndex.item('line'),
113382                     key: '2'
113383                 }),
113384                 modeAddArea(context, {
113385                     title: _t('modes.add_area.title'),
113386                     button: 'area',
113387                     description: _t('modes.add_area.description'),
113388                     preset: _mainPresetIndex.item('area'),
113389                     key: '3'
113390                 })
113391             ];
113392
113393
113394             function enabled() {
113395                 return osmEditable();
113396             }
113397
113398             function osmEditable() {
113399                 return context.editable();
113400             }
113401
113402             modes.forEach(function(mode) {
113403                 context.keybinding().on(mode.key, function() {
113404                     if (!enabled()) return;
113405
113406                     if (mode.id === context.mode().id) {
113407                         context.enter(modeBrowse(context));
113408                     } else {
113409                         context.enter(mode);
113410                     }
113411                 });
113412             });
113413
113414             tool.render = function(selection) {
113415
113416                 var wrap = selection
113417                     .append('div')
113418                     .attr('class', 'joined')
113419                     .style('display', 'flex');
113420
113421                 var debouncedUpdate = debounce(update, 500, { leading: true, trailing: true });
113422
113423                 context.map()
113424                     .on('move.modes', debouncedUpdate)
113425                     .on('drawn.modes', debouncedUpdate);
113426
113427                 context
113428                     .on('enter.modes', update);
113429
113430                 update();
113431
113432
113433                 function update() {
113434
113435                     var buttons = wrap.selectAll('button.add-button')
113436                         .data(modes, function(d) { return d.id; });
113437
113438                     // exit
113439                     buttons.exit()
113440                         .remove();
113441
113442                     // enter
113443                     var buttonsEnter = buttons.enter()
113444                         .append('button')
113445                         .attr('class', function(d) { return d.id + ' add-button bar-button'; })
113446                         .on('click.mode-buttons', function(d) {
113447                             if (!enabled()) return;
113448
113449                             // When drawing, ignore accidental clicks on mode buttons - #4042
113450                             var currMode = context.mode().id;
113451                             if (/^draw/.test(currMode)) return;
113452
113453                             if (d.id === currMode) {
113454                                 context.enter(modeBrowse(context));
113455                             } else {
113456                                 context.enter(d);
113457                             }
113458                         })
113459                         .call(uiTooltip()
113460                             .placement('bottom')
113461                             .title(function(d) { return d.description; })
113462                             .keys(function(d) { return [d.key]; })
113463                             .scrollContainer(context.container().select('.top-toolbar'))
113464                         );
113465
113466                     buttonsEnter
113467                         .each(function(d) {
113468                             select(this)
113469                                 .call(svgIcon('#iD-icon-' + d.button));
113470                         });
113471
113472                     buttonsEnter
113473                         .append('span')
113474                         .attr('class', 'label')
113475                         .text(function(mode) { return mode.title; });
113476
113477                     // if we are adding/removing the buttons, check if toolbar has overflowed
113478                     if (buttons.enter().size() || buttons.exit().size()) {
113479                         context.ui().checkOverflow('.top-toolbar', true);
113480                     }
113481
113482                     // update
113483                     buttons = buttons
113484                         .merge(buttonsEnter)
113485                         .classed('disabled', function(d) { return !enabled(); })
113486                         .classed('active', function(d) { return context.mode() && context.mode().button === d.button; });
113487                 }
113488             };
113489
113490             return tool;
113491         }
113492
113493         function uiToolNotes(context) {
113494
113495             var tool = {
113496                 id: 'notes',
113497                 label: _t('modes.add_note.label')
113498             };
113499
113500             var mode = modeAddNote(context);
113501
113502             function enabled() {
113503                 return notesEnabled() && notesEditable();
113504             }
113505
113506             function notesEnabled() {
113507                 var noteLayer = context.layers().layer('notes');
113508                 return noteLayer && noteLayer.enabled();
113509             }
113510
113511             function notesEditable() {
113512                 var mode = context.mode();
113513                 return context.map().notesEditable() && mode && mode.id !== 'save';
113514             }
113515
113516             context.keybinding().on(mode.key, function() {
113517                 if (!enabled()) return;
113518
113519                 if (mode.id === context.mode().id) {
113520                     context.enter(modeBrowse(context));
113521                 } else {
113522                     context.enter(mode);
113523                 }
113524             });
113525
113526             tool.render = function(selection) {
113527
113528
113529                 var debouncedUpdate = debounce(update, 500, { leading: true, trailing: true });
113530
113531                 context.map()
113532                     .on('move.notes', debouncedUpdate)
113533                     .on('drawn.notes', debouncedUpdate);
113534
113535                 context
113536                     .on('enter.notes', update);
113537
113538                 update();
113539
113540
113541                 function update() {
113542                     var showNotes = notesEnabled();
113543                     var data = showNotes ? [mode] : [];
113544
113545                     var buttons = selection.selectAll('button.add-button')
113546                         .data(data, function(d) { return d.id; });
113547
113548                     // exit
113549                     buttons.exit()
113550                         .remove();
113551
113552                     // enter
113553                     var buttonsEnter = buttons.enter()
113554                         .append('button')
113555                         .attr('tabindex', -1)
113556                         .attr('class', function(d) { return d.id + ' add-button bar-button'; })
113557                         .on('click.notes', function(d) {
113558                             if (!enabled()) return;
113559
113560                             // When drawing, ignore accidental clicks on mode buttons - #4042
113561                             var currMode = context.mode().id;
113562                             if (/^draw/.test(currMode)) return;
113563
113564                             if (d.id === currMode) {
113565                                 context.enter(modeBrowse(context));
113566                             } else {
113567                                 context.enter(d);
113568                             }
113569                         })
113570                         .call(uiTooltip()
113571                             .placement('bottom')
113572                             .title(function(d) { return d.description; })
113573                             .keys(function(d) { return [d.key]; })
113574                             .scrollContainer(context.container().select('.top-toolbar'))
113575                         );
113576
113577                     buttonsEnter
113578                         .each(function(d) {
113579                             select(this)
113580                                 .call(svgIcon(d.icon || '#iD-icon-' + d.button));
113581                         });
113582
113583                     // if we are adding/removing the buttons, check if toolbar has overflowed
113584                     if (buttons.enter().size() || buttons.exit().size()) {
113585                         context.ui().checkOverflow('.top-toolbar', true);
113586                     }
113587
113588                     // update
113589                     buttons = buttons
113590                         .merge(buttonsEnter)
113591                         .classed('disabled', function(d) { return !enabled(); })
113592                         .classed('active', function(d) { return context.mode() && context.mode().button === d.button; });
113593                 }
113594             };
113595
113596             tool.uninstall = function() {
113597                 context
113598                     .on('enter.editor.notes', null)
113599                     .on('exit.editor.notes', null)
113600                     .on('enter.notes', null);
113601
113602                 context.map()
113603                     .on('move.notes', null)
113604                     .on('drawn.notes', null);
113605             };
113606
113607             return tool;
113608         }
113609
113610         function uiToolSave(context) {
113611
113612             var tool = {
113613                 id: 'save',
113614                 label: _t('save.title')
113615             };
113616
113617             var button = null;
113618             var tooltipBehavior = null;
113619             var history = context.history();
113620             var key = uiCmd('⌘S');
113621             var _numChanges = 0;
113622
113623             function isSaving() {
113624                 var mode = context.mode();
113625                 return mode && mode.id === 'save';
113626             }
113627
113628             function isDisabled() {
113629                 return _numChanges === 0 || isSaving();
113630             }
113631
113632             function save() {
113633                 event.preventDefault();
113634                 if (!context.inIntro() && !isSaving() && history.hasChanges()) {
113635                     context.enter(modeSave(context));
113636                 }
113637             }
113638
113639             function bgColor() {
113640                 var step;
113641                 if (_numChanges === 0) {
113642                     return null;
113643                 } else if (_numChanges <= 50) {
113644                     step = _numChanges / 50;
113645                     return d3_interpolateRgb('#fff', '#ff8')(step);  // white -> yellow
113646                 } else {
113647                     step = Math.min((_numChanges - 50) / 50, 1.0);
113648                     return d3_interpolateRgb('#ff8', '#f88')(step);  // yellow -> red
113649                 }
113650             }
113651
113652             function updateCount() {
113653                 var val = history.difference().summary().length;
113654                 if (val === _numChanges) return;
113655
113656                 _numChanges = val;
113657
113658                 if (tooltipBehavior) {
113659                     tooltipBehavior
113660                         .title(_t(_numChanges > 0 ? 'save.help' : 'save.no_changes'))
113661                         .keys([key]);
113662                 }
113663
113664                 if (button) {
113665                     button
113666                         .classed('disabled', isDisabled())
113667                         .style('background', bgColor());
113668
113669                     button.select('span.count')
113670                         .text(_numChanges);
113671                 }
113672             }
113673
113674
113675             tool.render = function(selection) {
113676                 tooltipBehavior = uiTooltip()
113677                     .placement('bottom')
113678                     .title(_t('save.no_changes'))
113679                     .keys([key])
113680                     .scrollContainer(context.container().select('.top-toolbar'));
113681
113682                 var lastPointerUpType;
113683
113684                 button = selection
113685                     .append('button')
113686                     .attr('class', 'save disabled bar-button')
113687                     .on('pointerup', function() {
113688                         lastPointerUpType = event.pointerType;
113689                     })
113690                     .on('click', function() {
113691                         event.preventDefault();
113692
113693                         save();
113694
113695                         if (_numChanges === 0 && (
113696                             lastPointerUpType === 'touch' ||
113697                             lastPointerUpType === 'pen')
113698                         ) {
113699                             // there are no tooltips for touch interactions so flash feedback instead
113700                             context.ui().flash
113701                                 .duration(2000)
113702                                 .iconName('#iD-icon-save')
113703                                 .iconClass('disabled')
113704                                 .text(_t('save.no_changes'))();
113705                         }
113706                         lastPointerUpType = null;
113707                     })
113708                     .call(tooltipBehavior);
113709
113710                 button
113711                     .call(svgIcon('#iD-icon-save'));
113712
113713                 button
113714                     .append('span')
113715                     .attr('class', 'count')
113716                     .attr('aria-hidden', 'true')
113717                     .text('0');
113718
113719                 updateCount();
113720
113721
113722                 context.keybinding()
113723                     .on(key, save, true);
113724
113725
113726                 context.history()
113727                     .on('change.save', updateCount);
113728
113729                 context
113730                     .on('enter.save', function() {
113731                         if (button) {
113732                             button
113733                                 .classed('disabled', isDisabled());
113734
113735                             if (isSaving()) {
113736                                 button.call(tooltipBehavior.hide);
113737                             }
113738                         }
113739                     });
113740             };
113741
113742
113743             tool.uninstall = function() {
113744                 context.keybinding()
113745                     .off(key, true);
113746
113747                 context.history()
113748                     .on('change.save', null);
113749
113750                 context
113751                     .on('enter.save', null);
113752
113753                 button = null;
113754                 tooltipBehavior = null;
113755             };
113756
113757             return tool;
113758         }
113759
113760         function uiToolSidebarToggle(context) {
113761
113762             var tool = {
113763                 id: 'sidebar_toggle',
113764                 label: _t('toolbar.inspect')
113765             };
113766
113767             tool.render = function(selection) {
113768                 selection
113769                     .append('button')
113770                     .attr('class', 'bar-button')
113771                     .on('click', function() {
113772                         context.ui().sidebar.toggle();
113773                     })
113774                     .call(uiTooltip()
113775                         .placement('bottom')
113776                         .title(_t('sidebar.tooltip'))
113777                         .keys([_t('sidebar.key')])
113778                         .scrollContainer(context.container().select('.top-toolbar'))
113779                     )
113780                     .call(svgIcon('#iD-icon-sidebar-' + (_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left')));
113781             };
113782
113783             return tool;
113784         }
113785
113786         function uiToolUndoRedo(context) {
113787
113788             var tool = {
113789                 id: 'undo_redo',
113790                 label: _t('toolbar.undo_redo')
113791             };
113792
113793             var commands = [{
113794                 id: 'undo',
113795                 cmd: uiCmd('⌘Z'),
113796                 action: function() {
113797                     context.undo();
113798                 },
113799                 annotation: function() {
113800                     return context.history().undoAnnotation();
113801                 },
113802                 icon: 'iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo')
113803             }, {
113804                 id: 'redo',
113805                 cmd: uiCmd('⌘⇧Z'),
113806                 action: function() {
113807                     context.redo();
113808                 },
113809                 annotation: function() {
113810                     return context.history().redoAnnotation();
113811                 },
113812                 icon: 'iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'undo' : 'redo')
113813             }];
113814
113815
113816             function editable() {
113817                 return context.mode() && context.mode().id !== 'save' && context.map().editableDataEnabled(true /* ignore min zoom */);
113818             }
113819
113820
113821             tool.render = function(selection) {
113822                 var tooltipBehavior = uiTooltip()
113823                     .placement('bottom')
113824                     .title(function (d) {
113825                         return d.annotation() ?
113826                             _t(d.id + '.tooltip', { action: d.annotation() }) :
113827                             _t(d.id + '.nothing');
113828                     })
113829                     .keys(function(d) {
113830                         return [d.cmd];
113831                     })
113832                     .scrollContainer(context.container().select('.top-toolbar'));
113833
113834                 var lastPointerUpType;
113835
113836                 var buttons = selection.selectAll('button')
113837                     .data(commands)
113838                     .enter()
113839                     .append('button')
113840                     .attr('class', function(d) { return 'disabled ' + d.id + '-button bar-button'; })
113841                     .on('pointerup', function() {
113842                         // `pointerup` is always called before `click`
113843                         lastPointerUpType = event.pointerType;
113844                     })
113845                     .on('click', function(d) {
113846                         event.preventDefault();
113847
113848                         var annotation = d.annotation();
113849
113850                         if (editable() && annotation) {
113851                             d.action();
113852                         }
113853
113854                         if (editable() && (
113855                             lastPointerUpType === 'touch' ||
113856                             lastPointerUpType === 'pen')
113857                         ) {
113858                             // there are no tooltips for touch interactions so flash feedback instead
113859
113860                             var text = annotation ?
113861                                 _t(d.id + '.tooltip', { action: annotation }) :
113862                                 _t(d.id + '.nothing');
113863                             context.ui().flash
113864                                 .duration(2000)
113865                                 .iconName('#' + d.icon)
113866                                 .iconClass(annotation ? '' : 'disabled')
113867                                 .text(text)();
113868                         }
113869                         lastPointerUpType = null;
113870                     })
113871                     .call(tooltipBehavior);
113872
113873                 buttons.each(function(d) {
113874                     select(this)
113875                         .call(svgIcon('#' + d.icon));
113876                 });
113877
113878                 context.keybinding()
113879                     .on(commands[0].cmd, function() {
113880                         event.preventDefault();
113881                         if (editable()) commands[0].action();
113882                     })
113883                     .on(commands[1].cmd, function() {
113884                         event.preventDefault();
113885                         if (editable()) commands[1].action();
113886                     });
113887
113888
113889                 var debouncedUpdate = debounce(update, 500, { leading: true, trailing: true });
113890
113891                 context.map()
113892                     .on('move.undo_redo', debouncedUpdate)
113893                     .on('drawn.undo_redo', debouncedUpdate);
113894
113895                 context.history()
113896                     .on('change.undo_redo', function(difference) {
113897                         if (difference) update();
113898                     });
113899
113900                 context
113901                     .on('enter.undo_redo', update);
113902
113903
113904                 function update() {
113905                     buttons
113906                         .classed('disabled', function(d) {
113907                             return !editable() || !d.annotation();
113908                         })
113909                         .each(function() {
113910                             var selection = select(this);
113911                             if (!selection.select('.tooltip.in').empty()) {
113912                                 selection.call(tooltipBehavior.updateContent);
113913                             }
113914                         });
113915                 }
113916             };
113917
113918             tool.uninstall = function() {
113919                 context.keybinding()
113920                     .off(commands[0].cmd)
113921                     .off(commands[1].cmd);
113922
113923                 context.map()
113924                     .on('move.undo_redo', null)
113925                     .on('drawn.undo_redo', null);
113926
113927                 context.history()
113928                     .on('change.undo_redo', null);
113929
113930                 context
113931                     .on('enter.undo_redo', null);
113932             };
113933
113934             return tool;
113935         }
113936
113937         function uiTopToolbar(context) {
113938
113939             var sidebarToggle = uiToolSidebarToggle(context),
113940                 modes = uiToolOldDrawModes(context),
113941                 notes = uiToolNotes(context),
113942                 undoRedo = uiToolUndoRedo(context),
113943                 save = uiToolSave(context);
113944
113945             function notesEnabled() {
113946                 var noteLayer = context.layers().layer('notes');
113947                 return noteLayer && noteLayer.enabled();
113948             }
113949
113950             function topToolbar(bar) {
113951
113952                 bar.on('wheel.topToolbar', function() {
113953                     if (!event.deltaX) {
113954                         // translate vertical scrolling into horizontal scrolling in case
113955                         // the user doesn't have an input device that can scroll horizontally
113956                         bar.node().scrollLeft += event.deltaY;
113957                     }
113958                 });
113959
113960                 var debouncedUpdate = debounce(update, 500, { leading: true, trailing: true });
113961                 context.layers()
113962                     .on('change.topToolbar', debouncedUpdate);
113963
113964                 update();
113965
113966                 function update() {
113967
113968                     var tools = [
113969                         sidebarToggle,
113970                         'spacer',
113971                         modes
113972                     ];
113973
113974                     tools.push('spacer');
113975
113976                     if (notesEnabled()) {
113977                         tools = tools.concat([notes, 'spacer']);
113978                     }
113979
113980                     tools = tools.concat([undoRedo, save]);
113981
113982                     var toolbarItems = bar.selectAll('.toolbar-item')
113983                         .data(tools, function(d) {
113984                             return d.id || d;
113985                         });
113986
113987                     toolbarItems.exit()
113988                         .each(function(d) {
113989                             if (d.uninstall) {
113990                                 d.uninstall();
113991                             }
113992                         })
113993                         .remove();
113994
113995                     var itemsEnter = toolbarItems
113996                         .enter()
113997                         .append('div')
113998                         .attr('class', function(d) {
113999                             var classes = 'toolbar-item ' + (d.id || d).replace('_', '-');
114000                             if (d.klass) classes += ' ' + d.klass;
114001                             return classes;
114002                         });
114003
114004                     var actionableItems = itemsEnter.filter(function(d) { return d !== 'spacer'; });
114005
114006                     actionableItems
114007                         .append('div')
114008                         .attr('class', 'item-content')
114009                         .each(function(d) {
114010                             select(this).call(d.render, bar);
114011                         });
114012
114013                     actionableItems
114014                         .append('div')
114015                         .attr('class', 'item-label')
114016                         .text(function(d) {
114017                             return d.label;
114018                         });
114019                 }
114020
114021             }
114022
114023             return topToolbar;
114024         }
114025
114026         // these are module variables so they are preserved through a ui.restart()
114027         var sawVersion = null;
114028         var isNewVersion = false;
114029         var isNewUser = false;
114030
114031
114032         function uiVersion(context) {
114033
114034             var currVersion = context.version;
114035             var matchedVersion = currVersion.match(/\d+\.\d+\.\d+.*/);
114036
114037             if (sawVersion === null && matchedVersion !== null) {
114038                 if (corePreferences('sawVersion')) {
114039                     isNewUser = false;
114040                     isNewVersion = corePreferences('sawVersion') !== currVersion;
114041                 } else {
114042                     isNewUser = true;
114043                     isNewVersion = true;
114044                 }
114045                 corePreferences('sawVersion', currVersion);
114046                 sawVersion = currVersion;
114047             }
114048
114049             return function(selection) {
114050                 selection
114051                     .append('a')
114052                     .attr('target', '_blank')
114053                     .attr('href', 'https://github.com/openstreetmap/iD')
114054                     .text(currVersion);
114055
114056                 // only show new version indicator to users that have used iD before
114057                 if (isNewVersion && !isNewUser) {
114058                     selection
114059                         .append('div')
114060                         .attr('class', 'badge')
114061                         .append('a')
114062                         .attr('target', '_blank')
114063                         .attr('href', 'https://github.com/openstreetmap/iD/blob/release/CHANGELOG.md#whats-new')
114064                         .call(svgIcon('#maki-gift-11'))
114065                         .call(uiTooltip()
114066                             .title(_t('version.whats_new', { version: currVersion }))
114067                             .placement('top')
114068                         );
114069                 }
114070             };
114071         }
114072
114073         function uiZoom(context) {
114074
114075             var zooms = [{
114076                 id: 'zoom-in',
114077                 icon: 'iD-icon-plus',
114078                 title: _t('zoom.in'),
114079                 action: zoomIn,
114080                 disabled: function() {
114081                     return !context.map().canZoomIn();
114082                 },
114083                 disabledTitle: _t('zoom.disabled.in'),
114084                 key: '+'
114085             }, {
114086                 id: 'zoom-out',
114087                 icon: 'iD-icon-minus',
114088                 title: _t('zoom.out'),
114089                 action: zoomOut,
114090                 disabled: function() {
114091                     return !context.map().canZoomOut();
114092                 },
114093                 disabledTitle: _t('zoom.disabled.out'),
114094                 key: '-'
114095             }];
114096
114097             function zoomIn() {
114098                 event.preventDefault();
114099                 context.map().zoomIn();
114100             }
114101
114102             function zoomOut() {
114103                 event.preventDefault();
114104                 context.map().zoomOut();
114105             }
114106
114107             function zoomInFurther() {
114108                 event.preventDefault();
114109                 context.map().zoomInFurther();
114110             }
114111
114112             function zoomOutFurther() {
114113                 event.preventDefault();
114114                 context.map().zoomOutFurther();
114115             }
114116
114117             return function(selection) {
114118                 var tooltipBehavior = uiTooltip()
114119                     .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
114120                     .title(function(d) {
114121                         if (d.disabled()) {
114122                             return d.disabledTitle;
114123                         }
114124                         return d.title;
114125                     })
114126                     .keys(function(d) {
114127                         return [d.key];
114128                     });
114129
114130                 var lastPointerUpType;
114131
114132                 var buttons = selection.selectAll('button')
114133                     .data(zooms)
114134                     .enter()
114135                     .append('button')
114136                     .attr('class', function(d) { return d.id; })
114137                     .on('pointerup.editor', function() {
114138                         lastPointerUpType = event.pointerType;
114139                     })
114140                     .on('click.editor', function(d) {
114141                         if (!d.disabled()) {
114142                             d.action();
114143                         } else if (lastPointerUpType === 'touch' || lastPointerUpType === 'pen') {
114144                             context.ui().flash
114145                                 .duration(2000)
114146                                 .iconName('#' + d.icon)
114147                                 .iconClass('disabled')
114148                                 .text(d.disabledTitle)();
114149                         }
114150                         lastPointerUpType = null;
114151                     })
114152                     .call(tooltipBehavior);
114153
114154                 buttons.each(function(d) {
114155                     select(this)
114156                         .call(svgIcon('#' + d.icon, 'light'));
114157                 });
114158
114159                 ['plus', 'ffplus', '=', 'ffequals'].forEach(function(key) {
114160                     context.keybinding().on([key], zoomIn);
114161                     context.keybinding().on([uiCmd('⌘' + key)], zoomInFurther);
114162                 });
114163
114164                 ['_', '-', 'ffminus', 'dash'].forEach(function(key) {
114165                     context.keybinding().on([key], zoomOut);
114166                     context.keybinding().on([uiCmd('⌘' + key)], zoomOutFurther);
114167                 });
114168
114169                 function updateButtonStates() {
114170                     buttons
114171                         .classed('disabled', function(d) {
114172                             return d.disabled();
114173                         })
114174                         .each(function() {
114175                             var selection = select(this);
114176                             if (!selection.select('.tooltip.in').empty()) {
114177                                 selection.call(tooltipBehavior.updateContent);
114178                             }
114179                         });
114180                 }
114181
114182                 updateButtonStates();
114183
114184                 context.map().on('move.uiZoom', updateButtonStates);
114185             };
114186         }
114187
114188         function uiZoomToSelection(context) {
114189
114190             function isDisabled() {
114191                 var mode = context.mode();
114192                 return !mode || !mode.zoomToSelected;
114193             }
114194
114195             var _lastPointerUpType;
114196
114197             function pointerup() {
114198                 _lastPointerUpType = event.pointerType;
114199             }
114200
114201             function click() {
114202                 event.preventDefault();
114203
114204                 if (isDisabled()) {
114205                     if (_lastPointerUpType === 'touch' || _lastPointerUpType === 'pen') {
114206                         context.ui().flash
114207                             .duration(2000)
114208                             .iconName('#iD-icon-framed-dot')
114209                             .iconClass('disabled')
114210                             .text(_t('inspector.zoom_to.no_selection'))();
114211                     }
114212                 } else {
114213                     var mode = context.mode();
114214                     if (mode && mode.zoomToSelected) {
114215                         mode.zoomToSelected();
114216                     }
114217                 }
114218
114219                 _lastPointerUpType = null;
114220             }
114221
114222             return function(selection) {
114223
114224                 var tooltipBehavior = uiTooltip()
114225                     .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
114226                     .title(function() {
114227                         if (isDisabled()) {
114228                             return _t('inspector.zoom_to.no_selection');
114229                         }
114230                         return _t('inspector.zoom_to.title');
114231                     })
114232                     .keys([_t('inspector.zoom_to.key')]);
114233
114234                 var button = selection
114235                     .append('button')
114236                     .on('pointerup', pointerup)
114237                     .on('click', click)
114238                     .call(svgIcon('#iD-icon-framed-dot', 'light'))
114239                     .call(tooltipBehavior);
114240
114241                 function setEnabledState() {
114242                     button.classed('disabled', isDisabled());
114243                     if (!button.select('.tooltip.in').empty()) {
114244                         button.call(tooltipBehavior.updateContent);
114245                     }
114246                 }
114247
114248                 context.on('enter.uiZoomToSelection', setEnabledState);
114249
114250                 setEnabledState();
114251             };
114252         }
114253
114254         function uiPane(id, context) {
114255
114256             var _key;
114257             var _title = '';
114258             var _description = '';
114259             var _iconName = '';
114260             var _sections; // array of uiSection objects
114261
114262             var _paneSelection = select(null);
114263
114264             var _paneTooltip;
114265
114266             var pane = {
114267                 id: id
114268             };
114269
114270             pane.title = function(val) {
114271                 if (!arguments.length) return _title;
114272                 _title = val;
114273                 return pane;
114274             };
114275
114276             pane.key = function(val) {
114277                 if (!arguments.length) return _key;
114278                 _key = val;
114279                 return pane;
114280             };
114281
114282             pane.description = function(val) {
114283                 if (!arguments.length) return _description;
114284                 _description = val;
114285                 return pane;
114286             };
114287
114288             pane.iconName = function(val) {
114289                 if (!arguments.length) return _iconName;
114290                 _iconName = val;
114291                 return pane;
114292             };
114293
114294             pane.sections = function(val) {
114295                 if (!arguments.length) return _sections;
114296                 _sections = val;
114297                 return pane;
114298             };
114299
114300             pane.selection = function() {
114301                 return _paneSelection;
114302             };
114303
114304             function hidePane() {
114305                 context.ui().togglePanes();
114306             }
114307
114308             pane.togglePane = function() {
114309                 if (event) event.preventDefault();
114310                 _paneTooltip.hide();
114311                 context.ui().togglePanes(!_paneSelection.classed('shown') ? _paneSelection : undefined);
114312             };
114313
114314             pane.renderToggleButton = function(selection) {
114315
114316                 if (!_paneTooltip) {
114317                     _paneTooltip = uiTooltip()
114318                         .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
114319                         .title(_description)
114320                         .keys([_key]);
114321                 }
114322
114323                 selection
114324                     .append('button')
114325                     .on('click', pane.togglePane)
114326                     .call(svgIcon('#' + _iconName, 'light'))
114327                     .call(_paneTooltip);
114328             };
114329
114330             pane.renderContent = function(selection) {
114331                 // override to fully customize content
114332
114333                 if (_sections) {
114334                     _sections.forEach(function(section) {
114335                         selection.call(section.render);
114336                     });
114337                 }
114338             };
114339
114340             pane.renderPane = function(selection) {
114341
114342                 _paneSelection = selection
114343                     .append('div')
114344                     .attr('class', 'fillL map-pane hide ' + id + '-pane')
114345                     .attr('pane', id);
114346
114347                 var heading = _paneSelection
114348                     .append('div')
114349                     .attr('class', 'pane-heading');
114350
114351                 heading
114352                     .append('h2')
114353                     .text(_title);
114354
114355                 heading
114356                     .append('button')
114357                     .on('click', hidePane)
114358                     .call(svgIcon('#iD-icon-close'));
114359
114360
114361                 _paneSelection
114362                     .append('div')
114363                     .attr('class', 'pane-content')
114364                     .call(pane.renderContent);
114365
114366                 if (_key) {
114367                     context.keybinding()
114368                         .on(_key, pane.togglePane);
114369                 }
114370             };
114371
114372             return pane;
114373         }
114374
114375         function uiSectionBackgroundDisplayOptions(context) {
114376
114377             var section = uiSection('background-display-options', context)
114378                 .title(_t('background.display_options'))
114379                 .disclosureContent(renderDisclosureContent);
114380
114381             var _detected = utilDetect();
114382             var _storedOpacity = corePreferences('background-opacity');
114383             var _minVal = 0.25;
114384             var _maxVal = _detected.cssfilters ? 2 : 1;
114385
114386             var _sliders = _detected.cssfilters
114387                 ? ['brightness', 'contrast', 'saturation', 'sharpness']
114388                 : ['brightness'];
114389
114390             var _options = {
114391                 brightness: (_storedOpacity !== null ? (+_storedOpacity) : 1),
114392                 contrast: 1,
114393                 saturation: 1,
114394                 sharpness: 1
114395             };
114396
114397             function clamp(x, min, max) {
114398                 return Math.max(min, Math.min(x, max));
114399             }
114400
114401             function updateValue(d, val) {
114402                 if (!val && event && event.target) {
114403                     val = event.target.value;
114404                 }
114405
114406                 val = clamp(val, _minVal, _maxVal);
114407
114408                 _options[d] = val;
114409                 context.background()[d](val);
114410
114411                 if (d === 'brightness') {
114412                     corePreferences('background-opacity', val);
114413                 }
114414
114415                 section.reRender();
114416             }
114417
114418             function renderDisclosureContent(selection) {
114419                 var container = selection.selectAll('.display-options-container')
114420                     .data([0]);
114421
114422                 var containerEnter = container.enter()
114423                     .append('div')
114424                     .attr('class', 'display-options-container controls-list');
114425
114426                 // add slider controls
114427                 var slidersEnter = containerEnter.selectAll('.display-control')
114428                     .data(_sliders)
114429                     .enter()
114430                     .append('div')
114431                     .attr('class', function(d) { return 'display-control display-control-' + d; });
114432
114433                 slidersEnter
114434                     .append('h5')
114435                     .text(function(d) { return _t('background.' + d); })
114436                     .append('span')
114437                     .attr('class', function(d) { return 'display-option-value display-option-value-' + d; });
114438
114439                 slidersEnter
114440                     .append('input')
114441                     .attr('class', function(d) { return 'display-option-input display-option-input-' + d; })
114442                     .attr('type', 'range')
114443                     .attr('min', _minVal)
114444                     .attr('max', _maxVal)
114445                     .attr('step', '0.05')
114446                     .on('input', function(d) {
114447                         var val = select(this).property('value');
114448                         updateValue(d, val);
114449                     });
114450
114451                 slidersEnter
114452                     .append('button')
114453                     .attr('title', _t('background.reset'))
114454                     .attr('class', function(d) { return 'display-option-reset display-option-reset-' + d; })
114455                     .on('click', function(d) {
114456                         if (event.button !== 0) return;
114457                         updateValue(d, 1);
114458                     })
114459                     .call(svgIcon('#iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo')));
114460
114461                 // reset all button
114462                 containerEnter
114463                     .append('a')
114464                     .attr('class', 'display-option-resetlink')
114465                     .attr('href', '#')
114466                     .text(_t('background.reset_all'))
114467                     .on('click', function() {
114468                         for (var i = 0; i < _sliders.length; i++) {
114469                             updateValue(_sliders[i],1);
114470                         }
114471                     });
114472
114473                 // update
114474                 container = containerEnter
114475                     .merge(container);
114476
114477                 container.selectAll('.display-option-input')
114478                     .property('value', function(d) { return _options[d]; });
114479
114480                 container.selectAll('.display-option-value')
114481                     .text(function(d) { return Math.floor(_options[d] * 100) + '%'; });
114482
114483                 container.selectAll('.display-option-reset')
114484                     .classed('disabled', function(d) { return _options[d] === 1; });
114485
114486                 // first time only, set brightness if needed
114487                 if (containerEnter.size() && _options.brightness !== 1) {
114488                     context.background().brightness(_options.brightness);
114489                 }
114490             }
114491
114492             return section;
114493         }
114494
114495         function uiSettingsCustomBackground() {
114496             var dispatch$1 = dispatch('change');
114497
114498             function render(selection) {
114499                 // keep separate copies of original and current settings
114500                 var _origSettings = {
114501                     template: corePreferences('background-custom-template')
114502                 };
114503                 var _currSettings = {
114504                     template: corePreferences('background-custom-template')
114505                 };
114506
114507                 var example = 'https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png';
114508                 var modal = uiConfirm(selection).okButton();
114509
114510                 modal
114511                     .classed('settings-modal settings-custom-background', true);
114512
114513                 modal.select('.modal-section.header')
114514                     .append('h3')
114515                     .text(_t('settings.custom_background.header'));
114516
114517
114518                 var textSection = modal.select('.modal-section.message-text');
114519
114520                 var instructions =
114521                     `${_t('settings.custom_background.instructions.info')}\n` +
114522                     '\n' +
114523                     `#### ${_t('settings.custom_background.instructions.wms.tokens_label')}\n` +
114524                     `* ${_t('settings.custom_background.instructions.wms.tokens.proj')}\n` +
114525                     `* ${_t('settings.custom_background.instructions.wms.tokens.wkid')}\n` +
114526                     `* ${_t('settings.custom_background.instructions.wms.tokens.dimensions')}\n` +
114527                     `* ${_t('settings.custom_background.instructions.wms.tokens.bbox')}\n` +
114528                     '\n' +
114529                     `#### ${_t('settings.custom_background.instructions.tms.tokens_label')}\n` +
114530                     `* ${_t('settings.custom_background.instructions.tms.tokens.xyz')}\n` +
114531                     `* ${_t('settings.custom_background.instructions.tms.tokens.flipped_y')}\n` +
114532                     `* ${_t('settings.custom_background.instructions.tms.tokens.switch')}\n` +
114533                     `* ${_t('settings.custom_background.instructions.tms.tokens.quadtile')}\n` +
114534                     `* ${_t('settings.custom_background.instructions.tms.tokens.scale_factor')}\n` +
114535                     '\n' +
114536                     `#### ${_t('settings.custom_background.instructions.example')}\n` +
114537                     `\`${example}\``;
114538
114539                 textSection
114540                     .append('div')
114541                     .attr('class', 'instructions-template')
114542                     .html(marked_1(instructions));
114543
114544                 textSection
114545                     .append('textarea')
114546                     .attr('class', 'field-template')
114547                     .attr('placeholder', _t('settings.custom_background.template.placeholder'))
114548                     .call(utilNoAuto)
114549                     .property('value', _currSettings.template);
114550
114551
114552                 // insert a cancel button
114553                 var buttonSection = modal.select('.modal-section.buttons');
114554
114555                 buttonSection
114556                     .insert('button', '.ok-button')
114557                     .attr('class', 'button cancel-button secondary-action')
114558                     .text(_t('confirm.cancel'));
114559
114560
114561                 buttonSection.select('.cancel-button')
114562                     .on('click.cancel', clickCancel);
114563
114564                 buttonSection.select('.ok-button')
114565                     .attr('disabled', isSaveDisabled)
114566                     .on('click.save', clickSave);
114567
114568
114569                 function isSaveDisabled() {
114570                     return null;
114571                 }
114572
114573
114574                 // restore the original template
114575                 function clickCancel() {
114576                     textSection.select('.field-template').property('value', _origSettings.template);
114577                     corePreferences('background-custom-template', _origSettings.template);
114578                     this.blur();
114579                     modal.close();
114580                 }
114581
114582                 // accept the current template
114583                 function clickSave() {
114584                     _currSettings.template = textSection.select('.field-template').property('value');
114585                     corePreferences('background-custom-template', _currSettings.template);
114586                     this.blur();
114587                     modal.close();
114588                     dispatch$1.call('change', this, _currSettings);
114589                 }
114590             }
114591
114592             return utilRebind(render, dispatch$1, 'on');
114593         }
114594
114595         function uiSectionBackgroundList(context) {
114596
114597             var _backgroundList = select(null);
114598
114599             var _customSource = context.background().findSource('custom');
114600
114601             var _settingsCustomBackground = uiSettingsCustomBackground()
114602                 .on('change', customChanged);
114603
114604             var section = uiSection('background-list', context)
114605                 .title(_t('background.backgrounds'))
114606                 .disclosureContent(renderDisclosureContent);
114607
114608             function previousBackgroundID() {
114609                 return corePreferences('background-last-used-toggle');
114610             }
114611
114612             function renderDisclosureContent(selection) {
114613
114614                 // the background list
114615                 var container = selection.selectAll('.layer-background-list')
114616                     .data([0]);
114617
114618                 _backgroundList = container.enter()
114619                     .append('ul')
114620                     .attr('class', 'layer-list layer-background-list')
114621                     .attr('dir', 'auto')
114622                     .merge(container);
114623
114624
114625                 // add minimap toggle below list
114626                 var bgExtrasListEnter = selection.selectAll('.bg-extras-list')
114627                     .data([0])
114628                     .enter()
114629                     .append('ul')
114630                     .attr('class', 'layer-list bg-extras-list');
114631
114632                 var minimapLabelEnter = bgExtrasListEnter
114633                     .append('li')
114634                     .attr('class', 'minimap-toggle-item')
114635                     .append('label')
114636                     .call(uiTooltip()
114637                         .title(_t('background.minimap.tooltip'))
114638                         .keys([_t('background.minimap.key')])
114639                         .placement('top')
114640                     );
114641
114642                 minimapLabelEnter
114643                     .append('input')
114644                     .attr('type', 'checkbox')
114645                     .on('change', function() {
114646                         event.preventDefault();
114647                         uiMapInMap.toggle();
114648                     });
114649
114650                 minimapLabelEnter
114651                     .append('span')
114652                     .text(_t('background.minimap.description'));
114653
114654
114655                 var panelLabelEnter = bgExtrasListEnter
114656                     .append('li')
114657                     .attr('class', 'background-panel-toggle-item')
114658                     .append('label')
114659                     .call(uiTooltip()
114660                         .title(_t('background.panel.tooltip'))
114661                         .keys([uiCmd('⌘⇧' + _t('info_panels.background.key'))])
114662                         .placement('top')
114663                     );
114664
114665                 panelLabelEnter
114666                     .append('input')
114667                     .attr('type', 'checkbox')
114668                     .on('change', function() {
114669                         event.preventDefault();
114670                         context.ui().info.toggle('background');
114671                     });
114672
114673                 panelLabelEnter
114674                     .append('span')
114675                     .text(_t('background.panel.description'));
114676
114677                 var locPanelLabelEnter = bgExtrasListEnter
114678                     .append('li')
114679                     .attr('class', 'location-panel-toggle-item')
114680                     .append('label')
114681                     .call(uiTooltip()
114682                         .title(_t('background.location_panel.tooltip'))
114683                         .keys([uiCmd('⌘⇧' + _t('info_panels.location.key'))])
114684                         .placement('top')
114685                     );
114686
114687                 locPanelLabelEnter
114688                     .append('input')
114689                     .attr('type', 'checkbox')
114690                     .on('change', function() {
114691                         event.preventDefault();
114692                         context.ui().info.toggle('location');
114693                     });
114694
114695                 locPanelLabelEnter
114696                     .append('span')
114697                     .text(_t('background.location_panel.description'));
114698
114699
114700                 // "Info / Report a Problem" link
114701                 selection.selectAll('.imagery-faq')
114702                     .data([0])
114703                     .enter()
114704                     .append('div')
114705                     .attr('class', 'imagery-faq')
114706                     .append('a')
114707                     .attr('target', '_blank')
114708                     .call(svgIcon('#iD-icon-out-link', 'inline'))
114709                     .attr('href', 'https://github.com/openstreetmap/iD/blob/develop/FAQ.md#how-can-i-report-an-issue-with-background-imagery')
114710                     .append('span')
114711                     .text(_t('background.imagery_problem_faq'));
114712
114713                 _backgroundList
114714                     .call(drawListItems, 'radio', chooseBackground, function(d) { return !d.isHidden() && !d.overlay; });
114715             }
114716
114717             function setTooltips(selection) {
114718                 selection.each(function(d, i, nodes) {
114719                     var item = select(this).select('label');
114720                     var span = item.select('span');
114721                     var placement = (i < nodes.length / 2) ? 'bottom' : 'top';
114722                     var description = d.description();
114723                     var isOverflowing = (span.property('clientWidth') !== span.property('scrollWidth'));
114724
114725                     item.call(uiTooltip().destroyAny);
114726
114727                     if (d.id === previousBackgroundID()) {
114728                         item.call(uiTooltip()
114729                             .placement(placement)
114730                             .title('<div>' + _t('background.switch') + '</div>')
114731                             .keys([uiCmd('⌘' + _t('background.key'))])
114732                         );
114733                     } else if (description || isOverflowing) {
114734                         item.call(uiTooltip()
114735                             .placement(placement)
114736                             .title(description || d.name())
114737                         );
114738                     }
114739                 });
114740             }
114741
114742             function drawListItems(layerList, type, change, filter) {
114743                 var sources = context.background()
114744                     .sources(context.map().extent(), context.map().zoom(), true)
114745                     .filter(filter);
114746
114747                 var layerLinks = layerList.selectAll('li')
114748                     .data(sources, function(d) { return d.name(); });
114749
114750                 layerLinks.exit()
114751                     .remove();
114752
114753                 var enter = layerLinks.enter()
114754                     .append('li')
114755                     .classed('layer-custom', function(d) { return d.id === 'custom'; })
114756                     .classed('best', function(d) { return d.best(); });
114757
114758                 var label = enter
114759                     .append('label');
114760
114761                 label
114762                     .append('input')
114763                     .attr('type', type)
114764                     .attr('name', 'layers')
114765                     .on('change', change);
114766
114767                 label
114768                     .append('span')
114769                     .text(function(d) { return d.name(); });
114770
114771                 enter.filter(function(d) { return d.id === 'custom'; })
114772                     .append('button')
114773                     .attr('class', 'layer-browse')
114774                     .call(uiTooltip()
114775                         .title(_t('settings.custom_background.tooltip'))
114776                         .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
114777                     )
114778                     .on('click', editCustom)
114779                     .call(svgIcon('#iD-icon-more'));
114780
114781                 enter.filter(function(d) { return d.best(); })
114782                     .append('div')
114783                     .attr('class', 'best')
114784                     .call(uiTooltip()
114785                         .title(_t('background.best_imagery'))
114786                         .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
114787                     )
114788                     .append('span')
114789                     .html('&#9733;');
114790
114791
114792                 layerList.selectAll('li')
114793                     .sort(sortSources);
114794
114795                 layerList
114796                     .call(updateLayerSelections);
114797
114798
114799                 function sortSources(a, b) {
114800                     return a.best() && !b.best() ? -1
114801                         : b.best() && !a.best() ? 1
114802                         : d3_descending(a.area(), b.area()) || d3_ascending(a.name(), b.name()) || 0;
114803                 }
114804             }
114805
114806             function updateLayerSelections(selection) {
114807                 function active(d) {
114808                     return context.background().showsLayer(d);
114809                 }
114810
114811                 selection.selectAll('li')
114812                     .classed('active', active)
114813                     .classed('switch', function(d) { return d.id === previousBackgroundID(); })
114814                     .call(setTooltips)
114815                     .selectAll('input')
114816                     .property('checked', active);
114817             }
114818
114819
114820             function chooseBackground(d) {
114821                 if (d.id === 'custom' && !d.template()) {
114822                     return editCustom();
114823                 }
114824
114825                 event.preventDefault();
114826                 var previousBackground = context.background().baseLayerSource();
114827                 corePreferences('background-last-used-toggle', previousBackground.id);
114828                 corePreferences('background-last-used', d.id);
114829                 context.background().baseLayerSource(d);
114830                 document.activeElement.blur();
114831             }
114832
114833
114834             function customChanged(d) {
114835                 if (d && d.template) {
114836                     _customSource.template(d.template);
114837                     chooseBackground(_customSource);
114838                 } else {
114839                     _customSource.template('');
114840                     chooseBackground(context.background().findSource('none'));
114841                 }
114842             }
114843
114844
114845             function editCustom() {
114846                 event.preventDefault();
114847                 context.container()
114848                     .call(_settingsCustomBackground);
114849             }
114850
114851
114852             context.background()
114853                 .on('change.background_list', function() {
114854                     _backgroundList.call(updateLayerSelections);
114855                 });
114856
114857             context.map()
114858                 .on('move.background_list',
114859                     debounce(function() {
114860                         // layers in-view may have changed due to map move
114861                         window.requestIdleCallback(section.reRender);
114862                     }, 1000)
114863                 );
114864
114865             return section;
114866         }
114867
114868         function uiSectionBackgroundOffset(context) {
114869
114870             var section = uiSection('background-offset', context)
114871                 .title(_t('background.fix_misalignment'))
114872                 .disclosureContent(renderDisclosureContent)
114873                 .expandedByDefault(false);
114874
114875             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
114876
114877             var _directions = [
114878                 ['right', [0.5, 0]],
114879                 ['top', [0, -0.5]],
114880                 ['left', [-0.5, 0]],
114881                 ['bottom', [0, 0.5]]
114882             ];
114883
114884
114885             function cancelEvent() {
114886                 event.stopPropagation();
114887                 event.preventDefault();
114888             }
114889
114890
114891             function updateValue() {
114892                 var meters = geoOffsetToMeters(context.background().offset());
114893                 var x = +meters[0].toFixed(2);
114894                 var y = +meters[1].toFixed(2);
114895
114896                 context.container().selectAll('.nudge-inner-rect')
114897                     .select('input')
114898                     .classed('error', false)
114899                     .property('value', x + ', ' + y);
114900
114901                 context.container().selectAll('.nudge-reset')
114902                     .classed('disabled', function() {
114903                         return (x === 0 && y === 0);
114904                     });
114905             }
114906
114907
114908             function resetOffset() {
114909                 context.background().offset([0, 0]);
114910                 updateValue();
114911             }
114912
114913
114914             function nudge(d) {
114915                 context.background().nudge(d, context.map().zoom());
114916                 updateValue();
114917             }
114918
114919
114920             function pointerdownNudgeButton(d) {
114921                 var interval;
114922                 var timeout = window.setTimeout(function() {
114923                         interval = window.setInterval(nudge.bind(null, d), 100);
114924                     }, 500);
114925
114926                 function doneNudge() {
114927                     window.clearTimeout(timeout);
114928                     window.clearInterval(interval);
114929                     select(window)
114930                         .on(_pointerPrefix + 'up.buttonoffset', null, true)
114931                         .on(_pointerPrefix + 'down.buttonoffset', null, true);
114932                 }
114933
114934                 select(window)
114935                     .on(_pointerPrefix + 'up.buttonoffset', doneNudge, true)
114936                     .on(_pointerPrefix + 'down.buttonoffset', doneNudge, true);
114937
114938                 nudge(d);
114939             }
114940
114941
114942             function inputOffset() {
114943                 var input = select(this);
114944                 var d = input.node().value;
114945
114946                 if (d === '') return resetOffset();
114947
114948                 d = d.replace(/;/g, ',').split(',').map(function(n) {
114949                     // if n is NaN, it will always get mapped to false.
114950                     return !isNaN(n) && n;
114951                 });
114952
114953                 if (d.length !== 2 || !d[0] || !d[1]) {
114954                     input.classed('error', true);
114955                     return;
114956                 }
114957
114958                 context.background().offset(geoMetersToOffset(d));
114959                 updateValue();
114960             }
114961
114962
114963             function dragOffset() {
114964                 if (event.button !== 0) return;
114965
114966                 var origin = [event.clientX, event.clientY];
114967
114968                 var pointerId = event.pointerId || 'mouse';
114969
114970                 context.container()
114971                     .append('div')
114972                     .attr('class', 'nudge-surface');
114973
114974                 select(window)
114975                     .on(_pointerPrefix + 'move.drag-bg-offset', pointermove)
114976                     .on(_pointerPrefix + 'up.drag-bg-offset', pointerup);
114977
114978                 if (_pointerPrefix === 'pointer') {
114979                     select(window)
114980                         .on('pointercancel.drag-bg-offset', pointerup);
114981                 }
114982
114983                 function pointermove() {
114984                     if (pointerId !== (event.pointerId || 'mouse')) return;
114985
114986                     var latest = [event.clientX, event.clientY];
114987                     var d = [
114988                         -(origin[0] - latest[0]) / 4,
114989                         -(origin[1] - latest[1]) / 4
114990                     ];
114991
114992                     origin = latest;
114993                     nudge(d);
114994                 }
114995
114996                 function pointerup() {
114997                     if (pointerId !== (event.pointerId || 'mouse')) return;
114998                     if (event.button !== 0) return;
114999
115000                     context.container().selectAll('.nudge-surface')
115001                         .remove();
115002
115003                     select(window)
115004                         .on('.drag-bg-offset', null);
115005                 }
115006             }
115007
115008
115009             function renderDisclosureContent(selection) {
115010                 var container = selection.selectAll('.nudge-container')
115011                     .data([0]);
115012
115013                 var containerEnter = container.enter()
115014                     .append('div')
115015                     .attr('class', 'nudge-container cf');
115016
115017                 containerEnter
115018                     .append('div')
115019                     .attr('class', 'nudge-instructions')
115020                     .text(_t('background.offset'));
115021
115022                 var nudgeEnter = containerEnter
115023                     .append('div')
115024                     .attr('class', 'nudge-outer-rect')
115025                     .on(_pointerPrefix + 'down', dragOffset);
115026
115027                 nudgeEnter
115028                     .append('div')
115029                     .attr('class', 'nudge-inner-rect')
115030                     .append('input')
115031                     .on('change', inputOffset);
115032
115033                 containerEnter
115034                     .append('div')
115035                     .selectAll('button')
115036                     .data(_directions).enter()
115037                     .append('button')
115038                     .attr('class', function(d) { return d[0] + ' nudge'; })
115039                     .on('contextmenu', cancelEvent)
115040                     .on(_pointerPrefix + 'down', function(d) {
115041                         if (event.button !== 0) return;
115042                         pointerdownNudgeButton(d[1]);
115043                     });
115044
115045                 containerEnter
115046                     .append('button')
115047                     .attr('title', _t('background.reset'))
115048                     .attr('class', 'nudge-reset disabled')
115049                     .on('contextmenu', cancelEvent)
115050                     .on('click', function() {
115051                         event.preventDefault();
115052                         if (event.button !== 0) return;
115053                         resetOffset();
115054                     })
115055                     .call(svgIcon('#iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo')));
115056
115057                 updateValue();
115058             }
115059
115060             context.background()
115061                 .on('change.backgroundOffset-update', updateValue);
115062
115063             return section;
115064         }
115065
115066         function uiSectionOverlayList(context) {
115067
115068             var section = uiSection('overlay-list', context)
115069                 .title(_t('background.overlays'))
115070                 .disclosureContent(renderDisclosureContent);
115071
115072             var _overlayList = select(null);
115073
115074             function setTooltips(selection) {
115075                 selection.each(function(d, i, nodes) {
115076                     var item = select(this).select('label');
115077                     var span = item.select('span');
115078                     var placement = (i < nodes.length / 2) ? 'bottom' : 'top';
115079                     var description = d.description();
115080                     var isOverflowing = (span.property('clientWidth') !== span.property('scrollWidth'));
115081
115082                     item.call(uiTooltip().destroyAny);
115083
115084                     if (description || isOverflowing) {
115085                         item.call(uiTooltip()
115086                             .placement(placement)
115087                             .title(description || d.name())
115088                         );
115089                     }
115090                 });
115091             }
115092
115093             function updateLayerSelections(selection) {
115094                 function active(d) {
115095                     return context.background().showsLayer(d);
115096                 }
115097
115098                 selection.selectAll('li')
115099                     .classed('active', active)
115100                     .call(setTooltips)
115101                     .selectAll('input')
115102                     .property('checked', active);
115103             }
115104
115105
115106             function chooseOverlay(d) {
115107                 event.preventDefault();
115108                 context.background().toggleOverlayLayer(d);
115109                 _overlayList.call(updateLayerSelections);
115110                 document.activeElement.blur();
115111             }
115112
115113             function drawListItems(layerList, type, change, filter) {
115114                 var sources = context.background()
115115                     .sources(context.map().extent(), context.map().zoom(), true)
115116                     .filter(filter);
115117
115118                 var layerLinks = layerList.selectAll('li')
115119                     .data(sources, function(d) { return d.name(); });
115120
115121                 layerLinks.exit()
115122                     .remove();
115123
115124                 var enter = layerLinks.enter()
115125                     .append('li');
115126
115127                 var label = enter
115128                     .append('label');
115129
115130                 label
115131                     .append('input')
115132                     .attr('type', type)
115133                     .attr('name', 'layers')
115134                     .on('change', change);
115135
115136                 label
115137                     .append('span')
115138                     .text(function(d) { return d.name(); });
115139
115140
115141                 layerList.selectAll('li')
115142                     .sort(sortSources);
115143
115144                 layerList
115145                     .call(updateLayerSelections);
115146
115147
115148                 function sortSources(a, b) {
115149                     return a.best() && !b.best() ? -1
115150                         : b.best() && !a.best() ? 1
115151                         : d3_descending(a.area(), b.area()) || d3_ascending(a.name(), b.name()) || 0;
115152                 }
115153             }
115154
115155             function renderDisclosureContent(selection) {
115156
115157                 var container = selection.selectAll('.layer-overlay-list')
115158                     .data([0]);
115159
115160                 _overlayList = container.enter()
115161                     .append('ul')
115162                     .attr('class', 'layer-list layer-overlay-list')
115163                     .attr('dir', 'auto')
115164                     .merge(container);
115165
115166                 _overlayList
115167                     .call(drawListItems, 'checkbox', chooseOverlay, function(d) { return !d.isHidden() && d.overlay; });
115168             }
115169
115170             context.map()
115171                 .on('move.overlay_list',
115172                     debounce(function() {
115173                         // layers in-view may have changed due to map move
115174                         window.requestIdleCallback(section.reRender);
115175                     }, 1000)
115176                 );
115177
115178             return section;
115179         }
115180
115181         function uiPaneBackground(context) {
115182
115183             var backgroundPane = uiPane('background', context)
115184                 .key(_t('background.key'))
115185                 .title(_t('background.title'))
115186                 .description(_t('background.description'))
115187                 .iconName('iD-icon-layers')
115188                 .sections([
115189                     uiSectionBackgroundList(context),
115190                     uiSectionOverlayList(context),
115191                     uiSectionBackgroundDisplayOptions(context),
115192                     uiSectionBackgroundOffset(context)
115193                 ]);
115194
115195             return backgroundPane;
115196         }
115197
115198         function uiPaneHelp(context) {
115199
115200             var docKeys = [
115201                 ['help', [
115202                     'welcome',
115203                     'open_data_h',
115204                     'open_data',
115205                     'before_start_h',
115206                     'before_start',
115207                     'open_source_h',
115208                     'open_source',
115209                     'open_source_help'
115210                 ]],
115211                 ['overview', [
115212                     'navigation_h',
115213                     'navigation_drag',
115214                     'navigation_zoom',
115215                     'features_h',
115216                     'features',
115217                     'nodes_ways'
115218                 ]],
115219                 ['editing', [
115220                     'select_h',
115221                     'select_left_click',
115222                     'select_right_click',
115223                     'select_space',
115224                     'multiselect_h',
115225                     'multiselect',
115226                     'multiselect_shift_click',
115227                     'multiselect_lasso',
115228                     'undo_redo_h',
115229                     'undo_redo',
115230                     'save_h',
115231                     'save',
115232                     'save_validation',
115233                     'upload_h',
115234                     'upload',
115235                     'backups_h',
115236                     'backups',
115237                     'keyboard_h',
115238                     'keyboard'
115239                 ]],
115240                 ['feature_editor', [
115241                     'intro',
115242                     'definitions',
115243                     'type_h',
115244                     'type',
115245                     'type_picker',
115246                     'fields_h',
115247                     'fields_all_fields',
115248                     'fields_example',
115249                     'fields_add_field',
115250                     'tags_h',
115251                     'tags_all_tags',
115252                     'tags_resources'
115253                 ]],
115254                 ['points', [
115255                     'intro',
115256                     'add_point_h',
115257                     'add_point',
115258                     'add_point_finish',
115259                     'move_point_h',
115260                     'move_point',
115261                     'delete_point_h',
115262                     'delete_point',
115263                     'delete_point_command'
115264                 ]],
115265                 ['lines', [
115266                     'intro',
115267                     'add_line_h',
115268                     'add_line',
115269                     'add_line_draw',
115270                     'add_line_continue',
115271                     'add_line_finish',
115272                     'modify_line_h',
115273                     'modify_line_dragnode',
115274                     'modify_line_addnode',
115275                     'connect_line_h',
115276                     'connect_line',
115277                     'connect_line_display',
115278                     'connect_line_drag',
115279                     'connect_line_tag',
115280                     'disconnect_line_h',
115281                     'disconnect_line_command',
115282                     'move_line_h',
115283                     'move_line_command',
115284                     'move_line_connected',
115285                     'delete_line_h',
115286                     'delete_line',
115287                     'delete_line_command'
115288                 ]],
115289                 ['areas', [
115290                     'intro',
115291                     'point_or_area_h',
115292                     'point_or_area',
115293                     'add_area_h',
115294                     'add_area_command',
115295                     'add_area_draw',
115296                     'add_area_continue',
115297                     'add_area_finish',
115298                     'square_area_h',
115299                     'square_area_command',
115300                     'modify_area_h',
115301                     'modify_area_dragnode',
115302                     'modify_area_addnode',
115303                     'delete_area_h',
115304                     'delete_area',
115305                     'delete_area_command'
115306                 ]],
115307                 ['relations', [
115308                     'intro',
115309                     'edit_relation_h',
115310                     'edit_relation',
115311                     'edit_relation_add',
115312                     'edit_relation_delete',
115313                     'maintain_relation_h',
115314                     'maintain_relation',
115315                     'relation_types_h',
115316                     'multipolygon_h',
115317                     'multipolygon',
115318                     'multipolygon_create',
115319                     'multipolygon_merge',
115320                     'turn_restriction_h',
115321                     'turn_restriction',
115322                     'turn_restriction_field',
115323                     'turn_restriction_editing',
115324                     'route_h',
115325                     'route',
115326                     'route_add',
115327                     'boundary_h',
115328                     'boundary',
115329                     'boundary_add'
115330                 ]],
115331                 ['notes', [
115332                     'intro',
115333                     'add_note_h',
115334                     'add_note',
115335                     'place_note',
115336                     'move_note',
115337                     'update_note_h',
115338                     'update_note',
115339                     'save_note_h',
115340                     'save_note'
115341                 ]],
115342                 ['imagery', [
115343                     'intro',
115344                     'sources_h',
115345                     'choosing',
115346                     'sources',
115347                     'offsets_h',
115348                     'offset',
115349                     'offset_change'
115350                 ]],
115351                 ['streetlevel', [
115352                     'intro',
115353                     'using_h',
115354                     'using',
115355                     'photos',
115356                     'viewer'
115357                 ]],
115358                 ['gps', [
115359                     'intro',
115360                     'survey',
115361                     'using_h',
115362                     'using',
115363                     'tracing',
115364                     'upload'
115365                 ]],
115366                 ['qa', [
115367                     'intro',
115368                     'tools_h',
115369                     'tools',
115370                     'issues_h',
115371                     'issues'
115372                 ]]
115373             ];
115374
115375             var headings = {
115376                 'help.help.open_data_h': 3,
115377                 'help.help.before_start_h': 3,
115378                 'help.help.open_source_h': 3,
115379                 'help.overview.navigation_h': 3,
115380                 'help.overview.features_h': 3,
115381                 'help.editing.select_h': 3,
115382                 'help.editing.multiselect_h': 3,
115383                 'help.editing.undo_redo_h': 3,
115384                 'help.editing.save_h': 3,
115385                 'help.editing.upload_h': 3,
115386                 'help.editing.backups_h': 3,
115387                 'help.editing.keyboard_h': 3,
115388                 'help.feature_editor.type_h': 3,
115389                 'help.feature_editor.fields_h': 3,
115390                 'help.feature_editor.tags_h': 3,
115391                 'help.points.add_point_h': 3,
115392                 'help.points.move_point_h': 3,
115393                 'help.points.delete_point_h': 3,
115394                 'help.lines.add_line_h': 3,
115395                 'help.lines.modify_line_h': 3,
115396                 'help.lines.connect_line_h': 3,
115397                 'help.lines.disconnect_line_h': 3,
115398                 'help.lines.move_line_h': 3,
115399                 'help.lines.delete_line_h': 3,
115400                 'help.areas.point_or_area_h': 3,
115401                 'help.areas.add_area_h': 3,
115402                 'help.areas.square_area_h': 3,
115403                 'help.areas.modify_area_h': 3,
115404                 'help.areas.delete_area_h': 3,
115405                 'help.relations.edit_relation_h': 3,
115406                 'help.relations.maintain_relation_h': 3,
115407                 'help.relations.relation_types_h': 2,
115408                 'help.relations.multipolygon_h': 3,
115409                 'help.relations.turn_restriction_h': 3,
115410                 'help.relations.route_h': 3,
115411                 'help.relations.boundary_h': 3,
115412                 'help.notes.add_note_h': 3,
115413                 'help.notes.update_note_h': 3,
115414                 'help.notes.save_note_h': 3,
115415                 'help.imagery.sources_h': 3,
115416                 'help.imagery.offsets_h': 3,
115417                 'help.streetlevel.using_h': 3,
115418                 'help.gps.using_h': 3,
115419                 'help.qa.tools_h': 3,
115420                 'help.qa.issues_h': 3
115421             };
115422
115423             // For each section, squash all the texts into a single markdown document
115424             var docs = docKeys.map(function(key) {
115425                 var helpkey = 'help.' + key[0];
115426                 var helpPaneReplacements = { version: context.version };
115427                 var text = key[1].reduce(function(all, part) {
115428                     var subkey = helpkey + '.' + part;
115429                     var depth = headings[subkey];                              // is this subkey a heading?
115430                     var hhh = depth ? Array(depth + 1).join('#') + ' ' : '';   // if so, prepend with some ##'s
115431                     return all + hhh + helpString(subkey, helpPaneReplacements) + '\n\n';
115432                 }, '');
115433
115434                 return {
115435                     title: _t(helpkey + '.title'),
115436                     html: marked_1(text.trim())
115437                         // use keyboard key styling for shortcuts
115438                         .replace(/<code>/g, '<kbd>')
115439                         .replace(/<\/code>/g, '<\/kbd>')
115440                 };
115441             });
115442
115443             var helpPane = uiPane('help', context)
115444                 .key(_t('help.key'))
115445                 .title(_t('help.title'))
115446                 .description(_t('help.title'))
115447                 .iconName('iD-icon-help');
115448
115449             helpPane.renderContent = function(content) {
115450
115451                 function clickHelp(d, i) {
115452                     var rtl = (_mainLocalizer.textDirection() === 'rtl');
115453                     content.property('scrollTop', 0);
115454                     helpPane.selection().select('.pane-heading h2').html(d.title);
115455
115456                     body.html(d.html);
115457                     body.selectAll('a')
115458                         .attr('target', '_blank');
115459                     menuItems.classed('selected', function(m) {
115460                         return m.title === d.title;
115461                     });
115462
115463                     nav.html('');
115464                     if (rtl) {
115465                         nav.call(drawNext).call(drawPrevious);
115466                     } else {
115467                         nav.call(drawPrevious).call(drawNext);
115468                     }
115469
115470
115471                     function drawNext(selection) {
115472                         if (i < docs.length - 1) {
115473                             var nextLink = selection
115474                                 .append('a')
115475                                 .attr('class', 'next')
115476                                 .on('click', function() {
115477                                     clickHelp(docs[i + 1], i + 1);
115478                                 });
115479
115480                             nextLink
115481                                 .append('span')
115482                                 .text(docs[i + 1].title)
115483                                 .call(svgIcon((rtl ? '#iD-icon-backward' : '#iD-icon-forward'), 'inline'));
115484                         }
115485                     }
115486
115487
115488                     function drawPrevious(selection) {
115489                         if (i > 0) {
115490                             var prevLink = selection
115491                                 .append('a')
115492                                 .attr('class', 'previous')
115493                                 .on('click', function() {
115494                                     clickHelp(docs[i - 1], i - 1);
115495                                 });
115496
115497                             prevLink
115498                                 .call(svgIcon((rtl ? '#iD-icon-forward' : '#iD-icon-backward'), 'inline'))
115499                                 .append('span')
115500                                 .text(docs[i - 1].title);
115501                         }
115502                     }
115503                 }
115504
115505
115506                 function clickWalkthrough() {
115507                     if (context.inIntro()) return;
115508                     context.container().call(uiIntro(context));
115509                     context.ui().togglePanes();
115510                 }
115511
115512
115513                 function clickShortcuts() {
115514                     context.container().call(uiShortcuts(context), true);
115515                 }
115516
115517                 var toc = content
115518                     .append('ul')
115519                     .attr('class', 'toc');
115520
115521                 var menuItems = toc.selectAll('li')
115522                     .data(docs)
115523                     .enter()
115524                     .append('li')
115525                     .append('a')
115526                     .html(function(d) { return d.title; })
115527                     .on('click', clickHelp);
115528
115529                 var shortcuts = toc
115530                     .append('li')
115531                     .attr('class', 'shortcuts')
115532                     .call(uiTooltip()
115533                         .title(_t('shortcuts.tooltip'))
115534                         .keys(['?'])
115535                         .placement('top')
115536                     )
115537                     .append('a')
115538                     .on('click', clickShortcuts);
115539
115540                 shortcuts
115541                     .append('div')
115542                     .text(_t('shortcuts.title'));
115543
115544                 var walkthrough = toc
115545                     .append('li')
115546                     .attr('class', 'walkthrough')
115547                     .append('a')
115548                     .on('click', clickWalkthrough);
115549
115550                 walkthrough
115551                     .append('svg')
115552                     .attr('class', 'logo logo-walkthrough')
115553                     .append('use')
115554                     .attr('xlink:href', '#iD-logo-walkthrough');
115555
115556                 walkthrough
115557                     .append('div')
115558                     .text(_t('splash.walkthrough'));
115559
115560
115561                 var helpContent = content
115562                     .append('div')
115563                     .attr('class', 'left-content');
115564
115565                 var body = helpContent
115566                     .append('div')
115567                     .attr('class', 'body');
115568
115569                 var nav = helpContent
115570                     .append('div')
115571                     .attr('class', 'nav');
115572
115573                 clickHelp(docs[0], 0);
115574
115575             };
115576
115577             return helpPane;
115578         }
115579
115580         function uiSectionValidationIssues(id, severity, context) {
115581
115582             var _issues = [];
115583
115584             var section = uiSection(id, context)
115585                 .title(function() {
115586                     if (!_issues) return '';
115587                     var issueCountText = _issues.length > 1000 ? '1000+' : String(_issues.length);
115588                     return _t('issues.' + severity + 's.list_title', { count: issueCountText });
115589                 })
115590                 .disclosureContent(renderDisclosureContent)
115591                 .shouldDisplay(function() {
115592                     return _issues && _issues.length;
115593                 });
115594
115595             function getOptions() {
115596                 return {
115597                     what: corePreferences('validate-what') || 'edited',
115598                     where: corePreferences('validate-where') || 'all'
115599                 };
115600             }
115601
115602             // get and cache the issues to display, unordered
115603             function reloadIssues() {
115604                 _issues = context.validator().getIssuesBySeverity(getOptions())[severity];
115605             }
115606
115607             function renderDisclosureContent(selection) {
115608
115609                 var center = context.map().center();
115610                 var graph = context.graph();
115611
115612                 // sort issues by distance away from the center of the map
115613                 var issues = _issues.map(function withDistance(issue) {
115614                         var extent = issue.extent(graph);
115615                         var dist = extent ? geoSphericalDistance(center, extent.center()) : 0;
115616                         return Object.assign(issue, { dist: dist });
115617                     })
115618                     .sort(function byDistance(a, b) {
115619                         return a.dist - b.dist;
115620                     });
115621
115622                 // cut off at 1000
115623                 issues = issues.slice(0, 1000);
115624
115625                 //renderIgnoredIssuesReset(_warningsSelection);
115626
115627                 selection
115628                     .call(drawIssuesList, issues);
115629             }
115630
115631             function drawIssuesList(selection, issues) {
115632                 var list = selection.selectAll('.issues-list')
115633                     .data([0]);
115634
115635                 list = list.enter()
115636                     .append('ul')
115637                     .attr('class', 'layer-list issues-list ' + severity + 's-list')
115638                     .merge(list);
115639
115640
115641                 var items = list.selectAll('li')
115642                     .data(issues, function(d) { return d.id; });
115643
115644                 // Exit
115645                 items.exit()
115646                     .remove();
115647
115648                 // Enter
115649                 var itemsEnter = items.enter()
115650                     .append('li')
115651                     .attr('class', function (d) { return 'issue severity-' + d.severity; })
115652                     .on('click', function(d) {
115653                         context.validator().focusIssue(d);
115654                     })
115655                     .on('mouseover', function(d) {
115656                         utilHighlightEntities(d.entityIds, true, context);
115657                     })
115658                     .on('mouseout', function(d) {
115659                         utilHighlightEntities(d.entityIds, false, context);
115660                     });
115661
115662
115663                 var labelsEnter = itemsEnter
115664                     .append('div')
115665                     .attr('class', 'issue-label');
115666
115667                 var textEnter = labelsEnter
115668                     .append('span')
115669                     .attr('class', 'issue-text');
115670
115671                 textEnter
115672                     .append('span')
115673                     .attr('class', 'issue-icon')
115674                     .each(function(d) {
115675                         var iconName = '#iD-icon-' + (d.severity === 'warning' ? 'alert' : 'error');
115676                         select(this)
115677                             .call(svgIcon(iconName));
115678                     });
115679
115680                 textEnter
115681                     .append('span')
115682                     .attr('class', 'issue-message');
115683
115684                 /*
115685                 labelsEnter
115686                     .append('span')
115687                     .attr('class', 'issue-autofix')
115688                     .each(function(d) {
115689                         if (!d.autoFix) return;
115690
115691                         d3_select(this)
115692                             .append('button')
115693                             .attr('title', t('issues.fix_one.title'))
115694                             .datum(d.autoFix)  // set button datum to the autofix
115695                             .attr('class', 'autofix action')
115696                             .on('click', function(d) {
115697                                 d3_event.preventDefault();
115698                                 d3_event.stopPropagation();
115699
115700                                 var issuesEntityIDs = d.issue.entityIds;
115701                                 utilHighlightEntities(issuesEntityIDs.concat(d.entityIds), false, context);
115702
115703                                 context.perform.apply(context, d.autoArgs);
115704                                 context.validator().validate();
115705                             })
115706                             .call(svgIcon('#iD-icon-wrench'));
115707                     });
115708                 */
115709
115710                 // Update
115711                 items = items
115712                     .merge(itemsEnter)
115713                     .order();
115714
115715                 items.selectAll('.issue-message')
115716                     .text(function(d) {
115717                         return d.message(context);
115718                     });
115719
115720                 /*
115721                 // autofix
115722                 var canAutoFix = issues.filter(function(issue) { return issue.autoFix; });
115723
115724                 var autoFixAll = selection.selectAll('.autofix-all')
115725                     .data(canAutoFix.length ? [0] : []);
115726
115727                 // exit
115728                 autoFixAll.exit()
115729                     .remove();
115730
115731                 // enter
115732                 var autoFixAllEnter = autoFixAll.enter()
115733                     .insert('div', '.issues-list')
115734                     .attr('class', 'autofix-all');
115735
115736                 var linkEnter = autoFixAllEnter
115737                     .append('a')
115738                     .attr('class', 'autofix-all-link')
115739                     .attr('href', '#');
115740
115741                 linkEnter
115742                     .append('span')
115743                     .attr('class', 'autofix-all-link-text')
115744                     .text(t('issues.fix_all.title'));
115745
115746                 linkEnter
115747                     .append('span')
115748                     .attr('class', 'autofix-all-link-icon')
115749                     .call(svgIcon('#iD-icon-wrench'));
115750
115751                 if (severity === 'warning') {
115752                     renderIgnoredIssuesReset(selection);
115753                 }
115754
115755                 // update
115756                 autoFixAll = autoFixAll
115757                     .merge(autoFixAllEnter);
115758
115759                 autoFixAll.selectAll('.autofix-all-link')
115760                     .on('click', function() {
115761                         context.pauseChangeDispatch();
115762                         context.perform(actionNoop());
115763                         canAutoFix.forEach(function(issue) {
115764                             var args = issue.autoFix.autoArgs.slice();  // copy
115765                             if (typeof args[args.length - 1] !== 'function') {
115766                                 args.pop();
115767                             }
115768                             args.push(t('issues.fix_all.annotation'));
115769                             context.replace.apply(context, args);
115770                         });
115771                         context.resumeChangeDispatch();
115772                         context.validator().validate();
115773                     });
115774                 */
115775             }
115776
115777             context.validator().on('validated.uiSectionValidationIssues' + id, function() {
115778                 window.requestIdleCallback(function() {
115779                     reloadIssues();
115780                     section.reRender();
115781                 });
115782             });
115783
115784             context.map().on('move.uiSectionValidationIssues' + id,
115785                 debounce(function() {
115786                     window.requestIdleCallback(function() {
115787                         if (getOptions().where === 'visible') {
115788                             // must refetch issues if they are viewport-dependent
115789                             reloadIssues();
115790                         }
115791                         // always reload list to re-sort-by-distance
115792                         section.reRender();
115793                     });
115794                 }, 1000)
115795             );
115796
115797             return section;
115798         }
115799
115800         function uiSectionValidationOptions(context) {
115801
115802             var section = uiSection('issues-options', context)
115803                 .content(renderContent);
115804
115805             function renderContent(selection) {
115806
115807                 var container = selection.selectAll('.issues-options-container')
115808                     .data([0]);
115809
115810                 container = container.enter()
115811                     .append('div')
115812                     .attr('class', 'issues-options-container')
115813                     .merge(container);
115814
115815                 var data = [
115816                     { key: 'what', values: ['edited', 'all'] },
115817                     { key: 'where', values: ['visible', 'all'] }
115818                 ];
115819
115820                 var options = container.selectAll('.issues-option')
115821                     .data(data, function(d) { return d.key; });
115822
115823                 var optionsEnter = options.enter()
115824                     .append('div')
115825                     .attr('class', function(d) { return 'issues-option issues-option-' + d.key; });
115826
115827                 optionsEnter
115828                     .append('div')
115829                     .attr('class', 'issues-option-title')
115830                     .text(function(d) { return _t('issues.options.' + d.key + '.title'); });
115831
115832                 var valuesEnter = optionsEnter.selectAll('label')
115833                     .data(function(d) {
115834                         return d.values.map(function(val) { return { value: val, key: d.key }; });
115835                     })
115836                     .enter()
115837                     .append('label');
115838
115839                 valuesEnter
115840                     .append('input')
115841                     .attr('type', 'radio')
115842                     .attr('name', function(d) { return 'issues-option-' + d.key; })
115843                     .attr('value', function(d) { return d.value; })
115844                     .property('checked', function(d) { return getOptions()[d.key] === d.value; })
115845                     .on('change', function(d) { updateOptionValue(d.key, d.value); });
115846
115847                 valuesEnter
115848                     .append('span')
115849                     .text(function(d) { return _t('issues.options.' + d.key + '.' + d.value); });
115850             }
115851
115852             function getOptions() {
115853                 return {
115854                     what: corePreferences('validate-what') || 'edited',  // 'all', 'edited'
115855                     where: corePreferences('validate-where') || 'all'    // 'all', 'visible'
115856                 };
115857             }
115858
115859             function updateOptionValue(d, val) {
115860                 if (!val && event && event.target) {
115861                     val = event.target.value;
115862                 }
115863
115864                 corePreferences('validate-' + d, val);
115865                 context.validator().validate();
115866             }
115867
115868             return section;
115869         }
115870
115871         function uiSectionValidationRules(context) {
115872
115873             var MINSQUARE = 0;
115874             var MAXSQUARE = 20;
115875             var DEFAULTSQUARE = 5;  // see also unsquare_way.js
115876
115877             var section = uiSection('issues-rules', context)
115878                 .disclosureContent(renderDisclosureContent)
115879                 .title(_t('issues.rules.title'));
115880
115881             var _ruleKeys = context.validator().getRuleKeys()
115882                 .filter(function(key) { return key !== 'maprules'; })
115883                 .sort(function(key1, key2) {
115884                     // alphabetize by localized title
115885                     return _t('issues.' + key1 + '.title') < _t('issues.' + key2 + '.title') ? -1 : 1;
115886                 });
115887
115888             function renderDisclosureContent(selection) {
115889                 var container = selection.selectAll('.issues-rulelist-container')
115890                     .data([0]);
115891
115892                 var containerEnter = container.enter()
115893                     .append('div')
115894                     .attr('class', 'issues-rulelist-container');
115895
115896                 containerEnter
115897                     .append('ul')
115898                     .attr('class', 'layer-list issue-rules-list');
115899
115900                 var ruleLinks = containerEnter
115901                     .append('div')
115902                     .attr('class', 'issue-rules-links section-footer');
115903
115904                 ruleLinks
115905                     .append('a')
115906                     .attr('class', 'issue-rules-link')
115907                     .attr('href', '#')
115908                     .text(_t('issues.enable_all'))
115909                     .on('click', function() {
115910                         context.validator().disableRules([]);
115911                     });
115912
115913                 ruleLinks
115914                     .append('a')
115915                     .attr('class', 'issue-rules-link')
115916                     .attr('href', '#')
115917                     .text(_t('issues.disable_all'))
115918                     .on('click', function() {
115919                         context.validator().disableRules(_ruleKeys);
115920                     });
115921
115922
115923                 // Update
115924                 container = container
115925                     .merge(containerEnter);
115926
115927                 container.selectAll('.issue-rules-list')
115928                     .call(drawListItems, _ruleKeys, 'checkbox', 'rule', toggleRule, isRuleEnabled);
115929             }
115930
115931             function drawListItems(selection, data, type, name, change, active) {
115932                 var items = selection.selectAll('li')
115933                     .data(data);
115934
115935                 // Exit
115936                 items.exit()
115937                     .remove();
115938
115939                 // Enter
115940                 var enter = items.enter()
115941                     .append('li');
115942
115943                 if (name === 'rule') {
115944                     enter
115945                         .call(uiTooltip()
115946                             .title(function(d) { return _t('issues.' + d + '.tip'); })
115947                             .placement('top')
115948                         );
115949                 }
115950
115951                 var label = enter
115952                     .append('label');
115953
115954                 label
115955                     .append('input')
115956                     .attr('type', type)
115957                     .attr('name', name)
115958                     .on('change', change);
115959
115960                 label
115961                     .append('span')
115962                     .html(function(d) {
115963                         var params = {};
115964                         if (d === 'unsquare_way') {
115965                             params.val = '<span class="square-degrees"></span>';
115966                         }
115967                         return _t('issues.' + d + '.title', params);
115968                     });
115969
115970                 // Update
115971                 items = items
115972                     .merge(enter);
115973
115974                 items
115975                     .classed('active', active)
115976                     .selectAll('input')
115977                     .property('checked', active)
115978                     .property('indeterminate', false);
115979
115980
115981                 // user-configurable square threshold
115982                 var degStr = corePreferences('validate-square-degrees');
115983                 if (degStr === null) {
115984                     degStr = '' + DEFAULTSQUARE;
115985                 }
115986
115987                 var span = items.selectAll('.square-degrees');
115988                 var input = span.selectAll('.square-degrees-input')
115989                     .data([0]);
115990
115991                 // enter / update
115992                 input.enter()
115993                     .append('input')
115994                     .attr('type', 'number')
115995                     .attr('min', '' + MINSQUARE)
115996                     .attr('max', '' + MAXSQUARE)
115997                     .attr('step', '0.5')
115998                     .attr('class', 'square-degrees-input')
115999                     .call(utilNoAuto)
116000                     .on('click', function () {
116001                         event.preventDefault();
116002                         event.stopPropagation();
116003                         this.select();
116004                     })
116005                     .on('keyup', function () {
116006                         if (event.keyCode === 13) { // enter
116007                             this.blur();
116008                             this.select();
116009                         }
116010                     })
116011                     .on('blur', changeSquare)
116012                     .merge(input)
116013                     .property('value', degStr);
116014             }
116015
116016             function changeSquare() {
116017                 var input = select(this);
116018                 var degStr = utilGetSetValue(input).trim();
116019                 var degNum = parseFloat(degStr, 10);
116020
116021                 if (!isFinite(degNum)) {
116022                     degNum = DEFAULTSQUARE;
116023                 } else if (degNum > MAXSQUARE) {
116024                     degNum = MAXSQUARE;
116025                 } else if (degNum < MINSQUARE) {
116026                     degNum = MINSQUARE;
116027                 }
116028
116029                 degNum = Math.round(degNum * 10 ) / 10;   // round to 1 decimal
116030                 degStr = '' + degNum;
116031
116032                 input
116033                     .property('value', degStr);
116034
116035                 corePreferences('validate-square-degrees', degStr);
116036                 context.validator().reloadUnsquareIssues();
116037             }
116038
116039             function isRuleEnabled(d) {
116040                 return context.validator().isRuleEnabled(d);
116041             }
116042
116043             function toggleRule(d) {
116044                 context.validator().toggleRule(d);
116045             }
116046
116047             context.validator().on('validated.uiSectionValidationRules', function() {
116048                 window.requestIdleCallback(section.reRender);
116049             });
116050
116051             return section;
116052         }
116053
116054         function uiSectionValidationStatus(context) {
116055
116056             var section = uiSection('issues-status', context)
116057                 .content(renderContent)
116058                 .shouldDisplay(function() {
116059                     var issues = context.validator().getIssues(getOptions());
116060                     return issues.length === 0;
116061                 });
116062
116063             function getOptions() {
116064                 return {
116065                     what: corePreferences('validate-what') || 'edited',
116066                     where: corePreferences('validate-where') || 'all'
116067                 };
116068             }
116069
116070             function renderContent(selection) {
116071
116072                 var box = selection.selectAll('.box')
116073                     .data([0]);
116074
116075                 var boxEnter = box.enter()
116076                     .append('div')
116077                     .attr('class', 'box');
116078
116079                 boxEnter
116080                     .append('div')
116081                     .call(svgIcon('#iD-icon-apply', 'pre-text'));
116082
116083                 var noIssuesMessage = boxEnter
116084                     .append('span');
116085
116086                 noIssuesMessage
116087                     .append('strong')
116088                     .attr('class', 'message');
116089
116090                 noIssuesMessage
116091                     .append('br');
116092
116093                 noIssuesMessage
116094                     .append('span')
116095                     .attr('class', 'details');
116096
116097                 renderIgnoredIssuesReset(selection);
116098                 setNoIssuesText(selection);
116099             }
116100
116101             function renderIgnoredIssuesReset(selection) {
116102
116103                 var ignoredIssues = context.validator()
116104                     .getIssues({ what: 'all', where: 'all', includeDisabledRules: true, includeIgnored: 'only' });
116105
116106                 var resetIgnored = selection.selectAll('.reset-ignored')
116107                     .data(ignoredIssues.length ? [0] : []);
116108
116109                 // exit
116110                 resetIgnored.exit()
116111                     .remove();
116112
116113                 // enter
116114                 var resetIgnoredEnter = resetIgnored.enter()
116115                     .append('div')
116116                     .attr('class', 'reset-ignored section-footer');
116117
116118                 resetIgnoredEnter
116119                     .append('a')
116120                     .attr('href', '#');
116121
116122                 // update
116123                 resetIgnored = resetIgnored
116124                     .merge(resetIgnoredEnter);
116125
116126                 resetIgnored.select('a')
116127                     .text(_t('issues.reset_ignored', { count: ignoredIssues.length.toString() }));
116128
116129                 resetIgnored.on('click', function() {
116130                     context.validator().resetIgnoredIssues();
116131                 });
116132             }
116133
116134             function setNoIssuesText(selection) {
116135
116136                 var opts = getOptions();
116137
116138                 function checkForHiddenIssues(cases) {
116139                     for (var type in cases) {
116140                         var hiddenOpts = cases[type];
116141                         var hiddenIssues = context.validator().getIssues(hiddenOpts);
116142                         if (hiddenIssues.length) {
116143                             selection.select('.box .details')
116144                                 .text(_t(
116145                                     'issues.no_issues.hidden_issues.' + type,
116146                                     { count: hiddenIssues.length.toString() }
116147                                 ));
116148                             return;
116149                         }
116150                     }
116151                     selection.select('.box .details')
116152                         .text(_t('issues.no_issues.hidden_issues.none'));
116153                 }
116154
116155                 var messageType;
116156
116157                 if (opts.what === 'edited' && opts.where === 'visible') {
116158
116159                     messageType = 'edits_in_view';
116160
116161                     checkForHiddenIssues({
116162                         elsewhere: { what: 'edited', where: 'all' },
116163                         everything_else: { what: 'all', where: 'visible' },
116164                         disabled_rules: { what: 'edited', where: 'visible', includeDisabledRules: 'only' },
116165                         everything_else_elsewhere: { what: 'all', where: 'all' },
116166                         disabled_rules_elsewhere: { what: 'edited', where: 'all', includeDisabledRules: 'only' },
116167                         ignored_issues: { what: 'edited', where: 'visible', includeIgnored: 'only' },
116168                         ignored_issues_elsewhere: { what: 'edited', where: 'all', includeIgnored: 'only' }
116169                     });
116170
116171                 } else if (opts.what === 'edited' && opts.where === 'all') {
116172
116173                     messageType = 'edits';
116174
116175                     checkForHiddenIssues({
116176                         everything_else: { what: 'all', where: 'all' },
116177                         disabled_rules: { what: 'edited', where: 'all', includeDisabledRules: 'only' },
116178                         ignored_issues: { what: 'edited', where: 'all', includeIgnored: 'only' }
116179                     });
116180
116181                 } else if (opts.what === 'all' && opts.where === 'visible') {
116182
116183                     messageType = 'everything_in_view';
116184
116185                     checkForHiddenIssues({
116186                         elsewhere: { what: 'all', where: 'all' },
116187                         disabled_rules: { what: 'all', where: 'visible', includeDisabledRules: 'only' },
116188                         disabled_rules_elsewhere: { what: 'all', where: 'all', includeDisabledRules: 'only' },
116189                         ignored_issues: { what: 'all', where: 'visible', includeIgnored: 'only' },
116190                         ignored_issues_elsewhere: { what: 'all', where: 'all', includeIgnored: 'only' }
116191                     });
116192                 } else if (opts.what === 'all' && opts.where === 'all') {
116193
116194                     messageType = 'everything';
116195
116196                     checkForHiddenIssues({
116197                         disabled_rules: { what: 'all', where: 'all', includeDisabledRules: 'only' },
116198                         ignored_issues: { what: 'all', where: 'all', includeIgnored: 'only' }
116199                     });
116200                 }
116201
116202                 if (opts.what === 'edited' && context.history().difference().summary().length === 0) {
116203                     messageType = 'no_edits';
116204                 }
116205
116206                 selection.select('.box .message')
116207                     .text(_t('issues.no_issues.message.' + messageType));
116208
116209             }
116210
116211             context.validator().on('validated.uiSectionValidationStatus', function() {
116212                 window.requestIdleCallback(section.reRender);
116213             });
116214
116215             context.map().on('move.uiSectionValidationStatus',
116216                 debounce(function() {
116217                     window.requestIdleCallback(section.reRender);
116218                 }, 1000)
116219             );
116220
116221             return section;
116222         }
116223
116224         function uiPaneIssues(context) {
116225
116226             var issuesPane = uiPane('issues', context)
116227                 .key(_t('issues.key'))
116228                 .title(_t('issues.title'))
116229                 .description(_t('issues.title'))
116230                 .iconName('iD-icon-alert')
116231                 .sections([
116232                     uiSectionValidationOptions(context),
116233                     uiSectionValidationStatus(context),
116234                     uiSectionValidationIssues('issues-errors', 'error', context),
116235                     uiSectionValidationIssues('issues-warnings', 'warning', context),
116236                     uiSectionValidationRules(context)
116237                 ]);
116238
116239             return issuesPane;
116240         }
116241
116242         function uiSettingsCustomData(context) {
116243             var dispatch$1 = dispatch('change');
116244
116245             function render(selection) {
116246                 var dataLayer = context.layers().layer('data');
116247
116248                 // keep separate copies of original and current settings
116249                 var _origSettings = {
116250                     fileList: (dataLayer && dataLayer.fileList()) || null,
116251                     url: corePreferences('settings-custom-data-url')
116252                 };
116253                 var _currSettings = {
116254                     fileList: (dataLayer && dataLayer.fileList()) || null,
116255                     url: corePreferences('settings-custom-data-url')
116256                 };
116257
116258                 // var example = 'https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png';
116259                 var modal = uiConfirm(selection).okButton();
116260
116261                 modal
116262                     .classed('settings-modal settings-custom-data', true);
116263
116264                 modal.select('.modal-section.header')
116265                     .append('h3')
116266                     .text(_t('settings.custom_data.header'));
116267
116268
116269                 var textSection = modal.select('.modal-section.message-text');
116270
116271                 textSection
116272                     .append('pre')
116273                     .attr('class', 'instructions-file')
116274                     .text(_t('settings.custom_data.file.instructions'));
116275
116276                 textSection
116277                     .append('input')
116278                     .attr('class', 'field-file')
116279                     .attr('type', 'file')
116280                     .property('files', _currSettings.fileList)  // works for all except IE11
116281                     .on('change', function() {
116282                         var files = event.target.files;
116283                         if (files && files.length) {
116284                             _currSettings.url = '';
116285                             textSection.select('.field-url').property('value', '');
116286                             _currSettings.fileList = files;
116287                         } else {
116288                             _currSettings.fileList = null;
116289                         }
116290                     });
116291
116292                 textSection
116293                     .append('h4')
116294                     .text(_t('settings.custom_data.or'));
116295
116296                 textSection
116297                     .append('pre')
116298                     .attr('class', 'instructions-url')
116299                     .text(_t('settings.custom_data.url.instructions'));
116300
116301                 textSection
116302                     .append('textarea')
116303                     .attr('class', 'field-url')
116304                     .attr('placeholder', _t('settings.custom_data.url.placeholder'))
116305                     .call(utilNoAuto)
116306                     .property('value', _currSettings.url);
116307
116308
116309                 // insert a cancel button
116310                 var buttonSection = modal.select('.modal-section.buttons');
116311
116312                 buttonSection
116313                     .insert('button', '.ok-button')
116314                     .attr('class', 'button cancel-button secondary-action')
116315                     .text(_t('confirm.cancel'));
116316
116317
116318                 buttonSection.select('.cancel-button')
116319                     .on('click.cancel', clickCancel);
116320
116321                 buttonSection.select('.ok-button')
116322                     .attr('disabled', isSaveDisabled)
116323                     .on('click.save', clickSave);
116324
116325
116326                 function isSaveDisabled() {
116327                     return null;
116328                 }
116329
116330
116331                 // restore the original url
116332                 function clickCancel() {
116333                     textSection.select('.field-url').property('value', _origSettings.url);
116334                     corePreferences('settings-custom-data-url', _origSettings.url);
116335                     this.blur();
116336                     modal.close();
116337                 }
116338
116339                 // accept the current url
116340                 function clickSave() {
116341                     _currSettings.url = textSection.select('.field-url').property('value').trim();
116342
116343                     // one or the other but not both
116344                     if (_currSettings.url) { _currSettings.fileList = null; }
116345                     if (_currSettings.fileList) { _currSettings.url = ''; }
116346
116347                     corePreferences('settings-custom-data-url', _currSettings.url);
116348                     this.blur();
116349                     modal.close();
116350                     dispatch$1.call('change', this, _currSettings);
116351                 }
116352             }
116353
116354             return utilRebind(render, dispatch$1, 'on');
116355         }
116356
116357         function uiSectionDataLayers(context) {
116358
116359             var settingsCustomData = uiSettingsCustomData(context)
116360                 .on('change', customChanged);
116361
116362             var layers = context.layers();
116363
116364             var section = uiSection('data-layers', context)
116365                 .title(_t('map_data.data_layers'))
116366                 .disclosureContent(renderDisclosureContent);
116367
116368             function renderDisclosureContent(selection) {
116369                 var container = selection.selectAll('.data-layer-container')
116370                     .data([0]);
116371
116372                 container.enter()
116373                     .append('div')
116374                     .attr('class', 'data-layer-container')
116375                     .merge(container)
116376                     .call(drawOsmItems)
116377                     .call(drawQAItems)
116378                     .call(drawCustomDataItems)
116379                     .call(drawVectorItems)      // Beta - Detroit mapping challenge
116380                     .call(drawPanelItems);
116381             }
116382
116383             function showsLayer(which) {
116384                 var layer = layers.layer(which);
116385                 if (layer) {
116386                     return layer.enabled();
116387                 }
116388                 return false;
116389             }
116390
116391             function setLayer(which, enabled) {
116392                 // Don't allow layer changes while drawing - #6584
116393                 var mode = context.mode();
116394                 if (mode && /^draw/.test(mode.id)) return;
116395
116396                 var layer = layers.layer(which);
116397                 if (layer) {
116398                     layer.enabled(enabled);
116399
116400                     if (!enabled && (which === 'osm' || which === 'notes')) {
116401                         context.enter(modeBrowse(context));
116402                     }
116403                 }
116404             }
116405
116406             function toggleLayer(which) {
116407                 setLayer(which, !showsLayer(which));
116408             }
116409
116410             function drawOsmItems(selection) {
116411                 var osmKeys = ['osm', 'notes'];
116412                 var osmLayers = layers.all().filter(function(obj) { return osmKeys.indexOf(obj.id) !== -1; });
116413
116414                 var ul = selection
116415                     .selectAll('.layer-list-osm')
116416                     .data([0]);
116417
116418                 ul = ul.enter()
116419                     .append('ul')
116420                     .attr('class', 'layer-list layer-list-osm')
116421                     .merge(ul);
116422
116423                 var li = ul.selectAll('.list-item')
116424                     .data(osmLayers);
116425
116426                 li.exit()
116427                     .remove();
116428
116429                 var liEnter = li.enter()
116430                     .append('li')
116431                     .attr('class', function(d) { return 'list-item list-item-' + d.id; });
116432
116433                 var labelEnter = liEnter
116434                     .append('label')
116435                     .each(function(d) {
116436                         if (d.id === 'osm') {
116437                             select(this)
116438                                 .call(uiTooltip()
116439                                     .title(_t('map_data.layers.' + d.id + '.tooltip'))
116440                                     .keys([uiCmd('⌥' + _t('area_fill.wireframe.key'))])
116441                                     .placement('bottom')
116442                                 );
116443                         } else {
116444                             select(this)
116445                                 .call(uiTooltip()
116446                                     .title(_t('map_data.layers.' + d.id + '.tooltip'))
116447                                     .placement('bottom')
116448                                 );
116449                         }
116450                     });
116451
116452                 labelEnter
116453                     .append('input')
116454                     .attr('type', 'checkbox')
116455                     .on('change', function(d) { toggleLayer(d.id); });
116456
116457                 labelEnter
116458                     .append('span')
116459                     .text(function(d) { return _t('map_data.layers.' + d.id + '.title'); });
116460
116461
116462                 // Update
116463                 li
116464                     .merge(liEnter)
116465                     .classed('active', function (d) { return d.layer.enabled(); })
116466                     .selectAll('input')
116467                     .property('checked', function (d) { return d.layer.enabled(); });
116468             }
116469
116470             function drawQAItems(selection) {
116471                 var qaKeys = ['keepRight', 'improveOSM', 'osmose'];
116472                 var qaLayers = layers.all().filter(function(obj) { return qaKeys.indexOf(obj.id) !== -1; });
116473
116474                 var ul = selection
116475                     .selectAll('.layer-list-qa')
116476                     .data([0]);
116477
116478                 ul = ul.enter()
116479                     .append('ul')
116480                     .attr('class', 'layer-list layer-list-qa')
116481                     .merge(ul);
116482
116483                 var li = ul.selectAll('.list-item')
116484                     .data(qaLayers);
116485
116486                 li.exit()
116487                     .remove();
116488
116489                 var liEnter = li.enter()
116490                     .append('li')
116491                     .attr('class', function(d) { return 'list-item list-item-' + d.id; });
116492
116493                 var labelEnter = liEnter
116494                     .append('label')
116495                     .each(function(d) {
116496                         select(this)
116497                             .call(uiTooltip()
116498                                 .title(_t('map_data.layers.' + d.id + '.tooltip'))
116499                                 .placement('bottom')
116500                             );
116501                     });
116502
116503                 labelEnter
116504                     .append('input')
116505                     .attr('type', 'checkbox')
116506                     .on('change', function(d) { toggleLayer(d.id); });
116507
116508                 labelEnter
116509                     .append('span')
116510                     .text(function(d) { return _t('map_data.layers.' + d.id + '.title'); });
116511
116512
116513                 // Update
116514                 li
116515                     .merge(liEnter)
116516                     .classed('active', function (d) { return d.layer.enabled(); })
116517                     .selectAll('input')
116518                     .property('checked', function (d) { return d.layer.enabled(); });
116519             }
116520
116521             // Beta feature - sample vector layers to support Detroit Mapping Challenge
116522             // https://github.com/osmus/detroit-mapping-challenge
116523             function drawVectorItems(selection) {
116524                 var dataLayer = layers.layer('data');
116525                 var vtData = [
116526                     {
116527                         name: 'Detroit Neighborhoods/Parks',
116528                         src: 'neighborhoods-parks',
116529                         tooltip: 'Neighborhood boundaries and parks as compiled by City of Detroit in concert with community groups.',
116530                         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'
116531                     }, {
116532                         name: 'Detroit Composite POIs',
116533                         src: 'composite-poi',
116534                         tooltip: 'Fire Inspections, Business Licenses, and other public location data collated from the City of Detroit.',
116535                         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'
116536                     }, {
116537                         name: 'Detroit All-The-Places POIs',
116538                         src: 'alltheplaces-poi',
116539                         tooltip: 'Public domain business location data created by web scrapers.',
116540                         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'
116541                     }
116542                 ];
116543
116544                 // Only show this if the map is around Detroit..
116545                 var detroit = geoExtent([-83.5, 42.1], [-82.8, 42.5]);
116546                 var showVectorItems = (context.map().zoom() > 9 && detroit.contains(context.map().center()));
116547
116548                 var container = selection.selectAll('.vectortile-container')
116549                     .data(showVectorItems ? [0] : []);
116550
116551                 container.exit()
116552                     .remove();
116553
116554                 var containerEnter = container.enter()
116555                     .append('div')
116556                     .attr('class', 'vectortile-container');
116557
116558                 containerEnter
116559                     .append('h4')
116560                     .attr('class', 'vectortile-header')
116561                     .text('Detroit Vector Tiles (Beta)');
116562
116563                 containerEnter
116564                     .append('ul')
116565                     .attr('class', 'layer-list layer-list-vectortile');
116566
116567                 containerEnter
116568                     .append('div')
116569                     .attr('class', 'vectortile-footer')
116570                     .append('a')
116571                     .attr('target', '_blank')
116572                     .attr('tabindex', -1)
116573                     .call(svgIcon('#iD-icon-out-link', 'inline'))
116574                     .attr('href', 'https://github.com/osmus/detroit-mapping-challenge')
116575                     .append('span')
116576                     .text('About these layers');
116577
116578                 container = container
116579                     .merge(containerEnter);
116580
116581
116582                 var ul = container.selectAll('.layer-list-vectortile');
116583
116584                 var li = ul.selectAll('.list-item')
116585                     .data(vtData);
116586
116587                 li.exit()
116588                     .remove();
116589
116590                 var liEnter = li.enter()
116591                     .append('li')
116592                     .attr('class', function(d) { return 'list-item list-item-' + d.src; });
116593
116594                 var labelEnter = liEnter
116595                     .append('label')
116596                     .each(function(d) {
116597                         select(this).call(
116598                             uiTooltip().title(d.tooltip).placement('top')
116599                         );
116600                     });
116601
116602                 labelEnter
116603                     .append('input')
116604                     .attr('type', 'radio')
116605                     .attr('name', 'vectortile')
116606                     .on('change', selectVTLayer);
116607
116608                 labelEnter
116609                     .append('span')
116610                     .text(function(d) { return d.name; });
116611
116612                 // Update
116613                 li
116614                     .merge(liEnter)
116615                     .classed('active', isVTLayerSelected)
116616                     .selectAll('input')
116617                     .property('checked', isVTLayerSelected);
116618
116619
116620                 function isVTLayerSelected(d) {
116621                     return dataLayer && dataLayer.template() === d.template;
116622                 }
116623
116624                 function selectVTLayer(d) {
116625                     corePreferences('settings-custom-data-url', d.template);
116626                     if (dataLayer) {
116627                         dataLayer.template(d.template, d.src);
116628                         dataLayer.enabled(true);
116629                     }
116630                 }
116631             }
116632
116633             function drawCustomDataItems(selection) {
116634                 var dataLayer = layers.layer('data');
116635                 var hasData = dataLayer && dataLayer.hasData();
116636                 var showsData = hasData && dataLayer.enabled();
116637
116638                 var ul = selection
116639                     .selectAll('.layer-list-data')
116640                     .data(dataLayer ? [0] : []);
116641
116642                 // Exit
116643                 ul.exit()
116644                     .remove();
116645
116646                 // Enter
116647                 var ulEnter = ul.enter()
116648                     .append('ul')
116649                     .attr('class', 'layer-list layer-list-data');
116650
116651                 var liEnter = ulEnter
116652                     .append('li')
116653                     .attr('class', 'list-item-data');
116654
116655                 var labelEnter = liEnter
116656                     .append('label')
116657                     .call(uiTooltip()
116658                         .title(_t('map_data.layers.custom.tooltip'))
116659                         .placement('top')
116660                     );
116661
116662                 labelEnter
116663                     .append('input')
116664                     .attr('type', 'checkbox')
116665                     .on('change', function() { toggleLayer('data'); });
116666
116667                 labelEnter
116668                     .append('span')
116669                     .text(_t('map_data.layers.custom.title'));
116670
116671                 liEnter
116672                     .append('button')
116673                     .call(uiTooltip()
116674                         .title(_t('settings.custom_data.tooltip'))
116675                         .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
116676                     )
116677                     .on('click', editCustom)
116678                     .call(svgIcon('#iD-icon-more'));
116679
116680                 liEnter
116681                     .append('button')
116682                     .call(uiTooltip()
116683                         .title(_t('map_data.layers.custom.zoom'))
116684                         .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
116685                     )
116686                     .on('click', function() {
116687                         event.preventDefault();
116688                         event.stopPropagation();
116689                         dataLayer.fitZoom();
116690                     })
116691                     .call(svgIcon('#iD-icon-framed-dot'));
116692
116693                 // Update
116694                 ul = ul
116695                     .merge(ulEnter);
116696
116697                 ul.selectAll('.list-item-data')
116698                     .classed('active', showsData)
116699                     .selectAll('label')
116700                     .classed('deemphasize', !hasData)
116701                     .selectAll('input')
116702                     .property('disabled', !hasData)
116703                     .property('checked', showsData);
116704             }
116705
116706             function editCustom() {
116707                 event.preventDefault();
116708                 context.container()
116709                     .call(settingsCustomData);
116710             }
116711
116712             function customChanged(d) {
116713                 var dataLayer = layers.layer('data');
116714
116715                 if (d && d.url) {
116716                     dataLayer.url(d.url);
116717                 } else if (d && d.fileList) {
116718                     dataLayer.fileList(d.fileList);
116719                 }
116720             }
116721
116722
116723             function drawPanelItems(selection) {
116724
116725                 var panelsListEnter = selection.selectAll('.md-extras-list')
116726                     .data([0])
116727                     .enter()
116728                     .append('ul')
116729                     .attr('class', 'layer-list md-extras-list');
116730
116731                 var historyPanelLabelEnter = panelsListEnter
116732                     .append('li')
116733                     .attr('class', 'history-panel-toggle-item')
116734                     .append('label')
116735                     .call(uiTooltip()
116736                         .title(_t('map_data.history_panel.tooltip'))
116737                         .keys([uiCmd('⌘⇧' + _t('info_panels.history.key'))])
116738                         .placement('top')
116739                     );
116740
116741                 historyPanelLabelEnter
116742                     .append('input')
116743                     .attr('type', 'checkbox')
116744                     .on('change', function() {
116745                         event.preventDefault();
116746                         context.ui().info.toggle('history');
116747                     });
116748
116749                 historyPanelLabelEnter
116750                     .append('span')
116751                     .text(_t('map_data.history_panel.title'));
116752
116753                 var measurementPanelLabelEnter = panelsListEnter
116754                     .append('li')
116755                     .attr('class', 'measurement-panel-toggle-item')
116756                     .append('label')
116757                     .call(uiTooltip()
116758                         .title(_t('map_data.measurement_panel.tooltip'))
116759                         .keys([uiCmd('⌘⇧' + _t('info_panels.measurement.key'))])
116760                         .placement('top')
116761                     );
116762
116763                 measurementPanelLabelEnter
116764                     .append('input')
116765                     .attr('type', 'checkbox')
116766                     .on('change', function() {
116767                         event.preventDefault();
116768                         context.ui().info.toggle('measurement');
116769                     });
116770
116771                 measurementPanelLabelEnter
116772                     .append('span')
116773                     .text(_t('map_data.measurement_panel.title'));
116774             }
116775
116776             context.layers().on('change.uiSectionDataLayers', section.reRender);
116777
116778             context.map()
116779                 .on('move.uiSectionDataLayers',
116780                     debounce(function() {
116781                         // Detroit layers may have moved in or out of view
116782                         window.requestIdleCallback(section.reRender);
116783                     }, 1000)
116784                 );
116785
116786             return section;
116787         }
116788
116789         function uiSectionMapFeatures(context) {
116790
116791             var _features = context.features().keys();
116792
116793             var section = uiSection('map-features', context)
116794                 .title(_t('map_data.map_features'))
116795                 .disclosureContent(renderDisclosureContent)
116796                 .expandedByDefault(false);
116797
116798             function renderDisclosureContent(selection) {
116799
116800                 var container = selection.selectAll('.layer-feature-list-container')
116801                     .data([0]);
116802
116803                 var containerEnter = container.enter()
116804                     .append('div')
116805                     .attr('class', 'layer-feature-list-container');
116806
116807                 containerEnter
116808                     .append('ul')
116809                     .attr('class', 'layer-list layer-feature-list');
116810
116811                 var footer = containerEnter
116812                     .append('div')
116813                     .attr('class', 'feature-list-links section-footer');
116814
116815                 footer
116816                     .append('a')
116817                     .attr('class', 'feature-list-link')
116818                     .attr('href', '#')
116819                     .text(_t('issues.enable_all'))
116820                     .on('click', function() {
116821                         context.features().enableAll();
116822                     });
116823
116824                 footer
116825                     .append('a')
116826                     .attr('class', 'feature-list-link')
116827                     .attr('href', '#')
116828                     .text(_t('issues.disable_all'))
116829                     .on('click', function() {
116830                         context.features().disableAll();
116831                     });
116832
116833                 // Update
116834                 container = container
116835                     .merge(containerEnter);
116836
116837                 container.selectAll('.layer-feature-list')
116838                     .call(drawListItems, _features, 'checkbox', 'feature', clickFeature, showsFeature);
116839             }
116840
116841             function drawListItems(selection, data, type, name, change, active) {
116842                 var items = selection.selectAll('li')
116843                     .data(data);
116844
116845                 // Exit
116846                 items.exit()
116847                     .remove();
116848
116849                 // Enter
116850                 var enter = items.enter()
116851                     .append('li')
116852                     .call(uiTooltip()
116853                         .title(function(d) {
116854                             var tip = _t(name + '.' + d + '.tooltip');
116855                             if (autoHiddenFeature(d)) {
116856                                 var msg = showsLayer('osm') ? _t('map_data.autohidden') : _t('map_data.osmhidden');
116857                                 tip += '<div>' + msg + '</div>';
116858                             }
116859                             return tip;
116860                         })
116861                         .placement('top')
116862                     );
116863
116864                 var label = enter
116865                     .append('label');
116866
116867                 label
116868                     .append('input')
116869                     .attr('type', type)
116870                     .attr('name', name)
116871                     .on('change', change);
116872
116873                 label
116874                     .append('span')
116875                     .text(function(d) { return _t(name + '.' + d + '.description'); });
116876
116877                 // Update
116878                 items = items
116879                     .merge(enter);
116880
116881                 items
116882                     .classed('active', active)
116883                     .selectAll('input')
116884                     .property('checked', active)
116885                     .property('indeterminate', autoHiddenFeature);
116886             }
116887
116888             function autoHiddenFeature(d) {
116889                 return context.features().autoHidden(d);
116890             }
116891
116892             function showsFeature(d) {
116893                 return context.features().enabled(d);
116894             }
116895
116896             function clickFeature(d) {
116897                 context.features().toggle(d);
116898             }
116899
116900             function showsLayer(id) {
116901                 var layer = context.layers().layer(id);
116902                 return layer && layer.enabled();
116903             }
116904
116905             // add listeners
116906             context.features()
116907                 .on('change.map_features', section.reRender);
116908
116909             return section;
116910         }
116911
116912         function uiSectionMapStyleOptions(context) {
116913
116914             var section = uiSection('fill-area', context)
116915                 .title(_t('map_data.style_options'))
116916                 .disclosureContent(renderDisclosureContent)
116917                 .expandedByDefault(false);
116918
116919             function renderDisclosureContent(selection) {
116920                 var container = selection.selectAll('.layer-fill-list')
116921                     .data([0]);
116922
116923                 container.enter()
116924                     .append('ul')
116925                     .attr('class', 'layer-list layer-fill-list')
116926                     .merge(container)
116927                     .call(drawListItems, context.map().areaFillOptions, 'radio', 'area_fill', setFill, isActiveFill);
116928
116929                 var container2 = selection.selectAll('.layer-visual-diff-list')
116930                     .data([0]);
116931
116932                 container2.enter()
116933                     .append('ul')
116934                     .attr('class', 'layer-list layer-visual-diff-list')
116935                     .merge(container2)
116936                     .call(drawListItems, ['highlight_edits'], 'checkbox', 'visual_diff', toggleHighlightEdited, function() {
116937                         return context.surface().classed('highlight-edited');
116938                     });
116939             }
116940
116941             function drawListItems(selection, data, type, name, change, active) {
116942                 var items = selection.selectAll('li')
116943                     .data(data);
116944
116945                 // Exit
116946                 items.exit()
116947                     .remove();
116948
116949                 // Enter
116950                 var enter = items.enter()
116951                     .append('li')
116952                     .call(uiTooltip()
116953                         .title(function(d) {
116954                             return _t(name + '.' + d + '.tooltip');
116955                         })
116956                         .keys(function(d) {
116957                             var key = (d === 'wireframe' ? _t('area_fill.wireframe.key') : null);
116958                             if (d === 'highlight_edits') key = _t('map_data.highlight_edits.key');
116959                             return key ? [key] : null;
116960                         })
116961                         .placement('top')
116962                     );
116963
116964                 var label = enter
116965                     .append('label');
116966
116967                 label
116968                     .append('input')
116969                     .attr('type', type)
116970                     .attr('name', name)
116971                     .on('change', change);
116972
116973                 label
116974                     .append('span')
116975                     .text(function(d) { return _t(name + '.' + d + '.description'); });
116976
116977                 // Update
116978                 items = items
116979                     .merge(enter);
116980
116981                 items
116982                     .classed('active', active)
116983                     .selectAll('input')
116984                     .property('checked', active)
116985                     .property('indeterminate', false);
116986             }
116987
116988             function isActiveFill(d) {
116989                 return context.map().activeAreaFill() === d;
116990             }
116991
116992             function toggleHighlightEdited() {
116993                 event.preventDefault();
116994                 context.map().toggleHighlightEdited();
116995             }
116996
116997             function setFill(d) {
116998                 context.map().activeAreaFill(d);
116999             }
117000
117001             context.map()
117002                 .on('changeHighlighting.ui_style, changeAreaFill.ui_style', section.reRender);
117003
117004             return section;
117005         }
117006
117007         function uiSectionPhotoOverlays(context) {
117008
117009             var layers = context.layers();
117010
117011             var section = uiSection('photo-overlays', context)
117012                 .title(_t('photo_overlays.title'))
117013                 .disclosureContent(renderDisclosureContent)
117014                 .expandedByDefault(false);
117015
117016             function renderDisclosureContent(selection) {
117017                 var container = selection.selectAll('.photo-overlay-container')
117018                     .data([0]);
117019
117020                 container.enter()
117021                     .append('div')
117022                     .attr('class', 'photo-overlay-container')
117023                     .merge(container)
117024                     .call(drawPhotoItems)
117025                     .call(drawPhotoTypeItems);
117026             }
117027
117028             function drawPhotoItems(selection) {
117029                 var photoKeys = context.photos().overlayLayerIDs();
117030                 var photoLayers = layers.all().filter(function(obj) { return photoKeys.indexOf(obj.id) !== -1; });
117031                 var data = photoLayers.filter(function(obj) { return obj.layer.supported(); });
117032
117033                 function layerSupported(d) {
117034                     return d.layer && d.layer.supported();
117035                 }
117036                 function layerEnabled(d) {
117037                     return layerSupported(d) && d.layer.enabled();
117038                 }
117039
117040                 var ul = selection
117041                     .selectAll('.layer-list-photos')
117042                     .data([0]);
117043
117044                 ul = ul.enter()
117045                     .append('ul')
117046                     .attr('class', 'layer-list layer-list-photos')
117047                     .merge(ul);
117048
117049                 var li = ul.selectAll('.list-item-photos')
117050                     .data(data);
117051
117052                 li.exit()
117053                     .remove();
117054
117055                 var liEnter = li.enter()
117056                     .append('li')
117057                     .attr('class', function(d) {
117058                         var classes = 'list-item-photos list-item-' + d.id;
117059                         if (d.id === 'mapillary-signs' || d.id === 'mapillary-map-features') {
117060                             classes += ' indented';
117061                         }
117062                         return classes;
117063                     });
117064
117065                 var labelEnter = liEnter
117066                     .append('label')
117067                     .each(function(d) {
117068                         var titleID;
117069                         if (d.id === 'mapillary-signs') titleID = 'mapillary.signs.tooltip';
117070                         else if (d.id === 'mapillary') titleID = 'mapillary_images.tooltip';
117071                         else if (d.id === 'openstreetcam') titleID = 'openstreetcam_images.tooltip';
117072                         else titleID = d.id.replace(/-/g, '_') + '.tooltip';
117073                         select(this)
117074                             .call(uiTooltip()
117075                                 .title(_t(titleID))
117076                                 .placement('top')
117077                             );
117078                     });
117079
117080                 labelEnter
117081                     .append('input')
117082                     .attr('type', 'checkbox')
117083                     .on('change', function(d) { toggleLayer(d.id); });
117084
117085                 labelEnter
117086                     .append('span')
117087                     .text(function(d) {
117088                         var id = d.id;
117089                         if (id === 'mapillary-signs') id = 'photo_overlays.traffic_signs';
117090                         return _t(id.replace(/-/g, '_') + '.title');
117091                     });
117092
117093
117094                 // Update
117095                 li
117096                     .merge(liEnter)
117097                     .classed('active', layerEnabled)
117098                     .selectAll('input')
117099                     .property('checked', layerEnabled);
117100             }
117101
117102             function drawPhotoTypeItems(selection) {
117103                 var data = context.photos().allPhotoTypes();
117104
117105                 function typeEnabled(d) {
117106                     return context.photos().showsPhotoType(d);
117107                 }
117108
117109                 var ul = selection
117110                     .selectAll('.layer-list-photo-types')
117111                     .data(context.photos().shouldFilterByPhotoType() ? [0] : []);
117112
117113                 ul.exit()
117114                     .remove();
117115
117116                 ul = ul.enter()
117117                     .append('ul')
117118                     .attr('class', 'layer-list layer-list-photo-types')
117119                     .merge(ul);
117120
117121                 var li = ul.selectAll('.list-item-photo-types')
117122                     .data(data);
117123
117124                 li.exit()
117125                     .remove();
117126
117127                 var liEnter = li.enter()
117128                     .append('li')
117129                     .attr('class', function(d) {
117130                         return 'list-item-photo-types list-item-' + d;
117131                     });
117132
117133                 var labelEnter = liEnter
117134                     .append('label')
117135                     .each(function(d) {
117136                         select(this)
117137                             .call(uiTooltip()
117138                                 .title(_t('photo_overlays.photo_type.' + d + '.tooltip'))
117139                                 .placement('top')
117140                             );
117141                     });
117142
117143                 labelEnter
117144                     .append('input')
117145                     .attr('type', 'checkbox')
117146                     .on('change', function(d) {
117147                         context.photos().togglePhotoType(d);
117148                     });
117149
117150                 labelEnter
117151                     .append('span')
117152                     .text(function(d) {
117153                         return _t('photo_overlays.photo_type.' + d + '.title');
117154                     });
117155
117156
117157                 // Update
117158                 li
117159                     .merge(liEnter)
117160                     .classed('active', typeEnabled)
117161                     .selectAll('input')
117162                     .property('checked', typeEnabled);
117163             }
117164
117165             function toggleLayer(which) {
117166                 setLayer(which, !showsLayer(which));
117167             }
117168
117169             function showsLayer(which) {
117170                 var layer = layers.layer(which);
117171                 if (layer) {
117172                     return layer.enabled();
117173                 }
117174                 return false;
117175             }
117176
117177             function setLayer(which, enabled) {
117178                 var layer = layers.layer(which);
117179                 if (layer) {
117180                     layer.enabled(enabled);
117181                 }
117182             }
117183
117184             context.layers().on('change.uiSectionPhotoOverlays', section.reRender);
117185             context.photos().on('change.uiSectionPhotoOverlays', section.reRender);
117186
117187             return section;
117188         }
117189
117190         function uiPaneMapData(context) {
117191
117192             var mapDataPane = uiPane('map-data', context)
117193                 .key(_t('map_data.key'))
117194                 .title(_t('map_data.title'))
117195                 .description(_t('map_data.description'))
117196                 .iconName('iD-icon-data')
117197                 .sections([
117198                     uiSectionDataLayers(context),
117199                     uiSectionPhotoOverlays(context),
117200                     uiSectionMapStyleOptions(context),
117201                     uiSectionMapFeatures(context)
117202                 ]);
117203
117204             return mapDataPane;
117205         }
117206
117207         function uiSectionPrivacy(context) {
117208
117209             let section = uiSection('preferences-third-party', context)
117210               .title(_t('preferences.privacy.title'))
117211               .disclosureContent(renderDisclosureContent);
117212
117213             let _showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true';
117214
117215             function renderDisclosureContent(selection) {
117216               // enter
117217               let privacyOptionsListEnter = selection.selectAll('.privacy-options-list')
117218                 .data([0])
117219                 .enter()
117220                 .append('ul')
117221                 .attr('class', 'layer-list privacy-options-list');
117222
117223               let thirdPartyIconsEnter = privacyOptionsListEnter
117224                 .append('li')
117225                 .attr('class', 'privacy-third-party-icons-item')
117226                 .append('label')
117227                 .call(uiTooltip()
117228                   .title(_t('preferences.privacy.third_party_icons.tooltip'))
117229                   .placement('bottom')
117230                 );
117231
117232               thirdPartyIconsEnter
117233                 .append('input')
117234                 .attr('type', 'checkbox')
117235                 .on('change', () => {
117236                   event.preventDefault();
117237                   _showThirdPartyIcons = (_showThirdPartyIcons === 'true') ? 'false' : 'true';
117238                   corePreferences('preferences.privacy.thirdpartyicons', _showThirdPartyIcons);
117239                   update();
117240                 });
117241
117242               thirdPartyIconsEnter
117243                 .append('span')
117244                 .text(_t('preferences.privacy.third_party_icons.description'));
117245
117246
117247               // Privacy Policy link
117248               selection.selectAll('.privacy-link')
117249                 .data([0])
117250                 .enter()
117251                 .append('div')
117252                 .attr('class', 'privacy-link')
117253                 .append('a')
117254                 .attr('target', '_blank')
117255                 .call(svgIcon('#iD-icon-out-link', 'inline'))
117256                 .attr('href', 'https://github.com/openstreetmap/iD/blob/release/PRIVACY.md')
117257                 .append('span')
117258                 .text(_t('preferences.privacy.privacy_link'));
117259
117260               update();
117261
117262
117263               function update() {
117264                 selection.selectAll('.privacy-third-party-icons-item')
117265                   .classed('active', (_showThirdPartyIcons === 'true'))
117266                   .select('input')
117267                   .property('checked', (_showThirdPartyIcons === 'true'));
117268               }
117269             }
117270
117271             return section;
117272         }
117273
117274         function uiPanePreferences(context) {
117275
117276           let preferencesPane = uiPane('preferences', context)
117277             .key(_t('preferences.key'))
117278             .title(_t('preferences.title'))
117279             .description(_t('preferences.description'))
117280             .iconName('fas-user-cog')
117281             .sections([
117282                 uiSectionPrivacy(context)
117283             ]);
117284
117285           return preferencesPane;
117286         }
117287
117288         function uiInit(context) {
117289             var _initCounter = 0;
117290             var _needWidth = {};
117291
117292             var _lastPointerType;
117293
117294
117295             function render(container) {
117296
117297                 container
117298                     .on('click.ui', function() {
117299                         // we're only concerned with the primary mouse button
117300                         if (event.button !== 0) return;
117301
117302                         if (!event.composedPath) return;
117303
117304                         // some targets have default click events we don't want to override
117305                         var isOkayTarget = event.composedPath().some(function(node) {
117306                             // clicking <label> affects its <input> by default
117307                             return node.nodeName === 'LABEL' ||
117308                                 // clicking <a> opens a hyperlink by default
117309                                 node.nodeName === 'A';
117310                         });
117311                         if (isOkayTarget) return;
117312
117313                         // disable double-tap-to-zoom on touchscreens
117314                         event.preventDefault();
117315                     });
117316
117317                 var detected = utilDetect();
117318
117319                 // only WebKit supports gesture events
117320                 if ('GestureEvent' in window &&
117321                     // Listening for gesture events on iOS 13.4+ breaks double-tapping,
117322                     // but we only need to do this on desktop Safari anyway. – #7694
117323                     !detected.isMobileWebKit) {
117324
117325                     // On iOS we disable pinch-to-zoom of the UI via the `touch-action`
117326                     // CSS property, but on desktop Safari we need to manually cancel the
117327                     // default gesture events.
117328                     container.on('gesturestart.ui gesturechange.ui gestureend.ui', function() {
117329                         // disable pinch-to-zoom of the UI via multitouch trackpads on macOS Safari
117330                         event.preventDefault();
117331                     });
117332                 }
117333
117334                 if ('PointerEvent' in window) {
117335                     select(window)
117336                         .on('pointerdown.ui pointerup.ui', function() {
117337                             var pointerType = event.pointerType || 'mouse';
117338                             if (_lastPointerType !== pointerType) {
117339                                 _lastPointerType = pointerType;
117340                                 container
117341                                     .attr('pointer', pointerType);
117342                             }
117343                         }, true);
117344                 } else {
117345                     _lastPointerType = 'mouse';
117346                     container
117347                         .attr('pointer', 'mouse');
117348                 }
117349
117350                 container
117351                     .attr('dir', _mainLocalizer.textDirection());
117352
117353                 // setup fullscreen keybindings (no button shown at this time)
117354                 container
117355                     .call(uiFullScreen(context));
117356
117357                 var map = context.map();
117358                 map.redrawEnable(false);  // don't draw until we've set zoom/lat/long
117359
117360                 map
117361                     .on('hitMinZoom.ui', function() {
117362                         ui.flash.text(_t('cannot_zoom'))();
117363                     });
117364
117365                 container
117366                     .append('svg')
117367                     .attr('id', 'ideditor-defs')
117368                     .call(svgDefs(context));
117369
117370                 container
117371                     .append('div')
117372                     .attr('class', 'sidebar')
117373                     .call(ui.sidebar);
117374
117375                 var content = container
117376                     .append('div')
117377                     .attr('class', 'main-content active');
117378
117379                 // Top toolbar
117380                 content
117381                     .append('div')
117382                     .attr('class', 'top-toolbar-wrap')
117383                     .append('div')
117384                     .attr('class', 'top-toolbar fillD')
117385                     .call(uiTopToolbar(context));
117386
117387                 content
117388                     .append('div')
117389                     .attr('class', 'main-map')
117390                     .attr('dir', 'ltr')
117391                     .call(map);
117392
117393                 content
117394                     .append('div')
117395                     .attr('class', 'spinner')
117396                     .call(uiSpinner(context));
117397
117398                 // Add attribution and footer
117399                 var about = content
117400                     .append('div')
117401                     .attr('class', 'map-footer');
117402
117403                 about
117404                     .append('div')
117405                     .attr('class', 'attribution-wrap')
117406                     .attr('dir', 'ltr')
117407                     .call(uiAttribution(context));
117408
117409                 about
117410                     .append('div')
117411                     .attr('class', 'api-status')
117412                     .call(uiStatus(context));
117413
117414
117415                 var footer = about
117416                     .append('div')
117417                     .attr('class', 'map-footer-bar fillD');
117418
117419                 footer
117420                     .append('div')
117421                     .attr('class', 'flash-wrap footer-hide');
117422
117423                 var footerWrap = footer
117424                     .append('div')
117425                     .attr('class', 'main-footer-wrap footer-show');
117426
117427                 footerWrap
117428                     .append('div')
117429                     .attr('class', 'scale-block')
117430                     .call(uiScale(context));
117431
117432                 var aboutList = footerWrap
117433                     .append('div')
117434                     .attr('class', 'info-block')
117435                     .append('ul')
117436                     .attr('class', 'map-footer-list');
117437
117438                 if (!context.embed()) {
117439                     aboutList
117440                         .call(uiAccount(context));
117441                 }
117442
117443                 aboutList
117444                     .append('li')
117445                     .attr('class', 'version')
117446                     .call(uiVersion(context));
117447
117448                 var issueLinks = aboutList
117449                     .append('li');
117450
117451                 issueLinks
117452                     .append('a')
117453                     .attr('target', '_blank')
117454                     .attr('href', 'https://github.com/openstreetmap/iD/issues')
117455                     .call(svgIcon('#iD-icon-bug', 'light'))
117456                     .call(uiTooltip().title(_t('report_a_bug')).placement('top'));
117457
117458                 issueLinks
117459                     .append('a')
117460                     .attr('target', '_blank')
117461                     .attr('href', 'https://github.com/openstreetmap/iD/blob/develop/CONTRIBUTING.md#translating')
117462                     .call(svgIcon('#iD-icon-translate', 'light'))
117463                     .call(uiTooltip().title(_t('help_translate')).placement('top'));
117464
117465                 aboutList
117466                     .append('li')
117467                     .attr('class', 'feature-warning')
117468                     .attr('tabindex', -1)
117469                     .call(uiFeatureInfo(context));
117470
117471                 aboutList
117472                     .append('li')
117473                     .attr('class', 'issues-info')
117474                     .attr('tabindex', -1)
117475                     .call(uiIssuesInfo(context));
117476
117477                 var apiConnections = context.apiConnections();
117478                 if (apiConnections && apiConnections.length > 1) {
117479                     aboutList
117480                         .append('li')
117481                         .attr('class', 'source-switch')
117482                         .attr('tabindex', -1)
117483                         .call(uiSourceSwitch(context)
117484                             .keys(apiConnections)
117485                         );
117486                 }
117487
117488                 aboutList
117489                     .append('li')
117490                     .attr('class', 'user-list')
117491                     .attr('tabindex', -1)
117492                     .call(uiContributors(context));
117493
117494
117495                 // Setup map dimensions and move map to initial center/zoom.
117496                 // This should happen after .main-content and toolbars exist.
117497                 ui.onResize();
117498                 map.redrawEnable(true);
117499
117500                 ui.hash = behaviorHash(context);
117501                 ui.hash();
117502                 if (!ui.hash.hadHash) {
117503                     map.centerZoom([0, 0], 2);
117504                 }
117505
117506
117507                 var overMap = content
117508                     .append('div')
117509                     .attr('class', 'over-map');
117510
117511                 // Map controls
117512                 var controls = overMap
117513                     .append('div')
117514                     .attr('class', 'map-controls');
117515
117516                 controls
117517                     .append('div')
117518                     .attr('class', 'map-control zoombuttons')
117519                     .call(uiZoom(context));
117520
117521                 controls
117522                     .append('div')
117523                     .attr('class', 'map-control zoom-to-selection-control')
117524                     .call(uiZoomToSelection(context));
117525
117526                 controls
117527                     .append('div')
117528                     .attr('class', 'map-control geolocate-control')
117529                     .call(uiGeolocate(context));
117530
117531                 // Add panes
117532                 // This should happen after map is initialized, as some require surface()
117533                 var panes = overMap
117534                     .append('div')
117535                     .attr('class', 'map-panes');
117536
117537                 var uiPanes = [
117538                     uiPaneBackground(context),
117539                     uiPaneMapData(context),
117540                     uiPaneIssues(context),
117541                     uiPanePreferences(context),
117542                     uiPaneHelp(context)
117543                 ];
117544
117545                 uiPanes.forEach(function(pane) {
117546                     controls
117547                         .append('div')
117548                         .attr('class', 'map-control map-pane-control ' + pane.id + '-control')
117549                         .call(pane.renderToggleButton);
117550
117551                     panes
117552                         .call(pane.renderPane);
117553                 });
117554
117555                 ui.info = uiInfo(context);
117556
117557                 // Add absolutely-positioned elements that sit on top of the map
117558                 // This should happen after the map is ready (center/zoom)
117559                 overMap
117560                     .call(uiMapInMap(context))
117561                     .call(ui.info)
117562                     .call(uiNotice(context));
117563
117564
117565                 overMap
117566                     .append('div')
117567                     .attr('class', 'photoviewer')
117568                     .classed('al', true)       // 'al'=left,  'ar'=right
117569                     .classed('hide', true)
117570                     .call(ui.photoviewer);
117571
117572
117573                 // Bind events
117574                 window.onbeforeunload = function() {
117575                     return context.save();
117576                 };
117577                 window.onunload = function() {
117578                     context.history().unlock();
117579                 };
117580
117581                 select(window)
117582                     .on('resize.editor', ui.onResize);
117583
117584
117585                 var panPixels = 80;
117586                 context.keybinding()
117587                     .on('⌫', function() { event.preventDefault(); })
117588                     .on([_t('sidebar.key'), '`', '²', '@'], ui.sidebar.toggle)   // #5663, #6864 - common QWERTY, AZERTY
117589                     .on('←', pan([panPixels, 0]))
117590                     .on('↑', pan([0, panPixels]))
117591                     .on('→', pan([-panPixels, 0]))
117592                     .on('↓', pan([0, -panPixels]))
117593                     .on(uiCmd('⌘←'), pan([map.dimensions()[0], 0]))
117594                     .on(uiCmd('⌘↑'), pan([0, map.dimensions()[1]]))
117595                     .on(uiCmd('⌘→'), pan([-map.dimensions()[0], 0]))
117596                     .on(uiCmd('⌘↓'), pan([0, -map.dimensions()[1]]))
117597                     .on(uiCmd('⌘' + _t('background.key')), function quickSwitch() {
117598                         if (event) {
117599                             event.stopImmediatePropagation();
117600                             event.preventDefault();
117601                         }
117602                         var previousBackground = context.background().findSource(corePreferences('background-last-used-toggle'));
117603                         if (previousBackground) {
117604                             var currentBackground = context.background().baseLayerSource();
117605                             corePreferences('background-last-used-toggle', currentBackground.id);
117606                             corePreferences('background-last-used', previousBackground.id);
117607                             context.background().baseLayerSource(previousBackground);
117608                         }
117609                     })
117610                     .on(_t('area_fill.wireframe.key'), function toggleWireframe() {
117611                         event.preventDefault();
117612                         event.stopPropagation();
117613                         context.map().toggleWireframe();
117614                     })
117615                     .on(uiCmd('⌥' + _t('area_fill.wireframe.key')), function toggleOsmData() {
117616                         event.preventDefault();
117617                         event.stopPropagation();
117618
117619                         // Don't allow layer changes while drawing - #6584
117620                         var mode = context.mode();
117621                         if (mode && /^draw/.test(mode.id)) return;
117622
117623                         var layer = context.layers().layer('osm');
117624                         if (layer) {
117625                             layer.enabled(!layer.enabled());
117626                             if (!layer.enabled()) {
117627                                 context.enter(modeBrowse(context));
117628                             }
117629                         }
117630                     })
117631                     .on(_t('map_data.highlight_edits.key'), function toggleHighlightEdited() {
117632                         event.preventDefault();
117633                         context.map().toggleHighlightEdited();
117634                     });
117635
117636                 context
117637                     .on('enter.editor', function(entered) {
117638                         container
117639                             .classed('mode-' + entered.id, true);
117640                     })
117641                     .on('exit.editor', function(exited) {
117642                         container
117643                             .classed('mode-' + exited.id, false);
117644                     });
117645
117646                 context.enter(modeBrowse(context));
117647
117648                 if (!_initCounter++) {
117649                     if (!ui.hash.startWalkthrough) {
117650                         context.container()
117651                             .call(uiSplash(context))
117652                             .call(uiRestore(context));
117653                     }
117654
117655                     context.container()
117656                         .call(uiShortcuts(context));
117657                 }
117658
117659                 var osm = context.connection();
117660                 var auth = uiLoading(context).message(_t('loading_auth')).blocking(true);
117661
117662                 if (osm && auth) {
117663                     osm
117664                         .on('authLoading.ui', function() {
117665                             context.container()
117666                                 .call(auth);
117667                         })
117668                         .on('authDone.ui', function() {
117669                             auth.close();
117670                         });
117671                 }
117672
117673                 _initCounter++;
117674
117675                 if (ui.hash.startWalkthrough) {
117676                     ui.hash.startWalkthrough = false;
117677                     context.container().call(uiIntro(context));
117678                 }
117679
117680
117681                 function pan(d) {
117682                     return function() {
117683                         if (event.shiftKey) return;
117684                         if (context.container().select('.combobox').size()) return;
117685                         event.preventDefault();
117686                         context.map().pan(d, 100);
117687                     };
117688                 }
117689
117690             }
117691
117692
117693             let ui = {};
117694
117695             let _loadPromise;
117696             // renders the iD interface into the container node
117697             ui.ensureLoaded = () => {
117698
117699                 if (_loadPromise) return _loadPromise;
117700
117701                 return _loadPromise = Promise.all([
117702                         // must have strings and presets before loading the UI
117703                         _mainLocalizer.ensureLoaded(),
117704                         _mainPresetIndex.ensureLoaded()
117705                     ])
117706                     .then(() => {
117707                         if (!context.container().empty()) render(context.container());
117708                     })
117709                     .catch(err => console.error(err));  // eslint-disable-line
117710             };
117711
117712
117713             // `ui.restart()` will destroy and rebuild the entire iD interface,
117714             // for example to switch the locale while iD is running.
117715             ui.restart = function() {
117716                 context.keybinding().clear();
117717
117718                 _loadPromise = null;
117719
117720                 context.container().selectAll('*').remove();
117721
117722                 ui.ensureLoaded();
117723             };
117724
117725             ui.lastPointerType = function() {
117726                 return _lastPointerType;
117727             };
117728
117729             ui.flash = uiFlash(context);
117730
117731             ui.sidebar = uiSidebar(context);
117732
117733             ui.photoviewer = uiPhotoviewer(context);
117734
117735             ui.onResize = function(withPan) {
117736                 var map = context.map();
117737
117738                 // Recalc dimensions of map and sidebar.. (`true` = force recalc)
117739                 // This will call `getBoundingClientRect` and trigger reflow,
117740                 //  but the values will be cached for later use.
117741                 var mapDimensions = utilGetDimensions(context.container().select('.main-content'), true);
117742                 utilGetDimensions(context.container().select('.sidebar'), true);
117743
117744                 if (withPan !== undefined) {
117745                     map.redrawEnable(false);
117746                     map.pan(withPan);
117747                     map.redrawEnable(true);
117748                 }
117749                 map.dimensions(mapDimensions);
117750
117751                 ui.photoviewer.onMapResize();
117752
117753                 // check if header or footer have overflowed
117754                 ui.checkOverflow('.top-toolbar');
117755                 ui.checkOverflow('.map-footer-bar');
117756
117757                 // Use outdated code so it works on Explorer
117758                 var resizeWindowEvent = document.createEvent('Event');
117759
117760                 resizeWindowEvent.initEvent('resizeWindow', true, true);
117761
117762                 document.dispatchEvent(resizeWindowEvent);
117763             };
117764
117765
117766             // Call checkOverflow when resizing or whenever the contents change.
117767             ui.checkOverflow = function(selector, reset) {
117768                 if (reset) {
117769                     delete _needWidth[selector];
117770                 }
117771
117772                 var element = select(selector);
117773                 var scrollWidth = element.property('scrollWidth');
117774                 var clientWidth = element.property('clientWidth');
117775                 var needed = _needWidth[selector] || scrollWidth;
117776
117777                 if (scrollWidth > clientWidth) {    // overflow happening
117778                     element.classed('narrow', true);
117779                     if (!_needWidth[selector]) {
117780                         _needWidth[selector] = scrollWidth;
117781                     }
117782
117783                 } else if (scrollWidth >= needed) {
117784                     element.classed('narrow', false);
117785                 }
117786             };
117787
117788             ui.togglePanes = function(showPane) {
117789                 var shownPanes = context.container().selectAll('.map-pane.shown');
117790
117791                 var side = _mainLocalizer.textDirection() === 'ltr' ? 'right' : 'left';
117792
117793                 shownPanes
117794                     .classed('shown', false);
117795
117796                 context.container().selectAll('.map-pane-control button')
117797                     .classed('active', false);
117798
117799                 if (showPane) {
117800                     shownPanes
117801                         .style('display', 'none')
117802                         .style(side, '-500px');
117803
117804                     context.container().selectAll('.' + showPane.attr('pane') + '-control button')
117805                         .classed('active', true);
117806
117807                     showPane
117808                         .classed('shown', true)
117809                         .style('display', 'block');
117810                     if (shownPanes.empty()) {
117811                         showPane
117812                             .style('display', 'block')
117813                             .style(side, '-500px')
117814                             .transition()
117815                             .duration(200)
117816                             .style(side, '0px');
117817                     } else {
117818                         showPane
117819                             .style(side, '0px');
117820                     }
117821                 } else {
117822                     shownPanes
117823                         .style('display', 'block')
117824                         .style(side, '0px')
117825                         .transition()
117826                         .duration(200)
117827                         .style(side, '-500px')
117828                         .on('end', function() {
117829                             select(this).style('display', 'none');
117830                         });
117831                 }
117832             };
117833
117834
117835             var _editMenu = uiEditMenu(context);
117836
117837             ui.editMenu = function() {
117838                 return _editMenu;
117839             };
117840
117841             ui.showEditMenu = function(anchorPoint, triggerType, operations) {
117842
117843                 // remove any displayed menu
117844                 ui.closeEditMenu();
117845
117846                 if (!operations && context.mode().operations) operations = context.mode().operations();
117847                 if (!operations || !operations.length) return;
117848
117849                 // disable menu if in wide selection, for example
117850                 if (!context.map().editableDataEnabled()) return;
117851
117852                 var surfaceNode = context.surface().node();
117853                 if (surfaceNode.focus) {   // FF doesn't support it
117854                     // focus the surface or else clicking off the menu may not trigger modeBrowse
117855                     surfaceNode.focus();
117856                 }
117857
117858                 operations.forEach(function(operation) {
117859                     if (operation.point) operation.point(anchorPoint);
117860                 });
117861
117862                 _editMenu
117863                     .anchorLoc(anchorPoint)
117864                     .triggerType(triggerType)
117865                     .operations(operations);
117866
117867                 // render the menu
117868                 context.map().supersurface.call(_editMenu);
117869             };
117870
117871             ui.closeEditMenu = function() {
117872                 // remove any existing menu no matter how it was added
117873                 context.map().supersurface
117874                     .select('.edit-menu').remove();
117875             };
117876
117877
117878             var _saveLoading = select(null);
117879
117880             context.uploader()
117881                 .on('saveStarted.ui', function() {
117882                     _saveLoading = uiLoading(context)
117883                         .message(_t('save.uploading'))
117884                         .blocking(true);
117885                     context.container().call(_saveLoading);  // block input during upload
117886                 })
117887                 .on('saveEnded.ui', function() {
117888                     _saveLoading.close();
117889                     _saveLoading = select(null);
117890                 });
117891
117892             return ui;
117893         }
117894
117895         function coreContext() {
117896           const dispatch$1 = dispatch('enter', 'exit', 'change');
117897           let context = utilRebind({}, dispatch$1, 'on');
117898           let _deferred = new Set();
117899
117900           context.version = '2.18.0';
117901           context.privacyVersion = '20200407';
117902
117903           // iD will alter the hash so cache the parameters intended to setup the session
117904           context.initialHashParams = window.location.hash ? utilStringQs(window.location.hash) : {};
117905
117906           context.isFirstSession = !corePreferences('sawSplash') && !corePreferences('sawPrivacyVersion');
117907
117908
117909           /* Changeset */
117910           // An osmChangeset object. Not loaded until needed.
117911           context.changeset = null;
117912
117913           let _defaultChangesetComment = context.initialHashParams.comment;
117914           let _defaultChangesetSource = context.initialHashParams.source;
117915           let _defaultChangesetHashtags = context.initialHashParams.hashtags;
117916           context.defaultChangesetComment = function(val) {
117917             if (!arguments.length) return _defaultChangesetComment;
117918             _defaultChangesetComment = val;
117919             return context;
117920           };
117921           context.defaultChangesetSource = function(val) {
117922             if (!arguments.length) return _defaultChangesetSource;
117923             _defaultChangesetSource = val;
117924             return context;
117925           };
117926           context.defaultChangesetHashtags = function(val) {
117927             if (!arguments.length) return _defaultChangesetHashtags;
117928             _defaultChangesetHashtags = val;
117929             return context;
117930           };
117931
117932           /* Document title */
117933           /* (typically shown as the label for the browser window/tab) */
117934
117935           // If true, iD will update the title based on what the user is doing
117936           let _setsDocumentTitle = true;
117937           context.setsDocumentTitle = function(val) {
117938             if (!arguments.length) return _setsDocumentTitle;
117939             _setsDocumentTitle = val;
117940             return context;
117941           };
117942           // The part of the title that is always the same
117943           let _documentTitleBase = document.title;
117944           context.documentTitleBase = function(val) {
117945             if (!arguments.length) return _documentTitleBase;
117946             _documentTitleBase = val;
117947             return context;
117948           };
117949
117950
117951           /* User interface and keybinding */
117952           let _ui;
117953           context.ui = () => _ui;
117954           context.lastPointerType = () => _ui.lastPointerType();
117955
117956           let _keybinding = utilKeybinding('context');
117957           context.keybinding = () => _keybinding;
117958           select(document).call(_keybinding);
117959
117960
117961           /* Straight accessors. Avoid using these if you can. */
117962           let _connection;
117963           let _history;
117964           let _validator;
117965           let _uploader;
117966           context.connection = () => _connection;
117967           context.history = () => _history;
117968           context.validator = () => _validator;
117969           context.uploader = () => _uploader;
117970
117971           /* Connection */
117972           context.preauth = (options) => {
117973             if (_connection) {
117974               _connection.switch(options);
117975             }
117976             return context;
117977           };
117978
117979           /* connection options for source switcher (optional) */
117980           let _apiConnections;
117981           context.apiConnections = function(val) {
117982             if (!arguments.length) return _apiConnections;
117983             _apiConnections = val;
117984             return context;
117985           };
117986
117987
117988           function afterLoad(cid, callback) {
117989             return (err, result) => {
117990               if (err) {
117991                 // 400 Bad Request, 401 Unauthorized, 403 Forbidden..
117992                 if (err.status === 400 || err.status === 401 || err.status === 403) {
117993                   if (_connection) {
117994                     _connection.logout();
117995                   }
117996                 }
117997                 if (typeof callback === 'function') {
117998                   callback(err);
117999                 }
118000                 return;
118001
118002               } else if (_connection && _connection.getConnectionId() !== cid) {
118003                 if (typeof callback === 'function') {
118004                   callback({ message: 'Connection Switched', status: -1 });
118005                 }
118006                 return;
118007
118008               } else {
118009                 _history.merge(result.data, result.extent);
118010                 if (typeof callback === 'function') {
118011                   callback(err, result);
118012                 }
118013                 return;
118014               }
118015             };
118016           }
118017
118018
118019           context.loadTiles = (projection, callback) => {
118020             const handle = window.requestIdleCallback(() => {
118021               _deferred.delete(handle);
118022               if (_connection && context.editableDataEnabled()) {
118023                 const cid = _connection.getConnectionId();
118024                 _connection.loadTiles(projection, afterLoad(cid, callback));
118025               }
118026             });
118027             _deferred.add(handle);
118028           };
118029
118030           context.loadTileAtLoc = (loc, callback) => {
118031             const handle = window.requestIdleCallback(() => {
118032               _deferred.delete(handle);
118033               if (_connection && context.editableDataEnabled()) {
118034                 const cid = _connection.getConnectionId();
118035                 _connection.loadTileAtLoc(loc, afterLoad(cid, callback));
118036               }
118037             });
118038             _deferred.add(handle);
118039           };
118040
118041           context.loadEntity = (entityID, callback) => {
118042             if (_connection) {
118043               const cid = _connection.getConnectionId();
118044               _connection.loadEntity(entityID, afterLoad(cid, callback));
118045             }
118046           };
118047
118048           context.zoomToEntity = (entityID, zoomTo) => {
118049             if (zoomTo !== false) {
118050               context.loadEntity(entityID, (err, result) => {
118051                 if (err) return;
118052                 const entity = result.data.find(e => e.id === entityID);
118053                 if (entity) {
118054                   _map.zoomTo(entity);
118055                 }
118056               });
118057             }
118058
118059             _map.on('drawn.zoomToEntity', () => {
118060               if (!context.hasEntity(entityID)) return;
118061               _map.on('drawn.zoomToEntity', null);
118062               context.on('enter.zoomToEntity', null);
118063               context.enter(modeSelect(context, [entityID]));
118064             });
118065
118066             context.on('enter.zoomToEntity', () => {
118067               if (_mode.id !== 'browse') {
118068                 _map.on('drawn.zoomToEntity', null);
118069                 context.on('enter.zoomToEntity', null);
118070               }
118071             });
118072           };
118073
118074           let _minEditableZoom = 16;
118075           context.minEditableZoom = function(val) {
118076             if (!arguments.length) return _minEditableZoom;
118077             _minEditableZoom = val;
118078             if (_connection) {
118079               _connection.tileZoom(val);
118080             }
118081             return context;
118082           };
118083
118084           // String length limits in Unicode characters, not JavaScript UTF-16 code units
118085           context.maxCharsForTagKey = () => 255;
118086           context.maxCharsForTagValue = () => 255;
118087           context.maxCharsForRelationRole = () => 255;
118088
118089           function cleanOsmString(val, maxChars) {
118090             // be lenient with input
118091             if (val === undefined || val === null) {
118092               val = '';
118093             } else {
118094               val = val.toString();
118095             }
118096
118097             // remove whitespace
118098             val = val.trim();
118099
118100             // use the canonical form of the string
118101             if (val.normalize) val = val.normalize('NFC');
118102
118103             // trim to the number of allowed characters
118104             return utilUnicodeCharsTruncated(val, maxChars);
118105           }
118106           context.cleanTagKey = (val) => cleanOsmString(val, context.maxCharsForTagKey());
118107           context.cleanTagValue = (val) => cleanOsmString(val, context.maxCharsForTagValue());
118108           context.cleanRelationRole = (val) => cleanOsmString(val, context.maxCharsForRelationRole());
118109
118110
118111           /* History */
118112           let _inIntro = false;
118113           context.inIntro = function(val) {
118114             if (!arguments.length) return _inIntro;
118115             _inIntro = val;
118116             return context;
118117           };
118118
118119           // Immediately save the user's history to localstorage, if possible
118120           // This is called someteimes, but also on the `window.onbeforeunload` handler
118121           context.save = () => {
118122             // no history save, no message onbeforeunload
118123             if (_inIntro || context.container().select('.modal').size()) return;
118124
118125             let canSave;
118126             if (_mode && _mode.id === 'save') {
118127               canSave = false;
118128
118129               // Attempt to prevent user from creating duplicate changes - see #5200
118130               if (services.osm && services.osm.isChangesetInflight()) {
118131                 _history.clearSaved();
118132                 return;
118133               }
118134
118135             } else {
118136               canSave = context.selectedIDs().every(id => {
118137                 const entity = context.hasEntity(id);
118138                 return entity && !entity.isDegenerate();
118139               });
118140             }
118141
118142             if (canSave) {
118143               _history.save();
118144             }
118145             if (_history.hasChanges()) {
118146               return _t('save.unsaved_changes');
118147             }
118148           };
118149
118150           // Debounce save, since it's a synchronous localStorage write,
118151           // and history changes can happen frequently (e.g. when dragging).
118152           context.debouncedSave = debounce(context.save, 350);
118153
118154           function withDebouncedSave(fn) {
118155             return function() {
118156               const result = fn.apply(_history, arguments);
118157               context.debouncedSave();
118158               return result;
118159             };
118160           }
118161
118162
118163           /* Graph */
118164           context.hasEntity = (id) => _history.graph().hasEntity(id);
118165           context.entity = (id) => _history.graph().entity(id);
118166
118167
118168           /* Modes */
118169           let _mode;
118170           context.mode = () => _mode;
118171           context.enter = (newMode) => {
118172             if (_mode) {
118173               _mode.exit();
118174               dispatch$1.call('exit', this, _mode);
118175             }
118176
118177             _mode = newMode;
118178             _mode.enter();
118179             dispatch$1.call('enter', this, _mode);
118180           };
118181
118182           context.selectedIDs = () => (_mode && _mode.selectedIDs && _mode.selectedIDs()) || [];
118183           context.activeID = () => _mode && _mode.activeID && _mode.activeID();
118184
118185           let _selectedNoteID;
118186           context.selectedNoteID = function(noteID) {
118187             if (!arguments.length) return _selectedNoteID;
118188             _selectedNoteID = noteID;
118189             return context;
118190           };
118191
118192           // NOTE: Don't change the name of this until UI v3 is merged
118193           let _selectedErrorID;
118194           context.selectedErrorID = function(errorID) {
118195             if (!arguments.length) return _selectedErrorID;
118196             _selectedErrorID = errorID;
118197             return context;
118198           };
118199
118200
118201           /* Behaviors */
118202           context.install = (behavior) => context.surface().call(behavior);
118203           context.uninstall = (behavior) => context.surface().call(behavior.off);
118204
118205
118206           /* Copy/Paste */
118207           let _copyGraph;
118208           context.copyGraph = () => _copyGraph;
118209
118210           let _copyIDs = [];
118211           context.copyIDs = function(val) {
118212             if (!arguments.length) return _copyIDs;
118213             _copyIDs = val;
118214             _copyGraph = _history.graph();
118215             return context;
118216           };
118217
118218           let _copyLonLat;
118219           context.copyLonLat = function(val) {
118220             if (!arguments.length) return _copyLonLat;
118221             _copyLonLat = val;
118222             return context;
118223           };
118224
118225
118226           /* Background */
118227           let _background;
118228           context.background = () => _background;
118229
118230
118231           /* Features */
118232           let _features;
118233           context.features = () => _features;
118234           context.hasHiddenConnections = (id) => {
118235             const graph = _history.graph();
118236             const entity = graph.entity(id);
118237             return _features.hasHiddenConnections(entity, graph);
118238           };
118239
118240
118241           /* Photos */
118242           let _photos;
118243           context.photos = () => _photos;
118244
118245
118246           /* Map */
118247           let _map;
118248           context.map = () => _map;
118249           context.layers = () => _map.layers();
118250           context.surface = () => _map.surface;
118251           context.editableDataEnabled = () => _map.editableDataEnabled();
118252           context.surfaceRect = () => _map.surface.node().getBoundingClientRect();
118253           context.editable = () => {
118254             // don't allow editing during save
118255             const mode = context.mode();
118256             if (!mode || mode.id === 'save') return false;
118257             return _map.editableDataEnabled();
118258           };
118259
118260
118261           /* Debug */
118262           let _debugFlags = {
118263             tile: false,        // tile boundaries
118264             collision: false,   // label collision bounding boxes
118265             imagery: false,     // imagery bounding polygons
118266             target: false,      // touch targets
118267             downloaded: false   // downloaded data from osm
118268           };
118269           context.debugFlags = () => _debugFlags;
118270           context.getDebug = (flag) => flag && _debugFlags[flag];
118271           context.setDebug = function(flag, val) {
118272             if (arguments.length === 1) val = true;
118273             _debugFlags[flag] = val;
118274             dispatch$1.call('change');
118275             return context;
118276           };
118277
118278
118279           /* Container */
118280           let _container = select(null);
118281           context.container = function(val) {
118282             if (!arguments.length) return _container;
118283             _container = val;
118284             _container.classed('ideditor', true);
118285             return context;
118286           };
118287           context.containerNode = function(val) {
118288             if (!arguments.length) return context.container().node();
118289             context.container(select(val));
118290             return context;
118291           };
118292
118293           let _embed;
118294           context.embed = function(val) {
118295             if (!arguments.length) return _embed;
118296             _embed = val;
118297             return context;
118298           };
118299
118300
118301           /* Assets */
118302           let _assetPath = '';
118303           context.assetPath = function(val) {
118304             if (!arguments.length) return _assetPath;
118305             _assetPath = val;
118306             _mainFileFetcher.assetPath(val);
118307             return context;
118308           };
118309
118310           let _assetMap = {};
118311           context.assetMap = function(val) {
118312             if (!arguments.length) return _assetMap;
118313             _assetMap = val;
118314             _mainFileFetcher.assetMap(val);
118315             return context;
118316           };
118317
118318           context.asset = (val) => {
118319             if (/^http(s)?:\/\//i.test(val)) return val;
118320             const filename = _assetPath + val;
118321             return _assetMap[filename] || filename;
118322           };
118323
118324           context.imagePath = (val) => context.asset(`img/${val}`);
118325
118326
118327           /* reset (aka flush) */
118328           context.reset = context.flush = () => {
118329             context.debouncedSave.cancel();
118330
118331             Array.from(_deferred).forEach(handle => {
118332               window.cancelIdleCallback(handle);
118333               _deferred.delete(handle);
118334             });
118335
118336             Object.values(services).forEach(service => {
118337               if (service && typeof service.reset === 'function') {
118338                 service.reset(context);
118339               }
118340             });
118341
118342             context.changeset = null;
118343
118344             _validator.reset();
118345             _features.reset();
118346             _history.reset();
118347             _uploader.reset();
118348
118349             // don't leave stale state in the inspector
118350             context.container().select('.inspector-wrap *').remove();
118351
118352             return context;
118353           };
118354
118355
118356           /* Projections */
118357           context.projection = geoRawMercator();
118358           context.curtainProjection = geoRawMercator();
118359
118360
118361           /* Init */
118362           context.init = () => {
118363
118364             instantiateInternal();
118365
118366             initializeDependents();
118367
118368             return context;
118369
118370             // Load variables and properties. No property of `context` should be accessed
118371             // until this is complete since load statuses are indeterminate. The order
118372             // of instantiation shouldn't matter.
118373             function instantiateInternal() {
118374
118375               _history = coreHistory(context);
118376               context.graph = _history.graph;
118377               context.pauseChangeDispatch = _history.pauseChangeDispatch;
118378               context.resumeChangeDispatch = _history.resumeChangeDispatch;
118379               context.perform = withDebouncedSave(_history.perform);
118380               context.replace = withDebouncedSave(_history.replace);
118381               context.pop = withDebouncedSave(_history.pop);
118382               context.overwrite = withDebouncedSave(_history.overwrite);
118383               context.undo = withDebouncedSave(_history.undo);
118384               context.redo = withDebouncedSave(_history.redo);
118385
118386               _validator = coreValidator(context);
118387               _uploader = coreUploader(context);
118388
118389               _background = rendererBackground(context);
118390               _features = rendererFeatures(context);
118391               _map = rendererMap(context);
118392               _photos = rendererPhotos(context);
118393
118394               _ui = uiInit(context);
118395
118396               _connection = services.osm;
118397             }
118398
118399             // Set up objects that might need to access properties of `context`. The order
118400             // might matter if dependents make calls to each other. Be wary of async calls.
118401             function initializeDependents() {
118402
118403               if (context.initialHashParams.presets) {
118404                 _mainPresetIndex.addablePresetIDs(new Set(context.initialHashParams.presets.split(',')));
118405               }
118406
118407               // kick off some async work
118408               _mainLocalizer.ensureLoaded();
118409               _background.ensureLoaded();
118410               _mainPresetIndex.ensureLoaded();
118411
118412               Object.values(services).forEach(service => {
118413                 if (service && typeof service.init === 'function') {
118414                   service.init();
118415                 }
118416               });
118417
118418               _map.init();
118419               _validator.init();
118420               _features.init();
118421               _photos.init();
118422
118423               if (services.maprules && context.initialHashParams.maprules) {
118424                 d3_json(context.initialHashParams.maprules)
118425                   .then(mapcss => {
118426                     services.maprules.init();
118427                     mapcss.forEach(mapcssSelector => services.maprules.addRule(mapcssSelector));
118428                   })
118429                   .catch(() => { /* ignore */ });
118430               }
118431
118432               // if the container isn't available, e.g. when testing, don't load the UI
118433               if (!context.container().empty()) _ui.ensureLoaded();
118434             }
118435           };
118436
118437
118438           return context;
118439         }
118440
118441         // When `debug = true`, we use `Object.freeze` on immutables in iD.
118442         // This is only done in testing because of the performance penalty.
118443         let debug = false;
118444         let d3 = {
118445           customEvent: customEvent,
118446           dispatch:  dispatch,
118447           event:  event,
118448           geoMercator: mercator,
118449           geoProjection: projection,
118450           polygonArea: d3_polygonArea,
118451           polygonCentroid: d3_polygonCentroid,
118452           select: select,
118453           selectAll: selectAll,
118454           timerFlush: timerFlush
118455         };
118456
118457         var iD = /*#__PURE__*/Object.freeze({
118458                 __proto__: null,
118459                 debug: debug,
118460                 d3: d3,
118461                 actionAddEntity: actionAddEntity,
118462                 actionAddMember: actionAddMember,
118463                 actionAddMidpoint: actionAddMidpoint,
118464                 actionAddVertex: actionAddVertex,
118465                 actionChangeMember: actionChangeMember,
118466                 actionChangePreset: actionChangePreset,
118467                 actionChangeTags: actionChangeTags,
118468                 actionCircularize: actionCircularize,
118469                 actionConnect: actionConnect,
118470                 actionCopyEntities: actionCopyEntities,
118471                 actionDeleteMember: actionDeleteMember,
118472                 actionDeleteMultiple: actionDeleteMultiple,
118473                 actionDeleteNode: actionDeleteNode,
118474                 actionDeleteRelation: actionDeleteRelation,
118475                 actionDeleteWay: actionDeleteWay,
118476                 actionDiscardTags: actionDiscardTags,
118477                 actionDisconnect: actionDisconnect,
118478                 actionExtract: actionExtract,
118479                 actionJoin: actionJoin,
118480                 actionMerge: actionMerge,
118481                 actionMergeNodes: actionMergeNodes,
118482                 actionMergePolygon: actionMergePolygon,
118483                 actionMergeRemoteChanges: actionMergeRemoteChanges,
118484                 actionMove: actionMove,
118485                 actionMoveMember: actionMoveMember,
118486                 actionMoveNode: actionMoveNode,
118487                 actionNoop: actionNoop,
118488                 actionOrthogonalize: actionOrthogonalize,
118489                 actionRestrictTurn: actionRestrictTurn,
118490                 actionReverse: actionReverse,
118491                 actionRevert: actionRevert,
118492                 actionRotate: actionRotate,
118493                 actionSplit: actionSplit,
118494                 actionStraightenNodes: actionStraightenNodes,
118495                 actionStraightenWay: actionStraightenWay,
118496                 actionUnrestrictTurn: actionUnrestrictTurn,
118497                 actionReflect: actionReflect,
118498                 actionUpgradeTags: actionUpgradeTags,
118499                 behaviorAddWay: behaviorAddWay,
118500                 behaviorBreathe: behaviorBreathe,
118501                 behaviorDrag: behaviorDrag,
118502                 behaviorDrawWay: behaviorDrawWay,
118503                 behaviorDraw: behaviorDraw,
118504                 behaviorEdit: behaviorEdit,
118505                 behaviorHash: behaviorHash,
118506                 behaviorHover: behaviorHover,
118507                 behaviorLasso: behaviorLasso,
118508                 behaviorOperation: behaviorOperation,
118509                 behaviorPaste: behaviorPaste,
118510                 behaviorSelect: behaviorSelect,
118511                 coreContext: coreContext,
118512                 coreFileFetcher: coreFileFetcher,
118513                 fileFetcher: _mainFileFetcher,
118514                 coreDifference: coreDifference,
118515                 coreGraph: coreGraph,
118516                 coreHistory: coreHistory,
118517                 coreLocalizer: coreLocalizer,
118518                 t: _t,
118519                 localizer: _mainLocalizer,
118520                 prefs: corePreferences,
118521                 coreTree: coreTree,
118522                 coreUploader: coreUploader,
118523                 coreValidator: coreValidator,
118524                 geoExtent: geoExtent,
118525                 geoLatToMeters: geoLatToMeters,
118526                 geoLonToMeters: geoLonToMeters,
118527                 geoMetersToLat: geoMetersToLat,
118528                 geoMetersToLon: geoMetersToLon,
118529                 geoMetersToOffset: geoMetersToOffset,
118530                 geoOffsetToMeters: geoOffsetToMeters,
118531                 geoScaleToZoom: geoScaleToZoom,
118532                 geoSphericalClosestNode: geoSphericalClosestNode,
118533                 geoSphericalDistance: geoSphericalDistance,
118534                 geoZoomToScale: geoZoomToScale,
118535                 geoAngle: geoAngle,
118536                 geoChooseEdge: geoChooseEdge,
118537                 geoEdgeEqual: geoEdgeEqual,
118538                 geoGetSmallestSurroundingRectangle: geoGetSmallestSurroundingRectangle,
118539                 geoHasLineIntersections: geoHasLineIntersections,
118540                 geoHasSelfIntersections: geoHasSelfIntersections,
118541                 geoRotate: geoRotate,
118542                 geoLineIntersection: geoLineIntersection,
118543                 geoPathHasIntersections: geoPathHasIntersections,
118544                 geoPathIntersections: geoPathIntersections,
118545                 geoPathLength: geoPathLength,
118546                 geoPointInPolygon: geoPointInPolygon,
118547                 geoPolygonContainsPolygon: geoPolygonContainsPolygon,
118548                 geoPolygonIntersectsPolygon: geoPolygonIntersectsPolygon,
118549                 geoViewportEdge: geoViewportEdge,
118550                 geoRawMercator: geoRawMercator,
118551                 geoVecAdd: geoVecAdd,
118552                 geoVecAngle: geoVecAngle,
118553                 geoVecCross: geoVecCross,
118554                 geoVecDot: geoVecDot,
118555                 geoVecEqual: geoVecEqual,
118556                 geoVecFloor: geoVecFloor,
118557                 geoVecInterp: geoVecInterp,
118558                 geoVecLength: geoVecLength,
118559                 geoVecLengthSquare: geoVecLengthSquare,
118560                 geoVecNormalize: geoVecNormalize,
118561                 geoVecNormalizedDot: geoVecNormalizedDot,
118562                 geoVecProject: geoVecProject,
118563                 geoVecSubtract: geoVecSubtract,
118564                 geoVecScale: geoVecScale,
118565                 geoOrthoNormalizedDotProduct: geoOrthoNormalizedDotProduct,
118566                 geoOrthoCalcScore: geoOrthoCalcScore,
118567                 geoOrthoMaxOffsetAngle: geoOrthoMaxOffsetAngle,
118568                 geoOrthoCanOrthogonalize: geoOrthoCanOrthogonalize,
118569                 modeAddArea: modeAddArea,
118570                 modeAddLine: modeAddLine,
118571                 modeAddPoint: modeAddPoint,
118572                 modeAddNote: modeAddNote,
118573                 modeBrowse: modeBrowse,
118574                 modeDragNode: modeDragNode,
118575                 modeDragNote: modeDragNote,
118576                 modeDrawArea: modeDrawArea,
118577                 modeDrawLine: modeDrawLine,
118578                 modeMove: modeMove,
118579                 modeRotate: modeRotate,
118580                 modeSave: modeSave,
118581                 modeSelect: modeSelect,
118582                 modeSelectData: modeSelectData,
118583                 modeSelectError: modeSelectError,
118584                 modeSelectNote: modeSelectNote,
118585                 operationCircularize: operationCircularize,
118586                 operationContinue: operationContinue,
118587                 operationCopy: operationCopy,
118588                 operationDelete: operationDelete,
118589                 operationDisconnect: operationDisconnect,
118590                 operationDowngrade: operationDowngrade,
118591                 operationExtract: operationExtract,
118592                 operationMerge: operationMerge,
118593                 operationMove: operationMove,
118594                 operationOrthogonalize: operationOrthogonalize,
118595                 operationPaste: operationPaste,
118596                 operationReflectShort: operationReflectShort,
118597                 operationReflectLong: operationReflectLong,
118598                 operationReverse: operationReverse,
118599                 operationRotate: operationRotate,
118600                 operationSplit: operationSplit,
118601                 operationStraighten: operationStraighten,
118602                 osmChangeset: osmChangeset,
118603                 osmEntity: osmEntity,
118604                 osmNode: osmNode,
118605                 osmNote: osmNote,
118606                 osmRelation: osmRelation,
118607                 osmWay: osmWay,
118608                 QAItem: QAItem,
118609                 osmIntersection: osmIntersection,
118610                 osmTurn: osmTurn,
118611                 osmInferRestriction: osmInferRestriction,
118612                 osmLanes: osmLanes,
118613                 osmOldMultipolygonOuterMemberOfRelation: osmOldMultipolygonOuterMemberOfRelation,
118614                 osmIsOldMultipolygonOuterMember: osmIsOldMultipolygonOuterMember,
118615                 osmOldMultipolygonOuterMember: osmOldMultipolygonOuterMember,
118616                 osmJoinWays: osmJoinWays,
118617                 get osmAreaKeys () { return osmAreaKeys; },
118618                 osmSetAreaKeys: osmSetAreaKeys,
118619                 osmTagSuggestingArea: osmTagSuggestingArea,
118620                 get osmPointTags () { return osmPointTags; },
118621                 osmSetPointTags: osmSetPointTags,
118622                 get osmVertexTags () { return osmVertexTags; },
118623                 osmSetVertexTags: osmSetVertexTags,
118624                 osmNodeGeometriesForTags: osmNodeGeometriesForTags,
118625                 osmOneWayTags: osmOneWayTags,
118626                 osmPavedTags: osmPavedTags,
118627                 osmIsInterestingTag: osmIsInterestingTag,
118628                 osmRoutableHighwayTagValues: osmRoutableHighwayTagValues,
118629                 osmFlowingWaterwayTagValues: osmFlowingWaterwayTagValues,
118630                 osmRailwayTrackTagValues: osmRailwayTrackTagValues,
118631                 presetCategory: presetCategory,
118632                 presetCollection: presetCollection,
118633                 presetField: presetField,
118634                 presetPreset: presetPreset,
118635                 presetManager: _mainPresetIndex,
118636                 presetIndex: presetIndex,
118637                 rendererBackgroundSource: rendererBackgroundSource,
118638                 rendererBackground: rendererBackground,
118639                 rendererFeatures: rendererFeatures,
118640                 rendererMap: rendererMap,
118641                 rendererPhotos: rendererPhotos,
118642                 rendererTileLayer: rendererTileLayer,
118643                 services: services,
118644                 serviceKeepRight: serviceKeepRight,
118645                 serviceImproveOSM: serviceImproveOSM,
118646                 serviceOsmose: serviceOsmose,
118647                 serviceMapillary: serviceMapillary,
118648                 serviceMapRules: serviceMapRules,
118649                 serviceNominatim: serviceNominatim,
118650                 serviceOpenstreetcam: serviceOpenstreetcam,
118651                 serviceOsm: serviceOsm,
118652                 serviceOsmWikibase: serviceOsmWikibase,
118653                 serviceStreetside: serviceStreetside,
118654                 serviceTaginfo: serviceTaginfo,
118655                 serviceVectorTile: serviceVectorTile,
118656                 serviceWikidata: serviceWikidata,
118657                 serviceWikipedia: serviceWikipedia,
118658                 svgAreas: svgAreas,
118659                 svgData: svgData,
118660                 svgDebug: svgDebug,
118661                 svgDefs: svgDefs,
118662                 svgKeepRight: svgKeepRight,
118663                 svgIcon: svgIcon,
118664                 svgGeolocate: svgGeolocate,
118665                 svgLabels: svgLabels,
118666                 svgLayers: svgLayers,
118667                 svgLines: svgLines,
118668                 svgMapillaryImages: svgMapillaryImages,
118669                 svgMapillarySigns: svgMapillarySigns,
118670                 svgMidpoints: svgMidpoints,
118671                 svgNotes: svgNotes,
118672                 svgMarkerSegments: svgMarkerSegments,
118673                 svgOpenstreetcamImages: svgOpenstreetcamImages,
118674                 svgOsm: svgOsm,
118675                 svgPassiveVertex: svgPassiveVertex,
118676                 svgPath: svgPath,
118677                 svgPointTransform: svgPointTransform,
118678                 svgPoints: svgPoints,
118679                 svgRelationMemberTags: svgRelationMemberTags,
118680                 svgSegmentWay: svgSegmentWay,
118681                 svgStreetside: svgStreetside,
118682                 svgTagClasses: svgTagClasses,
118683                 svgTagPattern: svgTagPattern,
118684                 svgTouch: svgTouch,
118685                 svgTurns: svgTurns,
118686                 svgVertices: svgVertices,
118687                 uiFieldDefaultCheck: uiFieldCheck,
118688                 uiFieldOnewayCheck: uiFieldCheck,
118689                 uiFieldCheck: uiFieldCheck,
118690                 uiFieldMultiCombo: uiFieldCombo,
118691                 uiFieldNetworkCombo: uiFieldCombo,
118692                 uiFieldSemiCombo: uiFieldCombo,
118693                 uiFieldTypeCombo: uiFieldCombo,
118694                 uiFieldCombo: uiFieldCombo,
118695                 uiFieldUrl: uiFieldText,
118696                 uiFieldIdentifier: uiFieldText,
118697                 uiFieldNumber: uiFieldText,
118698                 uiFieldTel: uiFieldText,
118699                 uiFieldEmail: uiFieldText,
118700                 uiFieldText: uiFieldText,
118701                 uiFieldAccess: uiFieldAccess,
118702                 uiFieldAddress: uiFieldAddress,
118703                 uiFieldCycleway: uiFieldCycleway,
118704                 uiFieldLanes: uiFieldLanes,
118705                 uiFieldLocalized: uiFieldLocalized,
118706                 uiFieldMaxspeed: uiFieldMaxspeed,
118707                 uiFieldStructureRadio: uiFieldRadio,
118708                 uiFieldRadio: uiFieldRadio,
118709                 uiFieldRestrictions: uiFieldRestrictions,
118710                 uiFieldTextarea: uiFieldTextarea,
118711                 uiFieldWikidata: uiFieldWikidata,
118712                 uiFieldWikipedia: uiFieldWikipedia,
118713                 uiFields: uiFields,
118714                 uiIntro: uiIntro,
118715                 uiPanelBackground: uiPanelBackground,
118716                 uiPanelHistory: uiPanelHistory,
118717                 uiPanelLocation: uiPanelLocation,
118718                 uiPanelMeasurement: uiPanelMeasurement,
118719                 uiInfoPanels: uiInfoPanels,
118720                 uiPaneBackground: uiPaneBackground,
118721                 uiPaneHelp: uiPaneHelp,
118722                 uiPaneIssues: uiPaneIssues,
118723                 uiPaneMapData: uiPaneMapData,
118724                 uiPanePreferences: uiPanePreferences,
118725                 uiSectionBackgroundDisplayOptions: uiSectionBackgroundDisplayOptions,
118726                 uiSectionBackgroundList: uiSectionBackgroundList,
118727                 uiSectionBackgroundOffset: uiSectionBackgroundOffset,
118728                 uiSectionChanges: uiSectionChanges,
118729                 uiSectionDataLayers: uiSectionDataLayers,
118730                 uiSectionEntityIssues: uiSectionEntityIssues,
118731                 uiSectionFeatureType: uiSectionFeatureType,
118732                 uiSectionMapFeatures: uiSectionMapFeatures,
118733                 uiSectionMapStyleOptions: uiSectionMapStyleOptions,
118734                 uiSectionOverlayList: uiSectionOverlayList,
118735                 uiSectionPhotoOverlays: uiSectionPhotoOverlays,
118736                 uiSectionPresetFields: uiSectionPresetFields,
118737                 uiSectionPrivacy: uiSectionPrivacy,
118738                 uiSectionRawMemberEditor: uiSectionRawMemberEditor,
118739                 uiSectionRawMembershipEditor: uiSectionRawMembershipEditor,
118740                 uiSectionRawTagEditor: uiSectionRawTagEditor,
118741                 uiSectionSelectionList: uiSectionSelectionList,
118742                 uiSectionValidationIssues: uiSectionValidationIssues,
118743                 uiSectionValidationOptions: uiSectionValidationOptions,
118744                 uiSectionValidationRules: uiSectionValidationRules,
118745                 uiSectionValidationStatus: uiSectionValidationStatus,
118746                 uiSettingsCustomBackground: uiSettingsCustomBackground,
118747                 uiSettingsCustomData: uiSettingsCustomData,
118748                 uiInit: uiInit,
118749                 uiAccount: uiAccount,
118750                 uiAttribution: uiAttribution,
118751                 uiChangesetEditor: uiChangesetEditor,
118752                 uiCmd: uiCmd,
118753                 uiCombobox: uiCombobox,
118754                 uiCommit: uiCommit,
118755                 uiCommitWarnings: uiCommitWarnings,
118756                 uiConfirm: uiConfirm,
118757                 uiConflicts: uiConflicts,
118758                 uiContributors: uiContributors,
118759                 uiCurtain: uiCurtain,
118760                 uiDataEditor: uiDataEditor,
118761                 uiDataHeader: uiDataHeader,
118762                 uiDisclosure: uiDisclosure,
118763                 uiEditMenu: uiEditMenu,
118764                 uiEntityEditor: uiEntityEditor,
118765                 uiFeatureInfo: uiFeatureInfo,
118766                 uiFeatureList: uiFeatureList,
118767                 uiField: uiField,
118768                 uiFieldHelp: uiFieldHelp,
118769                 uiFlash: uiFlash,
118770                 uiFormFields: uiFormFields,
118771                 uiFullScreen: uiFullScreen,
118772                 uiGeolocate: uiGeolocate,
118773                 uiImproveOsmComments: uiImproveOsmComments,
118774                 uiImproveOsmDetails: uiImproveOsmDetails,
118775                 uiImproveOsmEditor: uiImproveOsmEditor,
118776                 uiImproveOsmHeader: uiImproveOsmHeader,
118777                 uiInfo: uiInfo,
118778                 uiInspector: uiInspector,
118779                 uiIssuesInfo: uiIssuesInfo,
118780                 uiKeepRightDetails: uiKeepRightDetails,
118781                 uiKeepRightEditor: uiKeepRightEditor,
118782                 uiKeepRightHeader: uiKeepRightHeader,
118783                 uiLasso: uiLasso,
118784                 uiLoading: uiLoading,
118785                 uiMapInMap: uiMapInMap,
118786                 uiModal: uiModal,
118787                 uiNotice: uiNotice,
118788                 uiNoteComments: uiNoteComments,
118789                 uiNoteEditor: uiNoteEditor,
118790                 uiNoteHeader: uiNoteHeader,
118791                 uiNoteReport: uiNoteReport,
118792                 uiPopover: uiPopover,
118793                 uiPresetIcon: uiPresetIcon,
118794                 uiPresetList: uiPresetList,
118795                 uiRestore: uiRestore,
118796                 uiScale: uiScale,
118797                 uiSidebar: uiSidebar,
118798                 uiSourceSwitch: uiSourceSwitch,
118799                 uiSpinner: uiSpinner,
118800                 uiSplash: uiSplash,
118801                 uiStatus: uiStatus,
118802                 uiSuccess: uiSuccess,
118803                 uiTagReference: uiTagReference,
118804                 uiToggle: uiToggle,
118805                 uiTooltip: uiTooltip,
118806                 uiVersion: uiVersion,
118807                 uiViewOnOSM: uiViewOnOSM,
118808                 uiViewOnKeepRight: uiViewOnKeepRight,
118809                 uiZoom: uiZoom,
118810                 utilAesEncrypt: utilAesEncrypt,
118811                 utilAesDecrypt: utilAesDecrypt,
118812                 utilArrayChunk: utilArrayChunk,
118813                 utilArrayDifference: utilArrayDifference,
118814                 utilArrayFlatten: utilArrayFlatten,
118815                 utilArrayGroupBy: utilArrayGroupBy,
118816                 utilArrayIdentical: utilArrayIdentical,
118817                 utilArrayIntersection: utilArrayIntersection,
118818                 utilArrayUnion: utilArrayUnion,
118819                 utilArrayUniq: utilArrayUniq,
118820                 utilArrayUniqBy: utilArrayUniqBy,
118821                 utilAsyncMap: utilAsyncMap,
118822                 utilCleanTags: utilCleanTags,
118823                 utilCombinedTags: utilCombinedTags,
118824                 utilDeepMemberSelector: utilDeepMemberSelector,
118825                 utilDetect: utilDetect,
118826                 utilDisplayName: utilDisplayName,
118827                 utilDisplayNameForPath: utilDisplayNameForPath,
118828                 utilDisplayType: utilDisplayType,
118829                 utilDisplayLabel: utilDisplayLabel,
118830                 utilEntityRoot: utilEntityRoot,
118831                 utilEditDistance: utilEditDistance,
118832                 utilEntitySelector: utilEntitySelector,
118833                 utilEntityOrMemberSelector: utilEntityOrMemberSelector,
118834                 utilEntityOrDeepMemberSelector: utilEntityOrDeepMemberSelector,
118835                 utilFastMouse: utilFastMouse,
118836                 utilFunctor: utilFunctor,
118837                 utilGetAllNodes: utilGetAllNodes,
118838                 utilGetSetValue: utilGetSetValue,
118839                 utilHashcode: utilHashcode,
118840                 utilHighlightEntities: utilHighlightEntities,
118841                 utilKeybinding: utilKeybinding,
118842                 utilNoAuto: utilNoAuto,
118843                 utilObjectOmit: utilObjectOmit,
118844                 utilPrefixCSSProperty: utilPrefixCSSProperty,
118845                 utilPrefixDOMProperty: utilPrefixDOMProperty,
118846                 utilQsString: utilQsString,
118847                 utilRebind: utilRebind,
118848                 utilSafeClassName: utilSafeClassName,
118849                 utilSetTransform: utilSetTransform,
118850                 utilSessionMutex: utilSessionMutex,
118851                 utilStringQs: utilStringQs,
118852                 utilTagDiff: utilTagDiff,
118853                 utilTagText: utilTagText,
118854                 utilTiler: utilTiler,
118855                 utilTotalExtent: utilTotalExtent,
118856                 utilTriggerEvent: utilTriggerEvent,
118857                 utilUnicodeCharsCount: utilUnicodeCharsCount,
118858                 utilUnicodeCharsTruncated: utilUnicodeCharsTruncated,
118859                 utilUniqueDomId: utilUniqueDomId,
118860                 utilWrap: utilWrap,
118861                 validationAlmostJunction: validationAlmostJunction,
118862                 validationCloseNodes: validationCloseNodes,
118863                 validationCrossingWays: validationCrossingWays,
118864                 validationDisconnectedWay: validationDisconnectedWay,
118865                 validationFormatting: validationFormatting,
118866                 validationHelpRequest: validationHelpRequest,
118867                 validationImpossibleOneway: validationImpossibleOneway,
118868                 validationIncompatibleSource: validationIncompatibleSource,
118869                 validationMaprules: validationMaprules,
118870                 validationMismatchedGeometry: validationMismatchedGeometry,
118871                 validationMissingRole: validationMissingRole,
118872                 validationMissingTag: validationMissingTag,
118873                 validationOutdatedTags: validationOutdatedTags,
118874                 validationPrivateData: validationPrivateData,
118875                 validationSuspiciousName: validationSuspiciousName,
118876                 validationUnsquareWay: validationUnsquareWay
118877         });
118878
118879         // polyfill requestIdleCallback
118880         window.requestIdleCallback = window.requestIdleCallback ||
118881             function(cb) {
118882                 var start = Date.now();
118883                 return window.requestAnimationFrame(function() {
118884                     cb({
118885                         didTimeout: false,
118886                         timeRemaining: function() {
118887                             return Math.max(0, 50 - (Date.now() - start));
118888                         }
118889                     });
118890                 });
118891             };
118892
118893         window.cancelIdleCallback = window.cancelIdleCallback ||
118894             function(id) {
118895                 window.cancelAnimationFrame(id);
118896             };
118897         window.iD = iD;
118898
118899 }());
118900 //# sourceMappingURL=iD.js.map